Skip to content

Verifying the user's 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-io/server-auth

Pass the user's acccess token as a string to the PrivyClient's verifyAuthToken method:

tsx
// `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).

Using a third-party library

See instructions below verifying access tokens using third-party libraries from various languages.

JavaScript/TypeScript

Two common libraries for verifying tokens in JavaScript/TypeScript are jose and jsonwebtoken.

To start, install jose:

sh
npm i jose

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

tsx
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!

tsx
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.

Go

For Go, the golang-jwt library is a popular choice for token verification. To start, install the library:

sh
go get -u github.com/golang-jwt/jwt/v5

Next, load your Privy verification key and app ID as strings:

go
verificationKey := "insert-your-privy-verification-key"
appId := "insert-your-privy-app-id"

Then, parse the claims from the JWT and verify that they are valid:

go
accessToken := "insert-the-users-access-token"

// Defining a Go type for Privy JWTs
type PrivyClaims struct {
	AppId      string `json:"aud,omitempty"`
	Expiration uint64 `json:"exp,omitempty"`
	Issuer     string `json:"iss,omitempty"`
	UserId     string `json:"sub,omitempty"`
}

// This method will be used to check the token's claims later
func (c *PrivyClaims) Valid() error {
	if c.AppId != appId {
		return errors.New("aud claim must be your Privy App ID.")
	}
	if c.Issuer != "privy.io" {
		return errors.New("iss claim must be 'privy.io'")
	}
	if c.Expiration < uint64(time.Now().Unix()) {
		return errors.New("Token is expired.");
	}

	return nil
}

// This method will be used to load the verification key in the required format later
func keyFunc(token *jwt.Token) (interface{}, error) {
	if token.Method.Alg() != "ES256" {
		return nil, fmt.Errorf("Unexpected JWT signing method=%v", token.Header["alg"])
	}
    // https://pkg.go.dev/github.com/dgrijalva/jwt-go#ParseECPublicKeyFromPEM
	return jwt.ParseECPublicKeyFromPEM([]byte(verificationKey)), nil
}

// Check the JWT signature and decode claims
// https://pkg.go.dev/github.com/dgrijalva/jwt-go#ParseWithClaims
token, err := jwt.ParseWithClaims(accessToken, &PrivyClaims{}, keyFunc)
if err != nil {
    fmt.Println("JWT signature is invalid.")
}

// Parse the JWT claims into your custom struct
privyClaim, ok := token.Claims.(*PrivyClaims)
if !ok {
    fmt.Println("JWT does not have all the necessary claims.")
}

// Check the JWT claims
err = Valid(privyClaim);
if err {
    fmt.Printf("JWT claims are invalid, with error=%v.", err);
    fmt.Println();
} else {
    fmt.Println("JWT is valid.")
    fmt.Printf("%v", privyClaim)
}

If the JWT is valid, you can access its claims, including the user's DID, from the privyClaim struct above.

If the JWT is invalid, an error will be thrown.

Python

For Python, the pyjwt library is a popular choice for token verification.

To start, install the library:

sh
pip install PyJWT

Next, load your Privy public key and app ID as variables:

python
verificationKey = 'insert-your-privy-verification-key'
appId = 'insert-your-privy-app-id'

Lastly, verify that your JWT is valid using the library's decode method:

python
accessToken = 'insert-the-users-access-token'

try:
    decoded = jwt.decode(authToken, verificationKey, issuer='privy.io', audience=appId, algorithms=['ES256'])
    print(decoded)
except:
    print("Token verification failed")

If the JWT is valid, you can extract the JWT's claims from the decoded object. For example, you can use decoded['sub'] to get the user's Privy DID.

If the JWT is invalid, decode will throw an error.