Skip to content

Using a custom auth provider

Privy's embedded wallets are fully-compatible with any authentication provider that supports JWT-based, stateless authentication. If you're looking to add embedded wallets to your app, you can either:

  • use Privy as your authentication provider (easy to set up out-of-the-box)
  • use a custom authentication provider (easy to integrate alongside your existing stack)

INFO

To make these instructions concrete, this guide uses Auth0 as a sample custom auth provider that you can integrate alongside Privy. In practice, you can integrate any auth provider that supports stateless, JWT-based authentication.

If you have questions on how to follow these instructions for your specific auth provider, please reach out!

1. Register your auth provider's information in the Privy dashboard

Register the details from your custom auth provider in the Privy Dashboard per the guide here!

Why does Privy need this information?

When a user logs into your app, your auth provider issues them an access and/or an identity token to represent their auth status. To provision your user's embedded wallet, Privy must validate this token to authenticate your user. Privy will verify both the token's signature and its expiration time (exp claim).

2. Install the Privy SDK

In your app, run the following command to install the latest version of the @privy-io/expo SDK:

sh
npm i @privy-io/expo@latest

3. Add the PrivyProvider to your app

The PrivyProvider is a React context that allow all components wrapped by the PrivyProvider to access the Privy SDK, via the usePrivy hook.

This example assumes your app:

  • determines a user's auth status on the client, and can get the user's access/identity token on the client
  • uses Auth0 via their client-side SDK (react-native-auth0)

(1) Wrap the PrivyProvider in a custom component

To start, we'll create a custom component to wrap the PrivyProvider and configure it. This allows you more easily abstract away the configuration of Privy alongside your auth provider.

Create a new component (e.g. EmbeddedWalletProvider.tsx) and add the following code:

tsx
import {useMemo, type PropsWithChildren, useCallback} from 'react';
import {useAuth0} from 'react-native-auth0';

import {PrivyProvider} from '@privy-io/expo';

const EmbeddedWalletProvider: React.FC<PropsWithChildren> = ({children}) => {
  // Get auth details from Auth0
  const {user: auth0User, isLoading, getCredentials} = useAuth0();

  // Wrap getCredentials as necessary (explained below)
  const getCustomToken = useCallback(async () => {
    const creds = await getCredentials();
    return creds?.idToken;
  }, [isLoading, auth0User, getCredentials]);

  return (
    <PrivyProvider
      appId="insert-your-privy-app-id"
      config={{
        // CUSTOM AUTH CONFIGURATION
        customAuth: {
          enabled: true,
          // The `isLoading` boolean from Auth0's `useAuth0` indicates if Auth0 is currently
          // updating the user's auth state on the client or not
          isLoading: isLoading,
          // The `getCustomToken` callback allows us to get the user's access/identity token
          // whenever their auth state changes
          getCustomAccessToken: getCustomToken,
        },
      }}
    >
      {children}
    </PrivyProvider>
  );
};

export default EmbeddedWalletProvider;

For the PrivyProvider's appId prop, pass in your Privy App ID from the Privy Dashboard.

For the PrivyProvider's config.customAuth, pass in the object as defined in the code snippet above. In particular, this object requires:

  • an isLoading boolean to determine whether the auth provider is actively updating your user's auth state in the client or not
    • For example, directly pass in the isLoading variable from Auth0's useAuth0 hook here.
  • a getCustomAccessToken callback that returns the user's access/identity token from your auth provider as a string. If the user is not authenticated with your auth provider, this callback should return undefined.
    • Pass in the lightweight wrapper (getCustomToken) around the getCredentials method from Auth0's useAuth0 hook.
Why do we need to wrap Auth0's getCredentials?

Privy must re-request an access/identity token for your user whenever their auth state changes. The wrapper in the code snippet above ensures that the callback passed to getCustomAccessToken gets recomputed whenever your user's auth state (isAuthenticated) updates:

tsx
// This is copy-pasted from the code snippet above for visibility
const getCustomToken = useCallback(async () => {
  const creds = await getCredentials();
  return creds?.idToken;
}, [isLoading, auth0User, getCredentials]);

(2) Wrap your app's screens/components with your custom wrapper of the PrivyProvider

Wrap your app's components/screens with the custom wrapper component (e.g EmbeddedWalletProvider) we wrote above.

Make sure to appropriately nest this custom component in your applications Root component. Since EmbeddedWalletProvider invokes Auth0's useAuth0 hook, it must be nested within Auth0's Auth0Provider React Context, like below:

tsx
import {Auth0Provider} from 'react-native-auth0';

// Import the custom wrapper component we just wrote
import EmbeddedWalletProvider from './EmbeddedWalletProvider';

function MyApp() {
  return (
    <>
      <Auth0Provider {...insertYourAuth0ProviderProps}>
        {/* Our custom wrapper component must be nested inside Auth0Provider */}
        <EmbeddedWalletProvider>{...MyAppComponent}</EmbeddedWalletProvider>
      </Auth0Provider>
    </>
  );
}

export default MyApp;

You can now use Privy (e.g. call the usePrivy and useEmbeddedWallet hooks from any content wrapped by the EmbeddedWalletProvider)!

4. Use the embedded wallet!

That's it! You can interface with the user's embedded wallet in any component wrapped by the PrivyProvider (or your custom wrapper component) to take on-chain actions: