When a user logs in to your app and becomes authenticated, Privy issues the user an app access token. This token is signed by Privy and cannot be spoofed.

When your frontend makes a request to your backend, you should include the current user’s access token in the request. This allows your server to determine whether the requesting user is truly authenticated or not.


Access token format

Privy access tokens are JSON Web Tokens (JWT), signed with the ES256 algorithm. These JWTs include certain information about the user in their claims, namely:

Read more about Privy’s tokens and their security in our security guide.


Sending the access token

To include the current user’s access token in requests from your frontend to your backend, you’ll first need to retrieve it, then send it appropriately.

You can get the current user’s Privy token as a string using the getAccessToken method from the usePrivy hook. This method will also automatically refresh the user’s access token if it is nearing expiration or has expired.

const { getAccessToken } = usePrivy();
const accessToken = await getAccessToken();

If you need to get a user’s Privy token outside of Privy’s React context, you can directly import the getAccessToken method:

import { getAccessToken } from '@privy-io/react-auth';

const authToken = await getAccessToken();

When using direct imports, you must ensure PrivyProvider has rendered before invoking the method. Whenever possible, you should retrieve getAccessToken from the usePrivy hook.

If your app is configured to use HTTP-only cookies (instead of the default local storage), the access token will automatically be included in the cookies for requests to the same domain. In this case, you don’t need to manually include the token in the request headers.


When sending requests to your backend, here’s how you can include the access token with different HTTP client libraries:

// For bearer token approach (when using local storage)
const accessToken = await getAccessToken();
const response = await fetch('<your-api-endpoint>', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${accessToken}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(data)
});

// For HTTP-only cookies approach
const response = await fetch('<your-api-endpoint>', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  credentials: 'include', // This includes cookies automatically
  body: JSON.stringify(data)
});

Getting the access token

When your server receives a request, the location of the user’s access token depends on whether your app uses local storage (the default) or cookies to manage user sessions.

If you’re using local storage for session management, the access token will be passed in the Authorization header of the request with the Bearer prefix. You can extract it like this:

// Example for Express.js
const accessToken = req.headers.authorization?.replace('Bearer ', '');

// Example for Next.js API route
const accessToken = req.headers.authorization?.replace('Bearer ', '');

// Example for Next.js App Router
const accessToken = headers().get('authorization')?.replace('Bearer ', '');

Verifying the access token

Once you’ve obtained the user’s access token from a request, you should verify the token against Privy’s verification key for your app to confirm that the token was issued by Privy and the user referenced by the DID in the token is truly authenticated.

The access token is a standard ES256 JWT and the verification key is a standard Ed25519 public key. You can verify the access token against the public key using the @privy-io/server-auth library or using a third-party library for managing tokens.

Using Privy SDK

Pass the user’s access token as a string to the PrivyClient’s verifyAuthToken method:

// `privy` refers to an instance of the `PrivyClient`
try {
  const verifiedClaims = await privy.verifyAuthToken(authToken);
} catch (error) {
  console.log(`Token verification failed with error ${error}.`);
}

If the token is valid, verifyAuthToken will return an AuthTokenClaims object with additional information about the request, with the fields below:

ParameterTypeDescription
appIdstringYour Privy app ID.
userIdstringThe authenticated user’s Privy DID. Use this to identify the requesting user.
issuerstringThis will always be 'privy.io'.
issuedAtstringTimestamp for when the access token was signed by Privy.
expirationstringTimestamp for when the access token will expire.
sessionIdstringUnique identifier for the user’s session.

If the token is invalid, verifyAuthToken will throw an error and you should not consider the requesting user authorized. This generally occurs if the token has expired or is invalid (e.g. corresponds to a different app ID).

The Privy Client’s verifyAuthToken method will make a request to Privy’s API to fetch the verification key for your app. You can avoid this API request by copying your verification key from the Configuration > App settings page of the Dashboard and passing it as a second parameter to verifyAuthToken:

const verifiedClaims = await privy.verifyAuthToken(
  authToken,
  'paste-your-verification-key-from-the-dashboard'
);

Using JavaScript libraries

You can also use common JavaScript libraries to verify tokens:

To start, install jose:

npm i jose

Then, load your Privy public key using jose.importSPKI:

const verificationKey = await jose.importSPKI(
  "insert-your-privy-verification-key",
  "ES256"
);

Lastly, using jose.jwtVerify, verify that the JWT is valid and was issued by Privy!

const accessToken = "insert-the-users-access-token";
try {
  const payload = await jose.jwtVerify(accessToken, verificationKey, {
    issuer: "privy.io",
    audience: "insert-your-privy-app-id",
  });
  console.log(payload);
} catch (error) {
  console.error(error);
}

If the JWT is valid, you can extract the JWT’s claims from the payload. For example, you can use payload.sub to get the user’s Privy DID.

If the JWT is invalid, this method will throw an error.

Managing expired access tokens

A user’s access token might expire while they are actively using your app. For example, if a user does not take action on an application for an extended period of time, the access token can become expired.

  • Handle invalid token errors: In these scenarios, if a method returns with an 'invalid auth token' error, we recommend calling the getAccessToken method with a time-based backoff until the user’s access token is refreshed with an updated expiration time.
  • Return errors from backend: If you receive an expired access token in your backend, return an error to your client, and as above, trigger getAccessToken in your client.
  • Handle failed refreshes: If the user’s access token cannot be refreshed, the user will be logged out.