Appearance
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!
TIP
This guide assumes you already have an application set up with a custom/external authentication provider and are now looking to integrate Privy. While Privy seamlessly supports custom authentication, we strongly recommend using Privy as your primary authentication provider for a smoother experience. If you have any questions, feel free to reach out to us on Slack.
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 custom 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.
{
"keys": [
{
// JWKS
}
]
}
- 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.. For example if using Auth0, you would put sub
as the JWT user ID 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:
- supports server-side rendering by using the Next13 App Router (a popular framework for SSR)
- uses Auth0 via their server-side SDK (
@auth0/nextjs-auth0
)
(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 inside a custom component called (e.g. getToken.ts
). 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 {PropsWithChildren} from 'react';
import {PrivyProvider} from '@privy-io/react-auth';
// Import the callback function we just wrote
import {getToken} from './getToken';
type Props = PropsWithChildren<{}>;
const EmbeddedWalletProvider: React.FC<Props> = ({children}) => {
return (
<PrivyProvider
appId="your-privy-app-id"
config={{
customAuth: {
isLoading: false,
getCustomAccessToken: getToken,
},
}}
>
{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, the object should have the following fields:
isLoading
: Hardcode this tofalse
.- 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
!
INFO
When using a custom authentication provider, you should not use the Privy login
method (from useLogin
or usePrivy
). Instead, call the login method on of your custom provider and the Privy SDK will automatically synchronize it's state.
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:
Sign messages
Request personal and typed data signatures from users
Send transactions
Send transactions and call contracts on any EVM chain
Get an EIP1193 provider
Use the EIP1193 provider as a standard wallet interface
Web3 integrations
Integrate with viem, wagmi, ethers, and more
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.