Headless Tria SDK Usage Guide for React (BETA)
This feature is still in BETA and might undergo breaking changes. An effort to release the stable version is underway.
This comprehensive guide covers the headless implementation of Tria authentication flows in a React application, including social auth, email/phone OTP, Web3 authentication, and username creation.
Table of Contents
- Installation
- TriaProvider Setup
- Social Authentication
- Email OTP Authentication
- Phone OTP Authentication
- Web3 Authentication
- Username Creation
- Common Authentication States
1. Installation
Install the Tria SDK and its peer dependencies:
npm install @tria-sdk/authenticate-react @wagmi/[email protected] @wagmi/[email protected]
2. TriaProvider Setup
Wrap your app with TriaProvider
and import the CSS file:
import { TriaProvider } from '@tria-sdk/authenticate-react'
import '@tria-sdk/authenticate-react/dist/style.css'
function App() {
return (
<TriaProvider
initialConfig={{
analyticsKeys: {
clientId: 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX',
projectId: 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX',
},
chain: 'FUSE',
}}
initialUIConfig={{
modalMode: true,
darkMode: true,
dappName: 'Tria Demo',
}}
initialWalletUIConfig={{
darkMode: true,
}}
>
{/* Your app components */}
</TriaProvider>
)
}
3. Social Authentication
Social authentication allows users to sign in using their existing social media accounts. The Tria SDK provides the useSocialAuth
hook for implementing this functionality.
Using the useSocialAuth
Hook
import React from 'react'
import { useSocialAuth } from '@tria-sdk/authenticate-react'
import { SocialPlatform } from '@tria-sdk/authenticate-react/types'
function SocialAuthComponent() {
const {
signIn,
socialAuthError,
isLoading,
authStep,
createUserName,
checkNameAvailability,
getNameSuggestion,
nameSuggestions,
isNameAvailable,
} = useSocialAuth()
// Component logic here
}
Available Methods and Variables
-
signIn(platform: SocialPlatform): Promise<void>
- Description: Initiates the social authentication process for the specified platform.
- Parameters:
platform
- The social platform to use for authentication (e.g., SocialPlatform.GOOGLE). - Usage:
await signIn(SocialPlatform.GOOGLE)
-
socialAuthError: string | null
- Description: Contains any error message that occurred during the authentication process.
- Usage:
if (socialAuthError) { /* handle error */ }
-
isLoading: boolean
- Description: Indicates whether an authentication process is currently in progress.
- Usage:
if (isLoading) { /* show loading indicator */ }
-
authStep: AuthSteps
- Description: Represents the current step in the authentication flow.
- Possible values: 'INITIAL', 'OTP', 'CREATE_NAME', 'COMPLETED'
- Usage:
if (authStep === 'CREATE_NAME') { /* show username creation UI */ }
-
createUserName(name: string): Promise<void>
- Description: Creates a username for the authenticated user.
- Parameters:
name
- The desired username. - Usage:
await createUserName('desired_username')
-
checkNameAvailability(name: string): Promise<boolean>
- Description: Checks if a username is available.
- Parameters:
name
- The username to check. - Returns: A promise that resolves to a boolean indicating availability.
- Usage:
const isAvailable = await checkNameAvailability('desired_username')
-
getNameSuggestion(name: string): Promise<string[]>
- Description: Fetches username suggestions based on the provided name.
- Parameters:
name
- The base name for suggestions. - Returns: A promise that resolves to an array of suggested usernames.
- Usage:
const suggestions = await getNameSuggestion('desired_username')
-
nameSuggestions: string[]
- Description: An array of suggested usernames.
- Usage:
nameSuggestions.map(suggestion => /* render suggestion */)
-
isNameAvailable: boolean
- Description: Indicates whether the last checked username is available.
- Usage:
if (isNameAvailable) { /* allow username creation */ }
Implementation Example
Here's an expanded example of how to implement social authentication with username creation:
import React, { useState } from 'react'
import { useSocialAuth } from '@tria-sdk/authenticate-react'
import { SocialPlatform, AuthSteps } from '@tria-sdk/authenticate-react/types'
function SocialAuthComponent() {
const {
signIn,
socialAuthError,
isLoading,
authStep,
createUserName,
checkNameAvailability,
getNameSuggestion,
nameSuggestions,
isNameAvailable,
} = useSocialAuth()
const [username, setUsername] = useState('')
const handleSocialLogin = async (platform) => {
try {
await signIn(platform)
} catch (err) {
console.error('Social login failed:', err)
}
}
const handleUsernameChange = async (e) => {
const newUsername = e.target.value
setUsername(newUsername)
if (newUsername.length >= 3) {
await checkNameAvailability(newUsername)
await getNameSuggestion(newUsername)
}
}
const handleCreateUsername = async () => {
if (isNameAvailable) {
try {
await createUserName(username)
} catch (err) {
console.error('Username creation failed:', err)
}
}
}
if (socialAuthError) return <div>Error: {socialAuthError}</div>
if (isLoading) return <div>Loading...</div>
return (
<div>
{authStep === AuthSteps.INITIAL && (
<div>
<button onClick={() => handleSocialLogin(SocialPlatform.GOOGLE)}>
Sign in with Google
</button>
<button onClick={() => handleSocialLogin(SocialPlatform.TWITTER)}>
Sign in with Twitter
</button>
<button onClick={() => handleSocialLogin(SocialPlatform.APPLE)}>
Sign in with Apple
</button>
<button onClick={() => handleSocialLogin(SocialPlatform.DISCORD)}>
Sign in with Discord
</button>
</div>
)}
{authStep === AuthSteps.CREATE_NAME && (
<div>
<h2>Create your username</h2>
<input
type="text"
value={username}
onChange={handleUsernameChange}
placeholder="Enter desired username"
/>
{username.length >= 3 && (
<p>
{isNameAvailable
? 'Username is available'
: 'Username is not available'}
</p>
)}
<button
onClick={handleCreateUsername}
disabled={!isNameAvailable || username.length < 3}
>
Create Username
</button>
{nameSuggestions.length > 0 && (
<div>
<h3>Suggestions:</h3>
<ul>
{nameSuggestions.map((suggestion, index) => (
<li key={index} onClick={() => setUsername(suggestion)}>
{suggestion}
</li>
))}
</ul>
</div>
)}
</div>
)}
</div>
)
}
It's crucial to implement the username creation step (AuthSteps.CREATE_NAME) as part of the social authentication flow. This step is required to complete the user's profile and enable full functionality within the Tria ecosystem.
This implementation guides the user through the social authentication process and then prompts them to create a username. It handles error states, loading indicators, and provides username suggestions. Remember to adapt this component to fit your specific UI/UX requirements.
4. Email OTP Authentication
Email OTP (One-Time Password) authentication allows users to sign in using their email address. The Tria SDK provides the useEmailOtpAuth
hook for implementing this functionality.
Using the useEmailOtpAuth
Hook
import React from 'react'
import { useEmailOtpAuth } from '@tria-sdk/authenticate-react'
function EmailOtpAuthComponent() {
const {
authStep,
email,
emailOtpError,
isLoading,
initiateEmailOtp,
subscribeToOtpInput,
verifyOtp,
otpIframeUrl,
allowResendOtp,
showOTPSuccess,
waitingVerification,
createUserName,
checkNameAvailability,
getNameSuggestion,
nameSuggestions,
isNameAvailable,
resetState,
} = useEmailOtpAuth()
// Component logic here
}
Available Methods and Variables
-
authStep: AuthSteps
- Description: Represents the current step in the authentication flow.
- Possible values: 'INITIAL', 'OTP', 'CREATE_NAME', 'COMPLETED'
- Usage:
if (authStep === 'OTP') { /* show OTP input UI */ }
-
email: string
- Description: The email address entered by the user.
- Usage:
<input value={email} />
-
emailOtpError: string | null
- Description: Contains any error message that occurred during the authentication process.
- Usage:
if (emailOtpError) { /* handle error */ }
-
isLoading: boolean
- Description: Indicates whether an authentication process is currently in progress.
- Usage:
if (isLoading) { /* show loading indicator */ }
-
initiateEmailOtp(email: string): Promise<void>
- Description: Initiates the email OTP process for the specified email address.
- Parameters:
email
- The user's email address. - Usage:
await initiateEmailOtp('[email protected]')
-
subscribeToOtpInput(): Promise<void>
- Description: Sets up a subscription to listen for OTP input events.
- Usage:
useEffect(() => { subscribeToOtpInput() }, [authStep])
-
verifyOtp(): Promise<void>
- Description: Verifies the entered OTP.
- Usage:
await verifyOtp()
-
otpIframeUrl: string
- Description: URL for the OTP input iframe.
- Usage:
<iframe src={otpIframeUrl} title="OTP Input" />
-
allowResendOtp: boolean
- Description: Indicates whether resending OTP is allowed (usually after a cooldown period).
- Usage:
<button disabled={!allowResendOtp}>Resend OTP</button>
-
showOTPSuccess: boolean
- Description: Indicates whether to show a success message after OTP is sent.
- Usage:
{showOTPSuccess && <p>OTP sent successfully!</p>}
-
waitingVerification: boolean
- Description: Indicates whether the system is waiting for OTP verification.
- Usage:
{waitingVerification && <p>Verifying OTP...</p>}
-
createUserName(name: string): Promise<void>
- Description: Creates a username for the authenticated user.
- Parameters:
name
- The desired username. - Usage:
await createUserName('desired_username')
-
checkNameAvailability(name: string): Promise<boolean>
- Description: Checks if a username is available.
- Parameters:
name
- The username to check. - Returns: A promise that resolves to a boolean indicating availability.
- Usage:
const isAvailable = await checkNameAvailability('desired_username')
-
getNameSuggestion(name: string): Promise<string[]>
- Description: Fetches username suggestions based on the provided name.
- Parameters:
name
- The base name for suggestions. - Returns: A promise that resolves to an array of suggested usernames.
- Usage:
const suggestions = await getNameSuggestion('desired_username')
-
nameSuggestions: string[]
- Description: An array of suggested usernames.
- Usage:
nameSuggestions.map(suggestion => /* render suggestion */)
-
isNameAvailable: boolean
- Description: Indicates whether the last checked username is available.
- Usage:
if (isNameAvailable) { /* allow username creation */ }
-
resetState(): void
- Description: Resets the authentication state to its initial values.
- Usage:
<button onClick={resetState}>Start Over</button>
Implementation Example
Here's an expanded example of how to implement email OTP authentication with username creation:
import React, { useState, useEffect } from 'react'
import { useEmailOtpAuth } from '@tria-sdk/authenticate-react'
import { AuthSteps } from '@tria-sdk/authenticate-react/types'
function EmailOtpAuthComponent() {
const {
authStep,
email,
emailOtpError,
isLoading,
initiateEmailOtp,
subscribeToOtpInput,
verifyOtp,
otpIframeUrl,
allowResendOtp,
showOTPSuccess,
waitingVerification,
createUserName,
checkNameAvailability,
getNameSuggestion,
nameSuggestions,
isNameAvailable,
resetState,
} = useEmailOtpAuth()
const [emailInput, setEmailInput] = useState('')
const [username, setUsername] = useState('')
useEffect(() => {
if (authStep === AuthSteps.OTP) {
subscribeToOtpInput()
}
}, [authStep, subscribeToOtpInput])
const handleEmailSubmit = async (e) => {
e.preventDefault()
await initiateEmailOtp(emailInput)
}
const handleResendOtp = async () => {
await initiateEmailOtp(email)
}
const handleUsernameChange = async (e) => {
const newUsername = e.target.value
setUsername(newUsername)
if (newUsername.length >= 3) {
await checkNameAvailability(newUsername)
await getNameSuggestion(newUsername)
}
}
const handleCreateUsername = async () => {
if (isNameAvailable) {
try {
await createUserName(username)
} catch (err) {
console.error('Username creation failed:', err)
}
}
}
if (emailOtpError) return <div>Error: {emailOtpError}</div>
if (isLoading) return <div>Loading...</div>
return (
<div>
{authStep === AuthSteps.INITIAL && (
<form onSubmit={handleEmailSubmit}>
<input
type="email"
value={emailInput}
onChange={(e) => setEmailInput(e.target.value)}
placeholder="Enter your email"
required
/>
<button type="submit">Send OTP</button>
</form>
)}
{authStep === AuthSteps.OTP && (
<div>
<h2>Enter OTP</h2>
{showOTPSuccess && <p>OTP sent successfully!</p>}
<iframe src={otpIframeUrl} title="OTP Input" />
{waitingVerification ? (
<p>Verifying OTP...</p>
) : (
<button onClick={verifyOtp}>Verify OTP</button>
)}
{allowResendOtp && (
<button onClick={handleResendOtp}>Resend OTP</button>
)}
</div>
)}
{authStep === AuthSteps.CREATE_NAME && (
<div>
<h2>Create your username</h2>
<input
type="text"
value={username}
onChange={handleUsernameChange}
placeholder="Enter desired username"
/>
{username.length >= 3 && (
<p>
{isNameAvailable
? 'Username is available'
: 'Username is not available'}
</p>
)}
<button
onClick={handleCreateUsername}
disabled={!isNameAvailable || username.length < 3}
>
Create Username
</button>
{nameSuggestions.length > 0 && (
<div>
<h3>Suggestions:</h3>
<ul>
{nameSuggestions.map((suggestion, index) => (
<li key={index} onClick={() => setUsername(suggestion)}>
{suggestion}
</li>
))}
</ul>
</div>
)}
</div>
)}
<button onClick={resetState}>Start Over</button>
</div>
)
}
The Email OTP authentication flow includes three main steps: email input, OTP verification, and username creation. It's crucial to implement all these steps, including the username creation (AuthSteps.CREATE_NAME), to complete the user's profile and enable full functionality within the Tria ecosystem.
This implementation guides the user through the email OTP authentication process and then prompts them to create a username. It handles error states, loading indicators, OTP resend functionality, and provides username suggestions. Remember to adapt this component to fit your specific UI/UX requirements.
5. Phone OTP Authentication
Phone OTP (One-Time Password) authentication allows users to sign in using their phone number. The Tria SDK provides the usePhoneOtpAuth
hook for implementing this functionality, including country selection and OTP verification.
Using the usePhoneOtpAuth
Hook
import React from 'react'
import { usePhoneOtpAuth } from '@tria-sdk/authenticate-react'
function PhoneOtpAuthComponent() {
const {
authStep,
phoneNumber,
selectedCountry,
phoneOtpError,
isLoading,
initiatePhoneOtp,
subscribeToOtpInput,
verifyOtp,
otpIframeUrl,
setPhoneNumber,
toggleShowCountryList,
selectCountry,
handleSearchInputChange,
showCountryList,
searchQuery,
countryList,
waitingVerification,
createUserName,
checkNameAvailability,
getNameSuggestion,
nameSuggestions,
isNameAvailable,
resetState,
} = usePhoneOtpAuth()
// Component logic here
}
Available Methods and Variables
-
authStep: AuthSteps
- Description: Represents the current step in the authentication flow.
- Possible values: 'INITIAL', 'OTP', 'CREATE_NAME', 'COMPLETED'
- Usage:
if (authStep === 'OTP') { /* show OTP input UI */ }
-
phoneNumber: string
- Description: The phone number entered by the user.
- Usage:
<input value={phoneNumber} />
-
selectedCountry: Country
- Description: The currently selected country for phone number prefix.
- Type:
{ name: string, number: string, code: string }
- Usage:
<span>{selectedCountry.name} (+{selectedCountry.number})</span>
-
phoneOtpError: string | null
- Description: Contains any error message that occurred during the authentication process.
- Usage:
if (phoneOtpError) { /* handle error */ }
-
isLoading: boolean
- Description: Indicates whether an authentication process is currently in progress.
- Usage:
if (isLoading) { /* show loading indicator */ }
-
initiatePhoneOtp(): Promise<void>
- Description: Initiates the phone OTP process for the specified phone number and country.
- Usage:
await initiatePhoneOtp()
-
subscribeToOtpInput(): Promise<void>
- Description: Sets up a subscription to listen for OTP input events.
- Usage:
useEffect(() => { subscribeToOtpInput() }, [authStep])
-
verifyOtp(): Promise<void>
- Description: Verifies the entered OTP.
- Usage:
await verifyOtp()
-
otpIframeUrl: string
- Description: URL for the OTP input iframe.
- Usage:
<iframe src={otpIframeUrl} title="OTP Input" />
-
setPhoneNumber(number: string): void
- Description: Sets the phone number input.
- Usage:
<input onChange={(e) => setPhoneNumber(e.target.value)} />
-
toggleShowCountryList(): void
- Description: Toggles the visibility of the country selection list.
- Usage:
<button onClick={toggleShowCountryList}>Select Country</button>
-
selectCountry(country: Country): void
- Description: Selects a country from the list.
- Usage:
<li onClick={() => selectCountry(country)}>{country.name}</li>
-
handleSearchInputChange(query: string): void
- Description: Handles changes in the country search input.
- Usage:
<input onChange={(e) => handleSearchInputChange(e.target.value)} />
-
showCountryList: boolean
- Description: Indicates whether the country list is currently visible.
- Usage:
{showCountryList && <CountryList />}
-
searchQuery: string
- Description: The current search query for country filtering.
- Usage:
<input value={searchQuery} />
-
countryList: Country[]
- Description: The list of countries available for selection.
- Usage:
countryList.map(country => <CountryItem country={country} />)
-
waitingVerification: boolean
- Description: Indicates whether the system is waiting for OTP verification.
- Usage:
{waitingVerification && <p>Verifying OTP...</p>}
-
createUserName(name: string): Promise<void>
- Description: Creates a username for the authenticated user.
- Parameters:
name
- The desired username. - Usage:
await createUserName('desired_username')
-
checkNameAvailability(name: string): Promise<boolean>
- Description: Checks if a username is available.
- Parameters:
name
- The username to check. - Returns: A promise that resolves to a boolean indicating availability.
- Usage:
const isAvailable = await checkNameAvailability('desired_username')
-
getNameSuggestion(name: string): Promise<string[]>
- Description: Fetches username suggestions based on the provided name.
- Parameters:
name
- The base name for suggestions. - Returns: A promise that resolves to an array of suggested usernames.
- Usage:
const suggestions = await getNameSuggestion('desired_username')
-
nameSuggestions: string[]
- Description: An array of suggested usernames.
- Usage:
nameSuggestions.map(suggestion => /* render suggestion */)
-
isNameAvailable: boolean
- Description: Indicates whether the last checked username is available.
- Usage:
if (isNameAvailable) { /* allow username creation */ }
-
resetState(): void
- Description: Resets the authentication state to its initial values.
- Usage:
<button onClick={resetState}>Start Over</button>
Implementation Example
Here's an expanded example of how to implement phone OTP authentication with country selection and username creation:
import React, { useState, useEffect } from 'react'
import { usePhoneOtpAuth } from '@tria-sdk/authenticate-react'
import { AuthSteps } from '@tria-sdk/authenticate-react/types'
function PhoneOtpAuthComponent() {
const {
authStep,
phoneNumber,
selectedCountry,
phoneOtpError,
isLoading,
initiatePhoneOtp,
subscribeToOtpInput,
verifyOtp,
otpIframeUrl,
setPhoneNumber,
toggleShowCountryList,
selectCountry,
handleSearchInputChange,
showCountryList,
searchQuery,
countryList,
waitingVerification,
createUserName,
checkNameAvailability,
getNameSuggestion,
nameSuggestions,
isNameAvailable,
resetState,
} = usePhoneOtpAuth()
const [username, setUsername] = useState('')
useEffect(() => {
if (authStep === AuthSteps.OTP) {
subscribeToOtpInput()
}
}, [authStep, subscribeToOtpInput])
const handlePhoneSubmit = async (e) => {
e.preventDefault()
await initiatePhoneOtp()
}
const handleUsernameChange = async (e) => {
const newUsername = e.target.value
setUsername(newUsername)
if (newUsername.length >= 3) {
await checkNameAvailability(newUsername)
await getNameSuggestion(newUsername)
}
}
const handleCreateUsername = async () => {
if (isNameAvailable) {
try {
await createUserName(username)
} catch (err) {
console.error('Username creation failed:', err)
}
}
}
if (phoneOtpError) return <div>Error: {phoneOtpError}</div>
if (isLoading) return <div>Loading...</div>
return (
<div>
{authStep === AuthSteps.INITIAL && (
<form onSubmit={handlePhoneSubmit}>
<div>
<button type="button" onClick={toggleShowCountryList}>
{selectedCountry.emoji} {selectedCountry.number}
</button>
<input
type="tel"
value={phoneNumber}
onChange={(e) => setPhoneNumber(e.target.value)}
placeholder="Enter your phone number"
required
/>
</div>
{showCountryList && (
<div>
<input
type="text"
value={searchQuery}
onChange={(e) => handleSearchInputChange(e.target.value)}
placeholder="Search countries"
/>
<ul>
{countryList.map((country) => (
<li key={country.code} onClick={() => selectCountry(country)}>
{country.emoji} {country.name} ({country.number})
</li>
))}
</ul>
</div>
)}
<button type="submit">Send OTP</button>
</form>
)}
{authStep === AuthSteps.OTP && (
<div>
<h2>Enter OTP</h2>
<iframe src={otpIframeUrl} title="OTP Input" />
{waitingVerification ? (
<p>Verifying OTP...</p>
) : (
<button onClick={verifyOtp}>Verify OTP</button>
)}
</div>
)}
{authStep === AuthSteps.CREATE_NAME && (
<div>
<h2>Create your username</h2>
<input
type="text"
value={username}
onChange={handleUsernameChange}
placeholder="Enter desired username"
/>
{username.length >= 3 && (
<p>
{isNameAvailable
? 'Username is available'
: 'Username is not available'}
</p>
)}
<button
onClick={handleCreateUsername}
disabled={!isNameAvailable || username.length < 3}
>
Create Username
</button>
{nameSuggestions.length > 0 && (
<div>
<h3>Suggestions:</h3>
<ul>
{nameSuggestions.map((suggestion, index) => (
<li key={index} onClick={() => setUsername(suggestion)}>
{suggestion}
</li>
))}
</ul>
</div>
)}
</div>
)}
<button onClick={resetState}>Start Over</button>
</div>
)
}
The Phone OTP authentication flow includes three main steps: phone number input with country selection, OTP verification, and username creation. It's crucial to implement all these steps, including the username creation (AuthSteps.CREATE_NAME), to complete the user's profile and enable full functionality within the Tria ecosystem.
This implementation guides the user through the phone OTP authentication process, including country selection, and then prompts them to create a username. It handles error states, loading indicators, country search functionality, and provides username suggestions. Remember to adapt this component to fit your specific UI/UX requirements.
6. Web3 Authentication
Web3 authentication allows users to sign in using their blockchain wallets, such as MetaMask or WalletConnect. The Tria SDK provides the useWeb3Auth
hook for implementing this functionality.
Using the useWeb3Auth
Hook
import React from 'react'
import { useWeb3Auth } from '@tria-sdk/authenticate-react'
import { Web3LoginType } from '@tria-sdk/authenticate-react/types'
function Web3AuthComponent() {
const {
loginWithWeb3,
web3AuthError,
isLoading,
userState,
logout,
wagmiConfig,
} = useWeb3Auth()
// Component logic here
}
Available Methods and Variables
-
loginWithWeb3(loginType: Web3LoginType): Promise<void>
- Description: Initiates the Web3 authentication process for the specified login type.
- Parameters:
loginType
- The Web3 login method to use (e.g., Web3LoginType.METAMASK). - Usage:
await loginWithWeb3(Web3LoginType.METAMASK)
-
web3AuthError: string | null
- Description: Contains any error message that occurred during the authentication process.
- Usage:
if (web3AuthError) { /* handle error */ }
-
isLoading: boolean
- Description: Indicates whether an authentication process is currently in progress.
- Usage:
if (isLoading) { /* show loading indicator */ }
-
userState: UserState | null
- Description: Contains information about the authenticated user.
- Type:
{ account: { triaName: string | null, evm: { address: string } } }
- Usage:
<p>Address: {userState?.account.evm.address}</p>
-
logout(): Promise<void>
- Description: Logs out the currently authenticated user.
- Usage:
await logout()
-
wagmiConfig: WagmiConfig | null
- Description: The Wagmi configuration object, if available.
- Usage: Typically used internally by the hook, but can be accessed if needed for advanced use cases.
Web3 Login Types
The Web3LoginType
enum provides the following options:
Web3LoginType.METAMASK
: For MetaMask wallet authenticationWeb3LoginType.WALLET_CONNECT
: For WalletConnect authentication
Implementation Example
Here's an expanded example of how to implement Web3 authentication:
import React, { useState } from 'react'
import { useWeb3Auth } from '@tria-sdk/authenticate-react'
import { Web3LoginType } from '@tria-sdk/authenticate-react/types'
function Web3AuthComponent() {
const {
loginWithWeb3,
web3AuthError,
isLoading,
userState,
logout,
} = useWeb3Auth()
const [selectedWallet, setSelectedWallet] = useState(null)
const handleLogin = async (loginType) => {
setSelectedWallet(loginType)
try {
await loginWithWeb3(loginType)
} catch (err) {
console.error('Web3 login failed:', err)
}
}
const handleLogout = async () => {
try {
await logout()
setSelectedWallet(null)
} catch (err) {
console.error('Logout failed:', err)
}
}
if (web3AuthError) return <div>Error: {web3AuthError}</div>
if (isLoading) return <div>Loading...</div>
return (
<div>
{!userState.authenticationStatus ===
AuthenticationStatus.AUTHENTICATED && ? (
<div>
<h2>Choose a Web3 Login Method</h2>
<button onClick={() => handleLogin(Web3LoginType.METAMASK)}>
Login with MetaMask
</button>
<button onClick={() => handleLogin(Web3LoginType.WALLET_CONNECT)}>
Login with WalletConnect
</button>
</div>
) : (
<div>
<h2>Authenticated with Web3</h2>
<p>Wallet Type: {selectedWallet}</p>
<p>Address: {userState?.account.evm.address}</p>
{userState?.account.triaName && (
<p>Tria Name: {userState.account.triaName}</p>
)}
<button onClick={handleLogout}>Logout</button>
</div>
)}
</div>
)
}
Web3 authentication typically doesn't require a separate username creation step, as the wallet address serves as the user's identifier. However, you may still want to offer users the option to set a Tria Name for a more user-friendly experience within your application.
7. Username Creation
Username creation is a crucial step in the Tria authentication process, allowing users to set a unique identifier within the Tria ecosystem. The Tria SDK provides the useNameCreation
hook for implementing this functionality.
The useNameCreation
hook is available both as part of the other
authentication hooks (useEmailOtpAuth, usePhoneOtpAuth, useSocialAuth) and as
a standalone hook. This allows for flexible implementation based on your
authentication flow.
Tria Name Constraints
When creating a username (Tria Name), the following constraints must be enforced:
- No capital letters allowed
- No special characters allowed (including in recommendations)
- Maximum length of 24 characters
These constraints should be applied both in client-side validation and when making requests to the Tria API.
Using the useNameCreation
Hook
import React from 'react'
import { useNameCreation } from '@tria-sdk/authenticate-react'
// Or, if using within another auth flow:
// import { useEmailOtpAuth } from '@tria-sdk/authenticate-react'
// const { ...nameCreationHook } = useEmailOtpAuth()
function UsernameCreationComponent() {
const {
checkNameAvailability,
getNameSuggestion,
createUserName,
nameSuggestions,
isNameAvailable,
nameCreationError,
isNameProcessing,
isNameCreationProcessing,
setNameCreationError,
setNameSuggestions,
} = useNameCreation()
// Component logic here
}
Available Methods and Variables
[... Keep the existing methods and variables descriptions ...]
Implementation Example
Here's an expanded example of how to implement username creation, incorporating the Tria Name constraints:
import React, { useState, useEffect } from 'react'
import { useNameCreation } from '@tria-sdk/authenticate-react'
function UsernameCreationComponent() {
const {
checkNameAvailability,
getNameSuggestion,
createUserName,
nameSuggestions,
isNameAvailable,
nameCreationError,
isNameProcessing,
isNameCreationProcessing,
setNameCreationError,
} = useNameCreation()
const [username, setUsername] = useState('')
const [isCreating, setIsCreating] = useState(false)
useEffect(() => {
if (nameCreationError) {
console.error('Name creation error:', nameCreationError)
}
}, [nameCreationError])
const validateUsername = (name) => {
if (name.length > 24) {
return 'Username must be 24 characters or less'
}
if (/[A-Z]/.test(name)) {
return 'Username cannot contain capital letters'
}
if (/[^a-z0-9]/.test(name)) {
return 'Username cannot contain special characters'
}
return null
}
const handleUsernameChange = async (e) => {
const newUsername = e.target.value.toLowerCase() // Convert to lowercase
setUsername(newUsername)
const validationError = validateUsername(newUsername)
if (validationError) {
setNameCreationError(validationError)
return
}
if (newUsername.length >= 3) {
try {
await checkNameAvailability(newUsername)
const suggestions = await getNameSuggestion(newUsername)
// Filter suggestions to comply with constraints
const validSuggestions = suggestions.filter(
(suggestion) => !validateUsername(suggestion),
)
setNameSuggestions(validSuggestions)
} catch (err) {
console.error('Error checking name:', err)
setNameCreationError(err.message)
}
}
}
const handleCreateUsername = async () => {
const validationError = validateUsername(username)
if (validationError) {
setNameCreationError(validationError)
return
}
if (isNameAvailable && username.length >= 3) {
setIsCreating(true)
try {
await createUserName(username)
console.log('Username created successfully')
} catch (err) {
console.error('Username creation failed:', err)
setNameCreationError(err.message)
} finally {
setIsCreating(false)
}
}
}
return (
<div>
<h2>Create your Tria username</h2>
<p>
Usernames must be lowercase, contain only letters and numbers, and be 24
characters or less.
</p>
<input
type="text"
value={username}
onChange={handleUsernameChange}
placeholder="Enter desired username"
minLength={3}
maxLength={24}
required
/>
{isNameProcessing ? (
<p>Checking availability...</p>
) : (
username.length >= 3 && (
<p>
{isNameAvailable
? 'Username is available'
: 'Username is not available'}
</p>
)
)}
<button
onClick={handleCreateUsername}
disabled={
!isNameAvailable ||
username.length < 3 ||
isNameCreationProcessing ||
isCreating ||
validateUsername(username)
}
>
{isCreating ? 'Creating...' : 'Create Username'}
</button>
{nameSuggestions.length > 0 && (
<div>
<h3>Suggestions:</h3>
<ul>
{nameSuggestions.map((suggestion, index) => (
<li key={index} onClick={() => setUsername(suggestion)}>
{suggestion}
</li>
))}
</ul>
</div>
)}
{error && <p style={{ color: 'red' }}>Error: {error}</p>}
</div>
)
}
Best Practices
-
Enforce Constraints: Always validate usernames against the Tria Name constraints both on the client-side and server-side.
-
Lowercase Input: Convert all input to lowercase to prevent users from entering capital letters.
-
Character Filtering: Remove or prevent the entry of special characters in the username input.
-
Length Limit: Enforce the 24-character limit, preferably using the
maxLength
attribute on the input field. -
Clear Error Messages: Provide clear error messages when a username doesn't meet the constraints.
-
Filter Suggestions: Ensure that all username suggestions comply with the Tria Name constraints.
-
Minimum Length: Continue to enforce a minimum username length (typically 3 characters) before checking availability or fetching suggestions.
-
Debouncing: Implement debouncing on the username input to avoid excessive API calls during typing.
-
Loading States: Show appropriate loading indicators during name processing and creation.
[... Keep the existing "Integration with Other Auth Flows" and closing note ...]
Integration with Other Auth Flows
When using the useNameCreation
hook as part of another authentication flow (e.g., useEmailOtpAuth
), you can access its methods and states directly from the parent hook:
const {
// Other auth states and methods
checkNameAvailability,
getNameSuggestion,
createUserName,
nameSuggestions,
isNameAvailable,
// ... other name creation related states and methods
} = useEmailOtpAuth()
// Use these methods and states in your component as shown in the example above
This allows for seamless integration of the username creation process into your overall authentication flow.
The username creation step is crucial for completing the user's profile in the Tria ecosystem. Ensure that this step is implemented in all authentication flows, including Web3 authentication where it might be optional but beneficial for user experience.
Remember to adapt this component to fit your specific UI/UX requirements and to handle any additional validation or user experience enhancements you may want to include in your username creation process.
8. Common Authentication States
Use the useTriaAuth
hook to manage common authentication states:
import React from 'react'
import { useTriaAuth } from '@tria-sdk/authenticate-react'
function AuthStateComponent() {
const { userState, logout } = useTriaAuth()
if (!userState.authenticationStatus ===
AuthenticationStatus.AUTHENTICATED &&) {
return <div>User is not authenticated</div>
}
return (
<div>
<p>Logged in as: {userState?.account?.evm.address}</p>
<p>Tria Name: {userState?.account?.triaName}</p>
<button onClick={logout}>Logout</button>
</div>
)
}
This guide provides a comprehensive overview of implementing headless authentication flows using the Tria SDK. Developers can use these components as a starting point and customize them to fit their specific UI and UX requirements. Remember to handle errors, loading states, and different authentication steps as demonstrated in the examples.