> ## 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.

# Wallet sign-in

> auth.wallet.signIn, plus the getNonce / createMessage / verify primitives — mint a Dynamic user JWT with a private-key signature (currently SIWE)

Methods on the `wallet` flow of a [createAuthClient](/node/reference/auth/create-auth-client) client. `signIn` runs the whole flow in one call; `getNonce`, `createMessage`, and `verify` are the underlying primitives for custom message construction.

The signer can be a human user (signing in their own wallet UI) or the agent itself signing programmatically with an agent signing token — only a valid signature from the wallet address is required, so SIWE supports fully autonomous agents with no human in the loop. See [Sign In with a Private Key](/node/agents/sign-in-with-a-private-key) for both patterns.

## signIn

### Function Signature

```typescript theme={"system"}
auth.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>
```

One call for the whole flow: fetches a nonce, builds the sign-in message internally, collects the signature from your `signMessage` callback, and verifies it. Key custody stays behind the callback — the wallet key never enters the client.

### Parameters

* **`address`** (`string`) - The signing wallet's public address. Must be a 0x-prefixed 20-byte hex address.
* **`signMessage`** (`(message: string) => Promise<string>`) - Your signer callback. Receives the sign-in message; returns the signature.
* **`sessionPublicKey`** (`string`, optional) - Compressed P-256 public key hex from [generateSessionKeyPair](/node/reference/auth/generate-session-key-pair). Binds the session key into the minted JWT — required if you will use a session signer with [authenticateJwt](/node/reference/auth/authenticate-jwt) for `backUpToDynamic` flows.
* **`domain`** / **`uri`** (`string`, optional) - The origin the sign-in presents as. Default: derived from the `appOrigin` set on `createAuthClient` (`domain` = its host, `uri` = the origin). Passing both per call overrides `appOrigin`.
* **`chainId`** (`number`, optional) - EVM-specific. Defaults to `1`.
* **`statement`** (`string`, optional) - Human-readable statement included in the message.
* **`walletName`** (`string`, optional) / **`walletProvider`** (`WalletProviderEnum`, optional) - Forwarded to `verify`; see its defaults below.

### Returns

* **`Promise<AuthResult>`** - `{ jwt, expiresAt, userId }`. Pass `jwt` to `authenticateJwt`; never log it.

### Errors

* `address` not a 0x-prefixed 20-byte hex address → `"wallet.signIn requires a 0x-prefixed 20-byte hex address"` (thrown before any network call).
* `domain`, `uri`, or `statement` containing line breaks → `"wallet.signIn: <name> must not contain line breaks"` (line breaks would corrupt the EIP-4361 message structure).
* Neither `appOrigin` nor per-call `domain`/`uri` provided → `"wallet.signIn requires domain/uri — pass them or set appOrigin on createAuthClient"`.
* Plus the `verify` errors below once the flow reaches the server.

## getNonce

### Function Signature

```typescript theme={"system"}
auth.wallet.getNonce(): Promise<{ nonce: string }>
```

### Parameters

None.

### Returns

* **`nonce`** (`string`) - Include it in the sign-in message via `createMessage`. Nonces are single-use.

## createMessage

### Function Signature

```typescript theme={"system"}
auth.wallet.createMessage(params: {
  domain: string;
  address: string;
  uri: string;
  chainId: number;
  nonce: string;
  statement?: string;
  issuedAt?: string;
}): string
```

Synchronous — builds the EIP-4361-style message locally; no network call.

### Parameters

* **`domain`** (`string`) - The domain requesting the sign-in (for example, `yourapp.example.com`).
* **`address`** (`string`) - The signing wallet's public address.
* **`uri`** (`string`) - The URI of the sign-in origin (for example, `https://yourapp.example.com`).
* **`chainId`** (`number`) - The EVM chain ID (for example, `1` for Ethereum mainnet).
* **`nonce`** (`string`) - The nonce from `getNonce`.
* **`statement`** (`string`, optional) - Human-readable statement shown to the user in their wallet.
* **`issuedAt`** (`string`, optional) - ISO 8601 timestamp. Defaults to now.

### Returns

* **`string`** - The sign-in message. Have the wallet key holder sign it, then pass both to `verify`.

## verify

### Function Signature

```typescript theme={"system"}
auth.wallet.verify(params: {
  message: string;
  signature: string;
  walletAddress: string;
  sessionPublicKey?: string;
  walletName?: string;
  walletProvider?: WalletProviderEnum;
  chain?: ChainEnum;
}): Promise<AuthResult>
```

### Parameters

* **`message`** (`string`) - The exact message from `createMessage`.
* **`signature`** (`string`) - The signature over the message.
* **`walletAddress`** (`string`) - The signing wallet's public address (must match the message).
* **`sessionPublicKey`** (`string`, optional) - Compressed P-256 public key hex from [generateSessionKeyPair](/node/reference/auth/generate-session-key-pair). Binds the session key into the minted JWT.
* **`walletName`** (`string`, optional) - Defaults to `'unknown'`.
* **`walletProvider`** (`WalletProviderEnum`, optional) - Defaults to `custodialService`.
* **`chain`** (`ChainEnum`, optional) - Defaults to `EVM`.

### Returns

* **`Promise<AuthResult>`** - `{ jwt, expiresAt, userId }`. Pass `jwt` to `authenticateJwt`; never log it.

## Example

```typescript theme={"system"}
import { createAuthClient, generateSessionKeyPair } from '@dynamic-labs-wallet/node';

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

// The public address of the wallet that will sign the message.
const walletAddress = '0xUserPublicAddress';

const { jwt, expiresAt } = await auth.wallet.signIn({
  address: walletAddress,
  // The wallet key holder signs — a human in your app's UI, or the agent's own key.
  signMessage: (message) => collectSignatureFromUser(message),
  sessionPublicKey: publicKeyHex,
  statement: 'Authorize this agent to use your Dynamic wallet',
});
```

## Error Handling

```typescript theme={"system"}
try {
  const result = await auth.wallet.signIn({ address, signMessage, sessionPublicKey });
} catch (error) {
  // - Invalid or expired signature/nonce (401): "SIWE sign-in failed: the code
  //   or signature is invalid or expired"
  // - MFA-required account: "account requires MFA, which the headless auth
  //   client does not support"
  // - Cookie-auth environment: sign-in returned no JWT in the response body
  // - Input validation errors thrown before any network call — see Errors above
  console.error('Sign-in failed:', error);
}
```

Sign-in endpoints are rate limited per IP address (429 when exceeded) — see [Rate Limit Policies](/overview/rate-limits).

## Related Functions

* [`createAuthClient()`](/node/reference/auth/create-auth-client) - Create the auth client (including `appOrigin`)
* [Email OTP sign-in](/node/reference/auth/email-otp-sign-in) - Email-based alternative
* [`authenticateJwt()`](/node/reference/auth/authenticate-jwt) - Use the minted JWT
