Skip to main content

Function Signature

createAuthClient(options: {
  environmentId: string;
  baseUrl?: string;
  fetchApi?: FetchAPI;
  appOrigin?: string;
}): DynamicAuthClient
Exported from @dynamic-labs-wallet/node.

Description

Creates a headless auth client — the server-side counterpart to the JS client SDK’s sign-in flows. It mints a Dynamic user JWT via email OTP or Sign-In With Ethereum (SIWE) so a server-side agent can obtain a JWT without a browser, then hand it to authenticateJwt. The client holds configuration only — no secrets, no persistence. Sign-in prompting (collecting the OTP code, getting the sign-in message signed) and storage of the resulting JWT stay with you.

Parameters

Required Parameters

  • environmentId (string) - Your Dynamic environment ID.

Optional Parameters

  • appOrigin (string) - The origin SIWE sign-ins present as (for example, https://yourapp.example.com). It must be on the environment’s allowlisted CORS origins. wallet.signIn derives its domain and uri from it; per-call params override. Throws "createAuthClient appOrigin is not a valid URL" at sign-in time if it does not parse as a URL.
  • baseUrl (string) - Override the Dynamic API base URL. Defaults to the production API.
  • fetchApi (FetchAPI) - Custom fetch implementation (tests, proxies).

Returns

  • DynamicAuthClient - An object with two sign-in flows:
interface DynamicAuthClient {
  email: {
    sendOtp(email: string): Promise<{ verificationUUID: string }>;
    verifyOtp(params: {
      verificationUUID: string;
      code: string;
      sessionPublicKey?: string;
    }): Promise<AuthResult>;
  };
  wallet: {
    signIn(params: {
      address: string;
      signMessage: (message: string) => Promise<string>;
      sessionPublicKey?: string;
      domain?: string;
      uri?: string;
      chainId?: number;
      statement?: string;
      walletName?: string;
      walletProvider?: WalletProviderEnum;
    }): Promise<AuthResult>;
    getNonce(): Promise<{ nonce: string }>;
    createMessage(params: {
      domain: string;
      address: string;
      uri: string;
      chainId: number;
      nonce: string;
      statement?: string;
      issuedAt?: string;
    }): string;
    verify(params: {
      message: string;
      signature: string;
      walletAddress: string;
      sessionPublicKey?: string;
      walletName?: string;
      walletProvider?: WalletProviderEnum;
      chain?: ChainEnum;
    }): Promise<AuthResult>;
  };
}
Each sign-in resolves to an AuthResult:
interface AuthResult {
  jwt: string; // Pass to authenticateJwt. Custody is yours; never log it.
  expiresAt: number;
  userId?: string;
}
See Email OTP sign-in and Wallet sign-in for per-method parameters, returns, and errors.

Example

import { createAuthClient } from '@dynamic-labs-wallet/node';

const auth = createAuthClient({
  environmentId: 'your-environment-id',
  appOrigin: 'https://yourapp.example.com',
});

const { verificationUUID } = await auth.email.sendOtp('user@example.com');
const { jwt } = await auth.email.verifyOtp({
  verificationUUID,
  code: otpCodeFromUser,
  sessionPublicKey: publicKeyHex,
});

Error Handling

All methods throw Error with a descriptive message:
  • 401 responses"<operation> failed: the code or signature is invalid or expired".
  • Other HTTP errors → the server’s error message and status code (codes and messages only — never tokens).
  • MFA-required accounts"account requires MFA, which the headless auth client does not support".
  • Cookie-auth environments → sign-in responses carry no JWT in the body; the client throws explaining that headless clients cannot consume cookie auth.
  • wallet.signIn input validation → thrown before any network call; see Wallet sign-in.
Last modified on July 9, 2026