Manual JWT Verification
Your Clerk-generated session tokens are essentially JWTs which are signed using your instance's private key and can be verified using your instance's public key. Depending on your architecture, these tokens will be in your backend requests either via a cookie named __session
or via the Authorization header.
For every request, you must validate its token to make sure it hasn't expired and it is authentic (i.e. no malicious user tried to tamper with it). If these validations pass, then it means that the user is authenticated to your application and you should consider them signed in.
Retrieve the session token
Retrieve the session token from either __session
cookie for a same origin request or from the Authorization header for cross origin requests.
Get your instance's Public Key
There are two ways to obtain your public key:
-
Using the Backend API in JSON Web Key Set (JWKS) format at the following endpoint https://api.clerk.dev/v1/jwks.
-
Using the Frontend API in JSON Web Key Set (JWKS) format at the following endpoint
https://<YOUR_FRONTEND_API>/.well-known/jwks.json
. This can be obtained from the API Keys section of the Clerk Dashboard. Scroll down and click on Advanced and in the JWT public key section, copy the JWKS URL.
- Using the PEM public key provided in the API Keys section of the Clerk Dashboard. Scroll down and click on Advanced and in the JWT public key section, copy the PEM public key. This option should only be used as a fallback for when the first two options are not available.
Verify the token signature
To verify the token signature, you should:
- Use the above Public Key to verify the token's signature
- Validate that the token is not expired, by checking the
exp
andnbf
claims - If the
azp
claim exists, validate that equals any of your known Origins that are permitted to generate those tokens. This is an extra security check that we highly recommend that you do.
If the above process is successful, it means that the user is signed in to your application and you can consider them authenticated. You can also retrieve the session ID and user ID out of the token's claims.
Example usage
pages/api/token-example.tsimport type { NextApiRequest, NextApiResponse } from "next"; import Cookies from "cookies"; import jwt from "jsonwebtoken"; export default async function (req: NextApiRequest, res: NextApiResponse) { const publicKey = process.env.CLERK_PEM_PUBLIC_KEY; const cookies = new Cookies(req, res); const sessToken = cookies.get("__session"); const token = req.headers.authorization; if (sessToken === undefined && token === undefined) { res.status(401).json({ error: "not signed in" }); return; } try { let decoded = ""; if (token) { decoded = jwt.verify(token, publicKey); res.status(200).json({ sessToken: decoded }); return; } else { decoded = jwt.verify(sessToken, publicKey); res.status(200).json({ sessToken: decoded }); return; } } catch (error) { res.status(400).json({ error: "Invalid Token", }); return; } }
This will return the following JSON which you can then use to validate the azp and the expiration. You then have a valid session
and user_id
that you can use in your application.
{ "sessToken": { "azp": "http://localhost:3000", "exp": 1687906422, "iat": 1687906362, "iss": "https://magical-marmoset-51.clerk.accounts.dev", "nbf": 1687906352, "sid": "sess_2Ro7e2IxrffdqBboq8KfB6eGbIy", "sub": "user_2RfWKJREkjKbHZy0Wqa5qrHeAnb" } }