Identity tokens provide a secure and efficient way to access user data, especially on the server side. These tokens are JSON Web Tokens (JWTs) whose claims contain information about the currently authenticated user, including their linked accounts, metadata, and more.

Privy strongly recommends using identity tokens when you need user-level data on your server. They allow you to easily pass a signed representation of the current user’s linked accounts from your frontend to your backend directly, letting you verifiably determine which accounts (wallet address, email address, Farcaster profile, etc.) are associated with the current request.

Enabling identity tokens

To enable identity tokens for your application:

  1. Navigate to your application dashboard’s User management > Authentication > Advanced section
  2. Toggle on Return user data in an identity token on the Basics page
  3. Make sure you’re using the latest version of the Privy SDK

Token Format

Privy identity tokens are JSON Web Tokens (JWT), signed with the ES256 algorithm. These JWTs include the following claims:

linked_accounts
string

A stringified array containing a lightweight version of the current user’s linkedAccounts

custom_metadata
string

A stringified version of the current user’s customMetadata

sub
string

The user’s Privy DID

iss
string

The token issuer, which should always be privy.io

aud
string

Your Privy app ID

iat
number

The timestamp of when the JWT was issued

exp
number

The timestamp of when the JWT will expire (generally 1 hour after issuance)

Retrieving identity Tokens

Once you’ve enabled identity tokens, Privy will automatically include the identity token as a cookie on every request from your frontend to your server.

For setups where you cannot use cookies, you can retrieve the identity token using the useIdentityToken hook:

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

function MyComponent() {
  const { identityToken } = useIdentityToken();
  
  // Use the token in your API requests
  const callApi = async () => {
    const response = await fetch('/api/your-endpoint', {
      headers: {
        'privy-id-token': identityToken
      }
    });
  };
  
  return (
    <button onClick={callApi}>Call API</button>
  );
}

We strongly recommend setting a base domain for your application, so that Privy can set the identity token as a more secure HttpOnly cookie.

Reading identity tokens on your server

On your server, you can retrieve the identity token from incoming requests and use it to identify the user.

// pages/api/example.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { getUser } from '@privy-io/server-auth';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  try {
    // Get identity token from cookie
    const idToken = req.cookies['privy-id-token'];
    
    // Or from header if sent that way
    // const idToken = req.headers['privy-id-token'];
    
    if (!idToken) {
      return res.status(401).json({ message: 'Unauthorized' });
    }
    
    // Parse and verify the token
    const user = await getUser({ idToken });
    
    // Now you can use the user data
    return res.status(200).json({ 
      userId: user.id,
      // Other user data...
    });
  } catch (error) {
    console.error('Error verifying identity token:', error);
    return res.status(401).json({ message: 'Invalid token' });
  }
}

Accessing custom metadata

Privy allows you to set custom metadata for a user via backend API requests. This metadata is available in the custom_metadata claim of the identity token.

Here’s how to parse and access it:

import {getUser} from '@privy-io/server-auth';
import * as jose from 'jose';

// Method 1: Using getUser (recommended)
async function getUserWithMetadata(idToken) {
  const user = await getUser({idToken});
  // Custom metadata is already parsed and available
  return user.customMetadata;
}

// Method 2: Manual parsing
async function parseCustomMetadata(idToken) {
  const verificationKey = await jose.importJWK(/* your verification key */);

  try {
    const {payload} = await jose.jwtVerify(idToken, verificationKey, {
      issuer: 'privy.io',
      audience: 'your-privy-app-id'
    });

    if (payload && payload.custom_metadata) {
      return JSON.parse(payload.custom_metadata);
    }

    return {};
  } catch (error) {
    console.error('Error parsing identity token:', error);
    throw error;
  }
}

Refreshing the identity token

A new identity token is automatically issued when a user:

  • Authenticates into the application
  • Links or unlinks an account
  • Refreshes their application page
  • Calls getAccessToken when the access token is expired

To programmatically refresh the identity token, call refreshUser from the useUser hook:

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

function RefreshButton() {
  const {refreshUser} = useUser();

  return <button onClick={refreshUser}>Refresh User Data</button>;
}

Verifying the identity token

When your server receives a request with an identity token, you should verify the token’s signature to authenticate the user. The preferred way is to use the getUser method from @privy-io/server-auth, which handles verification and parsing:

import {getUser} from '@privy-io/server-auth';

async function verifyAndGetUser(idToken) {
  try {
    // This verifies the token signature and parses the payload
    const user = await getUser({idToken});
    return user;
  } catch (error) {
    console.error('Invalid identity token:', error);
    throw new Error('Authentication failed');
  }
}

The verifyAuthToken method will not work on identity tokens, as it is only used to verify Privy access tokens. Always use getUser({idToken}) when working with identity tokens.

For manual verification without using getUser, you can use JWT libraries like jose:

import * as jose from 'jose';

async function verifyIdentityToken(idToken) {
  // Import the public key
  const publicKey = await jose.importJWK({
    // Your public key in JWK format
  });

  try {
    // Verify the token
    const {payload} = await jose.jwtVerify(idToken, publicKey, {
      issuer: 'privy.io',
      audience: 'your-privy-app-id'
    });

    return payload;
  } catch (error) {
    console.error('Token verification failed:', error);
    throw new Error('Authentication failed');
  }
}

Security Considerations

For optimal security when working with identity tokens:

  1. Always verify the token signature before trusting any claims
  2. Check the expiration time (exp claim) to ensure the token is still valid
  3. Set a base domain for your application to enable HttpOnly cookies for the identity token
  4. Use HTTPS for all communication between your frontend and backend
  5. Do not store sensitive information in custom metadata, as it will be included in the identity token
Read more about Privy’s tokens and their security in our security guide.