Skip to main content

Introduction

When a user signs in with Dynamic, your client receives a JSON Web Token (JWT) from Dynamic. To trust the user’s identity on your backend, you must verify that JWT on the server before using any claims (user ID, wallets, etc.). Client-sent tokens cannot be trusted until they are cryptographically verified. 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. The flow is:
  1. Client gets the JWT from Dynamic (the way you obtain it depends on your SDK—e.g. getAuthToken(), sdk.auth.token, etc.).
  2. Client sends the JWT to your backend (e.g. in the Authorization: Bearer <token> header).
  3. Backend verifies the JWT using Dynamic’s public key (signature and claims). This step is identical for all client SDKs.
  4. Backend uses the verified claims (e.g. sub for user ID, verified_credentials) to authorize the request.
No Dynamic SDK is required on the server. You can verify the JWT in any language (Node, Python, Go, etc.) using any JWT library that supports RS256 and JWKS.

Understanding JWTs

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:
ClaimDescription
issIssuer. Dynamic sets this to the issuer for your environment (e.g. app.dynamic.xyz/<environment_id>).
audAudience. Intended audience for the token.
subSubject. The Dynamic user ID.
iatIssued at. Unix timestamp when the token was issued.
expExpiration. Unix timestamp when the token expires.

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:
ClaimDescription
aliasAlias from customer information capture.
emailEmail from customer information capture.
environment_idUnique ID of the project environment (from the dashboard API).
given_nameFirst name from customer information capture.
family_nameLast name from customer information capture.
listsNames of access lists enabled for this user. See Access Control.
verified_credentialsList of all verified credentials connected to this user.
verified_accountIf present, the most recently signed and verified account.

Example payload

{
  "alias": "john",
  "aud": "https://dashboard.hello.xyz",
  "verified_credentials": [
    {
      "address": "0x000123abc",
      "chain": "eip155",
      "id": "af615228-99e5-48ee-905d-4575f0a6bfc9",
      "wallet_name": "metamask"
    }
  ],
  "email": "[email protected]",
  "environment_id": "fb6dd9d1-09f5-43c3-8a8c-eab6e44c37f9",
  "family_name": "bot",
  "given_name": "jon",
  "iss": "app.dynamic.xyz/fb6dd9d1-09f5-43c3-8a8c-eab6e44c37f9",
  "lists": ["Community dashboard access list"],
  "sub": "d261ee91-8ea0-4949-b8bb-b6ab4f712a49",
  "verified_account": {
    "address": "0x000123abc",
    "chain": "eip155",
    "id": "af615228-99e5-48ee-905d-4575f0a6bfc9",
    "wallet_name": "metamask"
  },
  "iat": 1660677597,
  "exp": 1660684797
}

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:
  1. Developer Dashboard — In the API tab of your developer dashboard, you can find the public key for your environment.
  2. JWKS endpoint — Dynamic exposes a JWKS (JSON Web Key Set) endpoint so your server can fetch the public key(s) used to sign tokens:
    GET https://app.dynamic.xyz/api/v0/sdk/{environmentId}/.well-known/jwks
    
    Replace {environmentId} with your environment ID (from the dashboard). Your JWT library can use this URL to resolve the key by the kid (key ID) in the JWT header. See the Find JWKS for public key API reference.
Any backend stack can call this endpoint or use the dashboard key; no SDK is required.

Verifying the JWT yourself

On your backend, after you receive the JWT (e.g. from the Authorization: Bearer <token> header):
  1. Verify the signature — Decode the JWT and verify the signature using the public key (RS256). If verification fails, reject the request.
  2. Validate claims — Ensure:
    • iss matches the issuer for your environment (e.g. app.dynamic.xyz/<environment_id>).
    • exp is in the future and iat is reasonable (token not expired, not from the future).
    • aud if you use audience restrictions.
  3. Optional: handle scopes — Dynamic may include a scopes claim. For example, requiresAdditionalAuth indicates the user must complete additional verification (e.g. MFA) before the token is fully trusted. Reject or handle such tokens according to your policy.
No Dynamic SDK is required on the server. Use any JWT library that supports RS256 and (if you use the JWKS URL) JWKS key resolution—e.g. in Node (jsonwebtoken + jwks-rsa), Python (PyJWT), Go (jwt-go), etc. The verification logic is the same regardless of which client SDK sent the token.

Security

Server-side and JWT security are critical. Follow Dynamic’s security guidance for JWT lifetime and storage (e.g. never log or persist user JWTs), cookie-based authentication to reduce session hijacking risk, secure storage of API keys (backend only, never on the client), and protection against common attack vectors. See the Security overview and Best Practices (including JWT length and storage, CORS, and developer credentials). For threat-specific mitigations, see Mitigating attack vectors.

Using your own JWT with Dynamic

If you have your own authentication system or another auth provider, you can use your own JWTs to sign users into Dynamic. Dynamic will verify your JWT (using a JWKS endpoint you provide) and then issue its own session/JWT. This is useful for bringing existing users into Dynamic without changing your auth flow. For setup and requirements (e.g. iss, sub, exp), see Third-party authentication (enterprise feature).

Next steps