Skip to content

Using a third-party 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 third-party authentication provider (easy to integrate alongside your existing stack)

INFO

To make these instructions concrete, this guide uses Auth0 as a sample third-party 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

Go to the Privy Dashboard and select your app from the App Dropdown in the left sidebar and navigate to the Custom Auth Provider page.

INFO

Don't see the Custom Auth Provider page in the Privy dashboard?

Integrating embedded wallets with third-party auth providers is an Enterprise feature. Read more about our tiers and pricing, and reach out if you'd like to upgrade to enterprise!

In the Custom Auth Provider page, insert the following:

JWT Verification Details (required)

To verify your user's auth status, Privy requires a verification key to ensure the JWTs received by Privy are valid. You must provide one of the following:

  • JWKS Endpoint: If your provider uses JWKS to sign JWTs, provide a JWKS endpoint to allow Privy to get your auth provider's JWT public key.
  • Public Verification Key: If your provider uses a single key to sign JWTs, provide the corresponding public key certificate used for verification.

For Auth0, you can follow these instructions to get these details.

JWT ID Claim (required)

Enter the claim from your user's JWT that contains the user's unique ID. In most access tokens and identity tokens, this is the sub claim.

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/react-auth SDK:

sh
npm i @privy-io/react-auth@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.

The placement and configuration of the PrivyProvider depends on whether your app determines a user's auth state from the server (via SSR) or from the client. Follow the instructions below corresponding to your app's setup.

This example assumes your app:

(1) Write a callback to allow Privy to get the user's access or identity token

To start, we'll write the callback to return a Promise for the user's access or identity token from the server. Privy uses this callback to automatically create a wallet for your user when they login, and to ensure the user can continue to use the wallet as long as they are authenticated.

Create a new file (e.g. getToken.ts) for this callback and add the following code:

ts
// getSession can only run on the server
'use server';

import {getSession} from '@auth0/nextjs-auth0';

export const getToken = async () => {
  try {
    // getSession will return a valid session or undefined, depending on if the user is auth'd or not
    const token = (await getSession())?.idToken;
    return token;
  } catch (e) {
    // If getSession returns an error, you should assume the user is unauthenticated
    return undefined;
  }
};

In the snippet above, our getToken method gets the user's access/identity token from the server via Auth0's getSession method. We will use this method to pass the token to the client, so it can be available on the first client-side render.

(2) Wrap the PrivyProvider in a custom component

Next, we'll create a custom component** (e.g. EmbeddedWalletProvider.tsx) **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
// PrivyProvider is a React Context, and can only render on the client
// https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns#using-context-providers
'use client';
import {PrivyProvider} from '@privy-io/react-auth';
import {PropsWithChildren} from 'react';
// Import the callback function we just wrote
import {getToken} from './getToken';

type Props = PropsWithChildren<{}>;

export default const EmbeddedWalletProvider: React.FC<Props> = ({children}) => {
  return (
    <PrivyProvider
      appId='your-privy-app-id'
      config={{
        customAuth: {
          isLoading: false,
          getCustomAccessToken: getToken
        }
      }}
    >
      {children}
    </PrivyProvider>
  );
};

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, the object should have the following fields:

  • isLoading: Hardcode this to false.
    • This variable is only relevant in setups where auth status is determined from the client. In those cases, Privy's SDK must know if your user's auth state is actively being updated on the client or is up-to-date.
  • getCustomAccessToken: Pass in the callback (e.g. getToken) that we wrote above.
    • Privy uses this callback to obtain a valid access or identity token for your user.

(3) Enable support for Next13 server actions

Note how EmbeddedWalletProvider.tsx is marked for usage in the client, but imports and invokes a server function (getToken.ts).

To allow this, enable support for Next13 server actions in your app, by updating your next.config.js like below:

js
module.exports = {
  // Add support for server actions
  experimental: {
    serverActions: true,
  },
  ...insertTheRestOfYourNextConfig,
};

This is the easiest way to synchronize the availability of your user's embedded wallet with their auth state.

Don't want to use Next13 server actions?

If you do not want to use Next13 server actions, you can instead just create an API endpoint that gets the current user's session and returns their access/identity token. You can then have your client request this endpoint in your getCustomAccessToken callback.

Effectively, this is what the Next13 server action would do behind-the-scenes.

(4) Wrap your app's pages/components with your custom wrapper of the PrivyProvider

Lastly, for an arbitrary page/component in your app where you'd like to use Privy, wrap the content of that page/component with the custom component (e.g. EmbeddedWalletProvider.tsx) that we just wrote:

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

export default async function MyPage() {
  return <EmbeddedWalletProvider>{/* Insert the content of your page */}</EmbeddedWalletProvider>;
}

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

(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 {PrivyProvider} from '@privy-io/react-auth';
import {useAuth0} from '@auth0/auth0-react';

export default const EmbeddedWalletProvider: React.FC<PropsWithChildren> = ({children}) => {
  // Get auth details from Auth0
  const {getAccessTokenSilently, isLoading, isAuthenticated} = useAuth0();

  // Wrap getAccessTokenSilently as necessary (explained below)
  const getCustomToken = useCallback(
    () => getAccessTokenSilently(),
    [isAuthenticated, getAccessTokenSilently],
  );

  return (
    <PrivyProvider
      appId='insert-your-privy-app-id'
      config={{
        // CUSTOM AUTH CONFIGURATION
        customAuth: {
          // 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 PrivyProvider;

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 Auth0 SDK is actively updating your user's auth state in the client or not
    • Directly pass in the isLoading variable from Auth0's useAuth0 hook here.
  • a getCustomAccessToken callback to allow Privy to retrieve an access/identity token for the user
    • Pass in the lightweight wrapper (getCustomToken) around the getAccessTokenSilently method from Auth0's useAuth0 hook.
Why do we need to wrap Auth0's getAccessTokenSilently?

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(
  () => getAccessTokenSilently(),
  [isAuthenticated, getAccessTokenSilently],
);

(4) Wrap your app's pages/components with your custom wrapper of the PrivyProvider

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

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

tsx
import {Auth0Provider} from '@auth0/auth0-react';
import type {AppProps} from 'next/app';
import Head from 'next/head';

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

function MyApp({Component, pageProps}: AppProps) {
  return (
    <>
      <Head>{/* Insert your HTML header */}</Head>
      <Auth0Provider {...insertYourAuth0ProviderProps}>
        {/* Our custom wrapper component must be nested inside Auth0Provider */}
        <EmbeddedWalletProvider>
          <Component {...pageProps} />
        </EmbeddedWalletProvider>
      </Auth0Provider>
    </>
  );
}

export default MyApp;

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

4. Use the embedded wallet!

That's it! Whenever a user logs into your app, Privy will automatically create an embedded wallet for them, which they can use as long as they are authenticated.

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:

Don't want to create an embedded wallet automatically for all users?

By default, when using a custom auth provider, Privy will automatically create an embedded wallet for all users on their first login to your app.

If you don't want to automatically create a wallet for users on login, and instead would like to manually trigger wallet creation for each user, add the following to the config prop of your PrivyProvider:

tsx
embeddedWallets: {
  createOnLogin: 'off';
}

You can then manually trigger wallet creation for an authenticated user by calling Privy's createWallet method, returned by the usePrivy hook.