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

# Sign-In Flow — Returning User Authentication in SocketFi

> How SocketFi authenticates returning users: verifying a passkey challenge, resolving their existing wallet, and issuing a fresh session token.

When a returning user authenticates, SocketFi does not create anything new — no wallet deployment, no key generation. Instead, the SDK verifies that the user controls the same passkey credential registered during sign-up, then looks up their existing wallet and issues a fresh session token. The entire flow takes a few seconds and requires nothing from the user beyond a biometric confirmation.

## The Sign-In Flow

<Steps>
  <Step title="User Initiates Authentication">
    The user clicks your sign-in button and your application calls `socketfi.authenticate()`. The SDK detects an existing account associated with the device and begins the sign-in flow rather than the registration flow.

    ```typescript theme={null}
    const session = await socketfi.authenticate();
    ```

    The call is identical for sign-up and sign-in. SocketFi handles the routing automatically based on whether a registered credential exists for the current device.
  </Step>

  <Step title="Passkey Challenge Issued">
    SocketFi generates a unique one-time challenge and passes it to the browser's WebAuthn API. The platform prompts the user with their registered authenticator.

    ```text theme={null}
    SocketFi Authentication Challenge (unique, expires quickly)
      ↓
    Browser WebAuthn API
      ↓
    Platform Authenticator Prompt (Face ID / Touch ID / Windows Hello / etc.)
    ```

    No challenge is ever reused. If an attacker intercepts a signed response from a previous session, it cannot be replayed against a new challenge.
  </Step>

  <Step title="Signature Verified">
    The user confirms their identity through biometrics or PIN. The authenticator signs the challenge with the private key stored in secure hardware and returns the signature.

    SocketFi verifies three things:

    * The challenge matches the one issued for this request
    * The signature is valid against the registered public key
    * The credential ID belongs to an active, registered account

    If any of these checks fail, authentication is denied and no session is created.

    ```text theme={null}
    User Confirms (Face ID / fingerprint / PIN)
      ↓
    Authenticator Signs Challenge with Private Key
      ↓
    Signature Returned to SocketFi
      ↓
    Verified Against Registered Public Key
      ↓
    Identity Confirmed
    ```
  </Step>

  <Step title="Wallet Resolved">
    Once identity is confirmed, SocketFi maps the verified credential to the user's wallet address. No new wallet is created or deployed.

    ```text theme={null}
    Verified Credential ID
      ↓
    User Profile Lookup
      ↓
    Existing Wallet Address Retrieved
      ↓
    Wallet Resolved
    ```

    The wallet address, balances, transaction history, and all associated state remain exactly as the user left them.
  </Step>

  <Step title="Session Returned">
    SocketFi creates a new session and returns it to your application. The user is authenticated and their wallet is accessible.

    ```typescript theme={null}
    {
      userProfile: {
        id: "usr_01HXYZ",
        username: "alice"
      },
      socketfiAccessToken: "eyJhbGciOiJFUzI1NiJ9..."
    }
    ```

    Note that `socketfiAccessToken` is a **new** signed token even though the wallet address is unchanged. Previous tokens from earlier sessions are not refreshed — you always need the latest token from a successful `authenticate()` call.
  </Step>
</Steps>

***

## The Session Response

Sign-in returns the same `Session` structure as sign-up:

```typescript theme={null}
const session = await socketfi.authenticate();

// Attach the token to every backend request
const response = await fetch("/api/dashboard", {
  headers: {
    Authorization: `Bearer ${session.socketfiAccessToken}`,
  },
});
```

Your backend can extract the user ID and wallet address by verifying the token — see [Backend Verification](/authentication/backend-verification).

***

## Multi-Device Authentication

Users who have set up passkeys on multiple devices can authenticate from any of them. Each device holds its own copy of the passkey private key — authentication on one device does not affect the ability to authenticate from another.

```text theme={null}
iPhone (Face ID)       MacBook (Touch ID)      YubiKey
      ↓                       ↓                    ↓
Passkey A              Passkey B              Passkey C
      ↓                       ↓                    ↓
            Same Wallet — Same Wallet Address
```

<Note>
  The number of simultaneously registered credentials per user depends on your SocketFi application configuration. Review your platform settings if you need to adjust credential limits.
</Note>

***

## Session Expiration and Re-Authentication

Sessions are time-limited. Once a `socketfiAccessToken` expires, your backend will reject requests that include it. Design your application to handle this gracefully:

```typescript theme={null}
async function fetchWithAuth(url: string) {
  let token = getStoredToken();

  const response = await fetch(url, {
    headers: { Authorization: `Bearer ${token}` },
  });

  if (response.status === 401) {
    // Token has expired — prompt the user to re-authenticate
    const session = await socketfi.authenticate();
    token = session.socketfiAccessToken;
    storeToken(token);

    // Retry the original request with the new token
    return fetch(url, {
      headers: { Authorization: `Bearer ${token}` },
    });
  }

  return response;
}
```

Re-authentication creates a brand-new session token. The wallet address remains unchanged.

***

## Failure Scenarios

Handle these cases to provide a smooth experience for your users.

<CodeGroup>
  ```typescript User Cancelled theme={null}
  try {
    const session = await socketfi.authenticate();
  } catch (err) {
    if (err.code === "USER_CANCELLED") {
      // User dismissed the prompt — show a retry option, don't log an error
      showSignInButton();
    }
  }
  ```

  ```typescript Authentication Failed theme={null}
  try {
    const session = await socketfi.authenticate();
  } catch (err) {
    if (err.code === "AUTHENTICATION_FAILED") {
      // Credential verification failed — may indicate a device or credential issue
      showErrorMessage("Authentication failed. Try a different device or use account recovery.");
    }
  }
  ```

  ```typescript Expired Session (Backend) theme={null}
  // Caught on your backend, not the SDK
  // HTTP 401 response from your API signals an expired token
  if (response.status === 401) {
    const session = await socketfi.authenticate();
    retryWithNewToken(session.socketfiAccessToken);
  }
  ```
</CodeGroup>

<Warning>
  Never treat a failed `socketfi.authenticate()` call as a successful sign-in. Always wait for the Promise to resolve successfully and check that the returned `socketfiAccessToken` is present before granting access to protected features.
</Warning>

***

## What Doesn't Change During Sign-In

Sign-in is explicitly non-destructive. Nothing about the user's wallet or account changes:

| Item                   | Changed During Sign-In?                  |
| ---------------------- | ---------------------------------------- |
| Wallet address         | No                                       |
| Asset balances         | No                                       |
| Transaction history    | No                                       |
| Registered credentials | No                                       |
| Wallet ownership       | No                                       |
| Session token          | **Yes — a fresh token is always issued** |

The session token is the only thing that changes. Everything else persists from the last time the user was active.
