Skip to main content
Use external JWT sign-in when each agent is a first-class identity in your own identity or authorization system: your issuer signs a JWT attesting to the agent’s identity, and the agent exchanges it for a Dynamic user JWT. This is the integration point for external identity systems — each agent identity your issuer attests becomes its own Dynamic user with its own MPC wallets, rather than a fleet of agents sharing one user or one credential. For the other paths, see Sign In with Email OTP (human relays a code) or Sign In with a Private Key (a signature authorizes the sign-in).
Before this: read the Agent Wallets overview, install @dynamic-labs-wallet/node, and configure third-party auth for your environment in the dashboard (issuer, JWKS URL, audience). Bring Your Own Auth is an enterprise feature.

Generate a session key pair

generateSessionKeyPair returns a P-256 key pair: publicKeyHex (33-byte compressed public key, lowercase hex) and privateKeyJwk (the private key as a JWK). The SDK stores nothing — you persist the JWK in your secrets vault.
The returned JWK is extractable so it can be handed back to you — it is a convenience, not a security boundary. For non-extractable custody, skip generateSessionKeyPair: generate a P-256 key in your KMS, HSM, or enclave, pass its compressed public key hex at sign-in, and back getSessionSignature with the external signer.

Sign in with externalJwt.signIn

createAuthClient performs Dynamic sign-in flows headlessly — the server-side counterpart to the JS client SDK’s sign-in. It holds configuration only: no secrets, no persistence. Obtaining the external JWT is your application’s job: the SDK never mints or verifies it — you supply it, it is used once for the exchange, and it is never persisted or logged. Dynamic verifies it server-side against the environment’s configured issuer, JWKS URL, and audience.
Pass sessionPublicKey at sign-in. This binds the session key into the minted JWT and is what makes the signed-session proof verifiable. If you omit it, wallet creation and signing with backUpToDynamic: true fail at the backup and recovery calls. This is the most commonly missed step.
Each distinct sub claim in your issuer’s JWTs is a distinct Dynamic user with its own wallets, so keep an agent’s subject stable across runs to keep operating the same user’s wallets — and give each agent its own subject to keep their wallets separate. Re-authentication at the refresh limit is another exchange with a fresh JWT from your issuer — programmatic if your issuer mints programmatically, so no human is required. This also keeps each agent’s lifecycle in your hands: an agent your issuer stops minting JWTs for cannot sign in again once its current session can no longer be refreshed. Sign-in resolves to { jwt, expiresAt, userId }. externalJwt.signIn throws before any network call when jwt is blank. Sign-in fails with a descriptive error when the external JWT’s signature or claims fail verification, when the environment does not have third-party auth configured (the server responds 422 with This feature is not enabled), when the account requires MFA (the headless client does not support MFA), or when the environment uses cookie-based auth (the response carries no JWT in the body).

What you must persist

The SDK is stateless by design — your store is the source of truth. After sign-in you hold two artifacts to keep safe: the session private key (secrets vault or KMS) and the Dynamic user JWT (update it on every refresh). The external JWT is single use — do not persist it; request a new one from your issuer when you need to sign in again. See Storage & Security for the full persistence matrix.

Next step

Hand the JWT and the session private key to the wallet client: Use Agent Wallets.

API reference

Last modified on July 17, 2026