> ## Documentation Index
> Fetch the complete documentation index at: https://www.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Best Practices

> Security and authentication best practices for Dynamic — JWT management, session configuration, MFA, CORS, and protecting your backend.

## Overview

Dynamic provides a range of security tools and configuration options. Not every measure will be necessary for every use case — evaluate what best suits your risk profile, business requirements, and user experience. The recommendations below focus on authentication-specific practices. For broader security guidance (including embedded wallet security, developer credentials, and attack vector mitigations), see the [Security overview](/overview/security/overview) and [Security Best Practices](/overview/security/recommendedpaths).

## JWT lifetime and storage

* **Keep JWT lifetime short.** Configure the JWT expiration to the shortest acceptable duration that balances security and user experience. The maximum is 30 days. Adjust this in your [dashboard security settings](/overview/developer-dashboard/security).
* **Never save or log user JWTs.** JWTs are session credentials — treat them like passwords. Do not persist them in databases, log files, or analytics.
* **Consider cookie-based storage.** Cookie storage makes the JWT inaccessible to client-side JavaScript, reducing the risk of XSS-based session theft. Use this when your architecture supports same-origin requests to your backend. See [Cookie-based authentication](/overview/authentication/dynamic-auth/auth-methods#cookie-based-authentication) for setup.
* **Use in-app storage only when needed.** If you need to pass the JWT to third-party integrations or your backend via explicit headers, in-app storage is appropriate — but be aware of the increased XSS exposure.

<Note>
  When using Dynamic-powered embedded wallets without transactional MFA, limit the JWT lifetime further — since the wallet is primarily gated by the JWT and the method used to log in.
</Note>

## Multi-factor authentication

* **Enable MFA for high-value applications.** MFA significantly reduces the risk of account takeover. Dynamic supports [TOTP and Passkey](/overview/authentication/mfa) as second factors.
* **Consider action-based MFA for sensitive operations.** Rather than requiring MFA at every login, you can require it only for high-value actions (e.g. large transactions, key export). This improves UX while protecting critical operations.
* **Do not rely on SMS as a sole factor.** If you use SMS for primary authentication, add a second factor (TOTP or Passkey) for stronger security.

## Server-side JWT verification

* **Always verify JWTs on your backend.** Never trust a client-sent JWT without cryptographically verifying the signature and validating claims (`iss`, `exp`, `aud`). See [Tokens](/overview/authentication/tokens#verifying-the-jwt).
* **Cache the JWKS public key.** Fetch the public key from the [JWKS endpoint](/overview/authentication/tokens#getting-the-verification-key) and cache it to avoid per-request latency. Refresh the cache when you encounter an unknown `kid`.
* **Verify the scope includes `user:basic`.** The JWT `scope` claim is a space-separated list; a fully authenticated user's token will include `user:basic` in that list. If `user:basic` is not present (e.g. scope is `requiresAdditionalAuth` for MFA-pending tokens), the user has not completed authentication. **Reject or limit access for tokens whose scope does not include `user:basic`** — these tokens should not be trusted for protected operations.

## CORS and allowed origins

* **Configure specific CORS origins.** Add explicit domains in your [dashboard settings](https://app.dynamic.xyz/dashboard/developer/security) to prevent unauthorized websites from using your environment ID.
* **Avoid wildcards.** Use exact domains rather than wildcard patterns, especially in production.
* **Keep production strict.** Do not include `localhost` or development domains in your live environment's allowed origins.

## Session management

* **Implement proper logout flows.** Ensure your application calls the SDK's logout method when users sign out, clearing the session and JWT from the client. See [Revoking Sessions](/overview/authentication/revoking-sessions).
* **Use server-side revocation for security incidents.** If an account is compromised, revoke all sessions for the user via the API to force re-authentication across all devices.
* **Monitor session events.** Use [webhooks](/overview/developer-dashboard/webhooks/events) (`user.session.revoked`, `admin.user.session.revoked`) to track session lifecycle and detect anomalies.

## Access control

* **Use access lists and gates at authentication time.** Configure [access control](/overview/access-control/overview) rules to block or scope users during authentication rather than only checking permissions on your backend.
* **Use Return scopes for fine-grained authorization.** Rather than blocking users entirely, add scopes to the JWT that your app and backend can use for feature gating.

## API key security

* **Store API keys on the backend only.** Dynamic API tokens should never be exposed in client-side code, public repositories, or browser-accessible storage.
* **Rotate API keys regularly.** Institute an internal policy for periodic key rotation.
* **Use role-based permissions.** Restrict which team members can access API tokens and dashboard features using Dynamic's [role-based permissions](/overview/developer-dashboard/invite-members).

## Rate limits

Familiarize yourself with Dynamic's rate limits for IP addresses, project environments, and endpoints. See [Rate Limits](/overview/rate-limits) for details.
