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

# Session Login

> Authenticate a connection or request on behalf of a wallet.

Session login proves that a connection (WebSocket) or a request (HTTP) may act on
behalf of a wallet. It works over both transports and returns the list of
subaccount IDs the caller is authenticated for.

<Warning>
  Logging in does **not** authorize trading. Every state-changing action carries
  its own EIP-712 signature that the protocol re-verifies independently — see
  [Action signing](/authentication/action-signing).
</Warning>

## What you sign

The signed message is the **current time in milliseconds as a decimal string**
(`Date.now().toString()`), signed with [EIP-191](https://eips.ethereum.org/EIPS/eip-191)
`personal_sign`. There is no separate challenge or login nonce — the timestamp is
the anti-replay token. The signer may be the wallet itself or a delegated
[session key](/authentication/session-keys).

The three values — `wallet`, `timestamp`, `signature` — are the same on both
transports. WebSocket sends them as `public/login` params; HTTP sends them as
`X-Derive*` headers. The server reads each value from the header **or** the
request params interchangeably.

## WebSocket

Open the socket, then call `public/login` with `{wallet, timestamp, signature}`.
The `result` is the array of subaccount IDs the connection is now authenticated
for (an empty session-key subaccount list resolves to all of the wallet's current
subaccounts).

<CodeGroup>
  ```typescript TypeScript (SDK) theme={null}
  import { DeriveClient } from '@derivexyz/derive-ts';

  const client = new DeriveClient({
    network: 'mainnet',
    wallet: process.env.PRIVATE_KEY!,
  });
  await client.connect(); // open the websocket
  const subaccountIds = await client.login(); // EIP-191 login over public/login
  // The client re-logs in automatically after a reconnect.

  ```

  ```python Python (SDK) theme={null}
  # Coming soon.
  ```

  ```rust Rust (SDK) theme={null}
  // Coming soon.
  ```
</CodeGroup>

The raw frame and response:

<CodeGroup>
  ```json Request theme={null}
  {
    "id": "1",
    "jsonrpc": "2.0",
    "method": "public/login",
    "params": {
      "wallet": "0xYourWalletAddress",
      "timestamp": "1720512000000",
      "signature": "0x…65-byte personal_sign over the timestamp string"
    }
  }
  ```

  ```json Response theme={null}
  {
    "id": "1",
    "jsonrpc": "2.0",
    "result": [9, 42]
  }
  ```
</CodeGroup>

## HTTP

REST requests authenticate **per request** — there is no separate login call.
Attach the same three values as headers to each private request:
`X-DeriveWallet`, `X-DeriveTimestamp`, `X-DeriveSignature`.

<CodeGroup>
  ```typescript TypeScript (SDK) theme={null}
  import { DeriveClient } from '@derivexyz/derive-ts';

  // No connect()/login(): the client signs and attaches X-Derive\* headers on
  // every private REST call automatically.
  const client = new DeriveClient({
    network: 'mainnet',
    wallet: process.env.PRIVATE_KEY!,
  });
  const subaccountIds = await client.subaccounts.list(); // authenticated over HTTP

  ```

  ```python Python (SDK) theme={null}
  # Coming soon.
  ```

  ```rust Rust (SDK) theme={null}
  // Coming soon.
  ```

  ```bash cURL theme={null}
  # timestamp = current time in milliseconds
  TS=$(($(date +%s) * 1000))
  # SIG = EIP-191 personal_sign over "$TS" (wallet or session key)
  SIG="0x…65-byte signature"

  curl -X POST https://api.derive.xyz/v3/private/get_subaccounts \
    -H "Content-Type: application/json" \
    -H "X-DeriveWallet: 0xYourWalletAddress" \
    -H "X-DeriveTimestamp: $TS" \
    -H "X-DeriveSignature: $SIG" \
    -d '{ "wallet": "0xYourWalletAddress" }'
  ```
</CodeGroup>

## Who may sign the login

The `signature` may come from the wallet itself **or** from a
[session key](/authentication/session-keys) the wallet has delegated:

<Steps>
  <Step title="Direct-wallet auth">
    The recovered signer equals `wallet`. The connection receives full
    authority, and the **timestamp freshness window is enforced** — sign
    immediately before connecting.
  </Step>

  <Step title="Session-key auth">
    The recovered signer is a registered session-key address whose `wallet`
    matches and which is not expired. The key's authority is bounded by its
    [access scopes](/authentication/access-scopes), and if it declares an IP
    whitelist the request IP must be on it. **Freshness is not enforced** on
    this path.
  </Step>
</Steps>

<Info>
  The exact timestamp validity window for direct-wallet auth is
  deployment-specific; allow for it (and a little clock skew) when signing the
  login timestamp.
</Info>

## Re-authentication

Long-lived WebSocket sessions periodically re-verify the stored login signature
as part of the heartbeat, so a connection stays authenticated without a fresh
`public/login`. See [Connecting over WebSocket](/connecting) for heartbeat and
reconnection guidance.


## Related topics

- [Action Signing](/authentication/action-signing.md)
- [Quickstart](/getting-started/quickstart.md)
- [Programmatic Onboarding](/getting-started/depositing.md)
