Introduction
When a user signs in with Dynamic, your client receives multiple JSON Web Token (JWTs). After authentication Dynamic issues both an access token and an id token. To trust the user’s identity on your backend, you must verify the access token on the server before using any claims. Client-sent tokens cannot be trusted until they are cryptographically verified.Types
Access Tokens (minJwt)
The access token JWT is the session credential — it grants the user access to protected resources like wallets and user data on Dynamic’s servers and can be used by customers to validate access on customer servers if utilizing Dynamic as the authentication provider for your app.ID Tokens (jwt)
The ID token JWT is meant to be used by the client application only. The id token contains claims about the authenticated user which can be verified by the applicationJWT structure
A JWT has three parts: header, payload (claims), and signature. Dynamic signs JWTs with RS256 (RSA signature with SHA-256). The signing key is unique per environment, so you use the public key for your environment to verify that the token was issued by Dynamic and has not been tampered with.Standard claims
Dynamic’s JWTs include standard JWT claims:| Claim | Description |
|---|---|
iss | Issuer. Dynamic sets this to the issuer for your environment (e.g. app.dynamic.xyz/<environment_id>). |
aud | Audience. Intended audience for the token. |
sub | Subject. The Dynamic user ID. |
iat | Issued at. Unix timestamp when the token was issued. |
exp | Expiration. Unix timestamp when the token expires. |
sid | Session Id. The unique identifier for the Dynamic session |
Dynamic-specific claims
The payload may also include Dynamic-specific claims. These fields are optional and depend on whether you collect the information during onboarding:| Claim | Description |
|---|---|
alias | Alias from customer information capture. |
email | Email from customer information capture. |
environment_id | Unique ID of the project environment (from the dashboard API). |
given_name | First name from customer information capture. |
family_name | Last name from customer information capture. |
lists | Names of access lists enabled for this user. See Access Control. |
verified_credentials | List of all verified credentials connected to this user. |
verified_account | If present, the most recently signed and verified account. |
Example payload
Token storage
The JWT is stored either in a cookie or in in-app storage (e.g. browser or device storage), depending on your project’s auth storage settings:- Cookie storage: The browser sends the token to your backend automatically on same-origin requests. The app cannot read the token directly — your backend should rely on the cookie for auth.
- In-app storage: The app can read the token and send it explicitly (e.g. in an
Authorization: Bearer <token>header). Use this when you need to pass the JWT to your backend or third-party integrations.
Getting the verification key
To verify a Dynamic-issued JWT, you need the public key for your environment. You can get it in two ways:- Developer Dashboard — In the API tab of your developer dashboard, you can find the public key for your environment.
-
JWKS endpoint — Dynamic exposes a JWKS (JSON Web Key Set) endpoint so your server can fetch the public key(s) used to sign tokens:
Replace
{environmentId}with your environment ID (from the dashboard). Your JWT library can use this URL to resolve the key by thekid(key ID) in the JWT header. See the Find JWKS for public key API reference.
Verifying the JWT
Regardless of which Dynamic SDK you use on the client (React, React Native, Flutter, Swift, Kotlin, JavaScript, etc.), your backend verifies the same Dynamic-issued JWT the same way:- Client gets the JWT from Dynamic (the way you obtain it depends on your SDK — e.g.
getAuthToken(),sdk.auth.token, etc.). - Client sends the JWT to your backend (e.g. in the
Authorization: Bearer <token>header). - Backend verifies the JWT using Dynamic’s public key (signature and claims).
- Backend uses the verified claims (e.g.
subfor user ID,verified_credentials) to authorize the request.
- Verify the signature — Decode the JWT and verify the signature using the public key (RS256). If verification fails, reject the request.
-
Validate claims — Ensure:
issmatches the issuer for your environment (e.g.app.dynamic.xyz/<environment_id>).expis in the future andiatis reasonable (token not expired, not from the future).audif you use audience restrictions.
-
Optional: handle scopes — Dynamic may include a
scopesclaim. For example,requiresAdditionalAuthindicates the user must complete additional verification (e.g. MFA) before the token is fully trusted. Reject or handle such tokens according to your policy.
jsonwebtoken + jwks-rsa), Python (PyJWT), Go (jwt-go), etc.
Security
Follow Dynamic’s security guidance for tokens:- Configure JWT lifetime to the shortest acceptable duration to balance security and user experience. The maximum is 30 days. See dashboard security settings.
- Never save or log user JWTs.
- Consider cookie-based authentication to reduce session hijacking risk by making the JWT inaccessible to client-side scripts.
- Store API keys on the backend only — never expose them on the client side.
- Never send Id tokens to the server. Only access tokens.
SDK-specific guides
How you obtain the JWT on the client depends on your SDK:- Server-side verification (JavaScript)
- Server-side verification (React)
- Server-side verification (React Native)
- For Flutter, Swift, Kotlin, and other SDKs, use the auth or session docs for your SDK to get
sdk.auth.token(or equivalent) and send it to your backend; the verification steps above apply unchanged.
Frameworks
- If you use Next.js, see NextAuth with Dynamic.
- For Passport.js, use the official passport-dynamic extension.