Verifying tokens in your backend
When your backend receives a request, you may want to identify the user making the request and verify that they are authenticated (as part of your API middleware, or otherwise). A common pattern for doing this is to verify the authorization headers for incoming requests against the Privy public key for your app.
Getting the auth token from a request
To authorize a request received by your backend, you should first get the Privy auth token from the incoming request:
- If your app uses local storage to store a user's session, you should extract this token from the request's
Authorization
header. - If your app uses cookies to store a user's session, you should extract this token from the cookie with the name
privy-token
in the request.
For example, in NextJS, you might extract the auth token from a NextApiRequest
as follows:
- Using local storage
- Using cookies
const authToken = req.headers.authorization.replace('Bearer ', '');
const authToken = req.cookies['privy-token'];
Verifying the auth token
Once you have the user's auth token, you should verify the token against the Privy public key for your app, which you can retrieve from the developer console.
This checks that the user making the request to your backend has successfully authenticated with Privy.
Regardless of if your app uses local storage or cookies, the instructions for verifying the Privy auth token are the same. Below are some code examples for token verification in various languages.
- JavaScript/TypeScript
- Go
- Python
Check out our example with jose
and our example with jsonwebtoken
Using jose
First, load your Privy public key using jose.importSPKI
:
const verificationKey = await jose.importSPKI(/* your Privy public key from the console */, 'ES256');
Then, using jose.jwtVerify
, verify that the JWT is valid and was issued by Privy!
try {
const payload = await jose.jwtVerify(authToken, verificationKey, {
issuer: 'privy.io',
audience: /* your Privy App ID */
});
console.log(payload);
} catch (error) {
console.log(`JWT failed to verify with 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.
Using jsonwebtoken
First, load your Privy public key as a string.
const verificationKey = (/* your Privy public key from the console */).replace(/\\n/g, '\n');
The replace
operation above ensures that any instances of '\n' in the stringified public key are replaced with actual newlines, per the PEM-encoded format.
Then, verify the JWT using jwt.verify
:
try {
const decoded = jwt.verify(authToken, verificationKey, {
issuer: 'privy.io',
audience: /* your Privy App ID */
});
console.log(decoded);
} catch (error) {
console.log(`JWT failed to verify with error ${error}.`);
}
If the JWT is valid, you can extract the JWT's claims from decoded
. For example, you can use decoded.sub
to get the user's Privy DID.
If the JWT is invalid, this method will throw an error.
Using golang-jwt
First, load your Privy public key and app ID as strings:
verificationKey := "your-privy-public-key"
appId := "your-privy-app-id"
Next, parse the claims from the JWT and verify that they are valid:
// 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(authToken, &PrivyClaims{}, keyFunc)
if err != nil {
fmt.Println("JWT signature is invalid.")
}
// Parse the JWT claims into our 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)
}
Using pyjwt
First, load your Privy public key and app ID as variables:
verificationKey = 'your-privy-public-key'
appId = 'your-privy-app-id'
Next, verify that your JWT is valid:
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, this method will throw an error.
Don't see your language/framework, or still have questions? Shoot us an email at [email protected] with more info about your setup! We're here to help.