Headless Tria SDK Usage Guide for React (BETA)

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

  1. Installation
  2. TriaProvider Setup
  3. Social Authentication
  4. Email OTP Authentication
  5. Phone OTP Authentication
  6. Web3 Authentication
  7. Username Creation
  8. 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

  1. 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)
  2. socialAuthError: string | null

    • Description: Contains any error message that occurred during the authentication process.
    • Usage: if (socialAuthError) { /* handle error */ }
  3. isLoading: boolean

    • Description: Indicates whether an authentication process is currently in progress.
    • Usage: if (isLoading) { /* show loading indicator */ }
  4. 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 */ }
  5. createUserName(name: string): Promise<void>

    • Description: Creates a username for the authenticated user.
    • Parameters: name - The desired username.
    • Usage: await createUserName('desired_username')
  6. 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')
  7. 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')
  8. nameSuggestions: string[]

    • Description: An array of suggested usernames.
    • Usage: nameSuggestions.map(suggestion => /* render suggestion */)
  9. 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>
  )
}

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

  1. 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 */ }
  2. email: string

    • Description: The email address entered by the user.
    • Usage: <input value={email} />
  3. emailOtpError: string | null

    • Description: Contains any error message that occurred during the authentication process.
    • Usage: if (emailOtpError) { /* handle error */ }
  4. isLoading: boolean

    • Description: Indicates whether an authentication process is currently in progress.
    • Usage: if (isLoading) { /* show loading indicator */ }
  5. 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]')
  6. subscribeToOtpInput(): Promise<void>

    • Description: Sets up a subscription to listen for OTP input events.
    • Usage: useEffect(() => { subscribeToOtpInput() }, [authStep])
  7. verifyOtp(): Promise<void>

    • Description: Verifies the entered OTP.
    • Usage: await verifyOtp()
  8. otpIframeUrl: string

    • Description: URL for the OTP input iframe.
    • Usage: <iframe src={otpIframeUrl} title="OTP Input" />
  9. allowResendOtp: boolean

    • Description: Indicates whether resending OTP is allowed (usually after a cooldown period).
    • Usage: <button disabled={!allowResendOtp}>Resend OTP</button>
  10. showOTPSuccess: boolean

    • Description: Indicates whether to show a success message after OTP is sent.
    • Usage: {showOTPSuccess && <p>OTP sent successfully!</p>}
  11. waitingVerification: boolean

    • Description: Indicates whether the system is waiting for OTP verification.
    • Usage: {waitingVerification && <p>Verifying OTP...</p>}
  12. createUserName(name: string): Promise<void>

    • Description: Creates a username for the authenticated user.
    • Parameters: name - The desired username.
    • Usage: await createUserName('desired_username')
  13. 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')
  14. 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')
  15. nameSuggestions: string[]

    • Description: An array of suggested usernames.
    • Usage: nameSuggestions.map(suggestion => /* render suggestion */)
  16. isNameAvailable: boolean

    • Description: Indicates whether the last checked username is available.
    • Usage: if (isNameAvailable) { /* allow username creation */ }
  17. 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>
  )
}

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

  1. 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 */ }
  2. phoneNumber: string

    • Description: The phone number entered by the user.
    • Usage: <input value={phoneNumber} />
  3. 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>
  4. phoneOtpError: string | null

    • Description: Contains any error message that occurred during the authentication process.
    • Usage: if (phoneOtpError) { /* handle error */ }
  5. isLoading: boolean

    • Description: Indicates whether an authentication process is currently in progress.
    • Usage: if (isLoading) { /* show loading indicator */ }
  6. initiatePhoneOtp(): Promise<void>

    • Description: Initiates the phone OTP process for the specified phone number and country.
    • Usage: await initiatePhoneOtp()
  7. subscribeToOtpInput(): Promise<void>

    • Description: Sets up a subscription to listen for OTP input events.
    • Usage: useEffect(() => { subscribeToOtpInput() }, [authStep])
  8. verifyOtp(): Promise<void>

    • Description: Verifies the entered OTP.
    • Usage: await verifyOtp()
  9. otpIframeUrl: string

    • Description: URL for the OTP input iframe.
    • Usage: <iframe src={otpIframeUrl} title="OTP Input" />
  10. setPhoneNumber(number: string): void

    • Description: Sets the phone number input.
    • Usage: <input onChange={(e) => setPhoneNumber(e.target.value)} />
  11. toggleShowCountryList(): void

    • Description: Toggles the visibility of the country selection list.
    • Usage: <button onClick={toggleShowCountryList}>Select Country</button>
  12. selectCountry(country: Country): void

    • Description: Selects a country from the list.
    • Usage: <li onClick={() => selectCountry(country)}>{country.name}</li>
  13. handleSearchInputChange(query: string): void

    • Description: Handles changes in the country search input.
    • Usage: <input onChange={(e) => handleSearchInputChange(e.target.value)} />
  14. showCountryList: boolean

    • Description: Indicates whether the country list is currently visible.
    • Usage: {showCountryList && <CountryList />}
  15. searchQuery: string

    • Description: The current search query for country filtering.
    • Usage: <input value={searchQuery} />
  16. countryList: Country[]

    • Description: The list of countries available for selection.
    • Usage: countryList.map(country => <CountryItem country={country} />)
  17. waitingVerification: boolean

    • Description: Indicates whether the system is waiting for OTP verification.
    • Usage: {waitingVerification && <p>Verifying OTP...</p>}
  18. createUserName(name: string): Promise<void>

    • Description: Creates a username for the authenticated user.
    • Parameters: name - The desired username.
    • Usage: await createUserName('desired_username')
  19. 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')
  20. 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')
  21. nameSuggestions: string[]

    • Description: An array of suggested usernames.
    • Usage: nameSuggestions.map(suggestion => /* render suggestion */)
  22. isNameAvailable: boolean

    • Description: Indicates whether the last checked username is available.
    • Usage: if (isNameAvailable) { /* allow username creation */ }
  23. 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>
  )
}

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

  1. 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)
  2. web3AuthError: string | null

    • Description: Contains any error message that occurred during the authentication process.
    • Usage: if (web3AuthError) { /* handle error */ }
  3. isLoading: boolean

    • Description: Indicates whether an authentication process is currently in progress.
    • Usage: if (isLoading) { /* show loading indicator */ }
  4. 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>
  5. logout(): Promise<void>

    • Description: Logs out the currently authenticated user.
    • Usage: await logout()
  6. 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 authentication
  • Web3LoginType.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>
  )
}

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.

Tria Name Constraints

When creating a username (Tria Name), the following constraints must be enforced:

  1. No capital letters allowed
  2. No special characters allowed (including in recommendations)
  3. 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

  1. Enforce Constraints: Always validate usernames against the Tria Name constraints both on the client-side and server-side.

  2. Lowercase Input: Convert all input to lowercase to prevent users from entering capital letters.

  3. Character Filtering: Remove or prevent the entry of special characters in the username input.

  4. Length Limit: Enforce the 24-character limit, preferably using the maxLength attribute on the input field.

  5. Clear Error Messages: Provide clear error messages when a username doesn't meet the constraints.

  6. Filter Suggestions: Ensure that all username suggestions comply with the Tria Name constraints.

  7. Minimum Length: Continue to enforce a minimum username length (typically 3 characters) before checking availability or fetching suggestions.

  8. Debouncing: Implement debouncing on the username input to avoid excessive API calls during typing.

  9. 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.

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.

Was this page helpful?