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

# SocketFi React SDK — Passkey Auth Integration Guide

> Authenticate users with passkeys via a hosted popup, manage sessions with React Context, and protect routes — all with a single authenticate() call.

Authentication in SocketFi is handled through a single method: `authenticate()`. When a user clicks your login button, the SDK opens a hosted popup where they complete passkey sign-in or registration. Once the popup closes, you receive a session object you can store and use throughout your app.

## How authentication works

Calling `authenticate()` triggers the following sequence entirely inside the hosted SocketFi popup:

<Steps>
  <Step title="Popup opens">
    The SDK opens the SocketFi hosted authentication screen in a popup window.
  </Step>

  <Step title="User authenticates with a passkey">
    New users register a passkey on their device. Returning users verify their existing passkey.
  </Step>

  <Step title="Wallet resolved or created">
    For returning users, the associated smart wallet is resolved automatically. For new users, a new embedded smart wallet is created and activated.
  </Step>

  <Step title="Session returned">
    The popup closes and `authenticate()` resolves with a `Session` object containing the user's profile and an access token.
  </Step>
</Steps>

The SDK handles sign-in and sign-up through the same `authenticate()` call — you don't need separate flows for new versus returning users.

## The Session object

A successful call to `authenticate()` returns a session with the following shape:

```typescript theme={null}
type Session = {
  userProfile: {
    id: string;
  };
  socketfiAccessToken: string;
};
```

Store `userProfile` to identify the user in your UI and keep `socketfiAccessToken` for any subsequent SDK calls that require an authenticated context.

## Sign-in vs sign-up flow

Both flows begin and end identically from your application's perspective — you call `authenticate()` and receive a session. The difference is invisible to your code.

<Tabs>
  <Tab title="Returning user (sign-in)">
    ```text theme={null}
    authenticate()
         ↓
    Hosted popup opens
         ↓
    User verifies existing passkey
         ↓
    Smart wallet resolved
         ↓
    Session returned
    ```
  </Tab>

  <Tab title="New user (sign-up)">
    ```text theme={null}
    authenticate()
         ↓
    Hosted popup opens
         ↓
    User registers a new passkey
         ↓
    Smart wallet created & activated
         ↓
    Session returned
    ```
  </Tab>
</Tabs>

<Note>
  Always call `authenticate()` from a direct user action such as a button click. Browsers will block the popup if it's triggered programmatically on page load or inside a `useEffect`.
</Note>

## Session management with React Context

For most applications, the best place to store the SocketFi session is a React Context that wraps your component tree. The pattern below gives any component access to the current user and a `logout` function through a `useSocketFiAuth` hook.

```typescript lib/AuthProvider.tsx theme={null}
import {
  createContext,
  useContext,
  useState,
  useEffect,
  type ReactNode,
} from "react";

type UserProfile = {
  id: string;
};

type AuthContextValue = {
  user: UserProfile | null;
  accessToken: string | null;
  isAuthenticated: boolean;
  setSession: (session: { userProfile: UserProfile; socketfiAccessToken: string }) => void;
  logout: () => void;
};

const AuthContext = createContext<AuthContextValue | null>(null);

const TOKEN_KEY = "socketfi_token";

export function AuthProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<UserProfile | null>(null);
  const [accessToken, setAccessToken] = useState<string | null>(null);

  // Restore session from localStorage on mount
  useEffect(() => {
    const stored = localStorage.getItem(TOKEN_KEY);
    if (stored) {
      setAccessToken(stored);
    }
  }, []);

  function setSession(session: {
    userProfile: UserProfile;
    socketfiAccessToken: string;
  }) {
    setUser(session.userProfile);
    setAccessToken(session.socketfiAccessToken);
    localStorage.setItem(TOKEN_KEY, session.socketfiAccessToken);
  }

  function logout() {
    setUser(null);
    setAccessToken(null);
    localStorage.removeItem(TOKEN_KEY);
  }

  return (
    <AuthContext.Provider
      value={{
        user,
        accessToken,
        isAuthenticated: Boolean(accessToken),
        setSession,
        logout,
      }}
    >
      {children}
    </AuthContext.Provider>
  );
}

export function useSocketFiAuth(): AuthContextValue {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error("useSocketFiAuth must be used within an AuthProvider");
  }
  return context;
}
```

Wrap your application with `AuthProvider` at the root:

```typescript main.tsx theme={null}
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { AuthProvider } from "./lib/AuthProvider";
import App from "./App";

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <AuthProvider>
      <App />
    </AuthProvider>
  </StrictMode>
);
```

Then use the hook in any component:

```typescript components/LoginButton.tsx theme={null}
import { socketfi } from "../lib/socketfi";
import { useSocketFiAuth } from "../lib/AuthProvider";

export function LoginButton() {
  const { setSession } = useSocketFiAuth();

  async function handleLogin() {
    try {
      const session = await socketfi.authenticate();
      setSession(session);
    } catch (error) {
      console.error("Authentication failed:", error);
    }
  }

  return <button onClick={handleLogin}>Continue with SocketFi</button>;
}
```

## Protecting routes

Use `isAuthenticated` from the context to gate access to parts of your application:

```typescript components/ProtectedRoute.tsx theme={null}
import { type ReactNode } from "react";
import { useSocketFiAuth } from "../lib/AuthProvider";
import { LoginButton } from "./LoginButton";

export function ProtectedRoute({ children }: { children: ReactNode }) {
  const { isAuthenticated } = useSocketFiAuth();

  if (!isAuthenticated) {
    return (
      <div>
        <p>Please sign in to continue.</p>
        <LoginButton />
      </div>
    );
  }

  return <>{children}</>;
}
```

## Logout

Call `logout()` from the context to clear the session from both React state and `localStorage`:

```typescript components/UserMenu.tsx theme={null}
import { useSocketFiAuth } from "../lib/AuthProvider";

export function UserMenu() {
  const { user, logout } = useSocketFiAuth();

  return (
    <div>
      <span>Signed in as {user?.id}</span>
      <button onClick={logout}>Sign out</button>
    </div>
  );
}
```

## Error handling

Wrap every `authenticate()` call in a `try/catch` block and handle the error codes your users are most likely to encounter:

```typescript theme={null}
import { socketfi } from "../lib/socketfi";
import { useSocketFiAuth } from "../lib/AuthProvider";
import { useState } from "react";

export function LoginButton() {
  const { setSession } = useSocketFiAuth();
  const [errorMessage, setErrorMessage] = useState<string | null>(null);

  async function handleLogin() {
    setErrorMessage(null);
    try {
      const session = await socketfi.authenticate();
      setSession(session);
    } catch (error: any) {
      switch (error?.code) {
        case "POPUP_BLOCKED":
          setErrorMessage(
            "The sign-in popup was blocked. Please allow popups for this site and try again."
          );
          break;
        case "USER_CANCELLED":
          // User closed the popup deliberately — no need to show an error
          break;
        case "INVALID_CLIENT_ID":
          setErrorMessage(
            "Application configuration error. Please contact support."
          );
          break;
        default:
          setErrorMessage("Authentication failed. Please try again.");
          console.error("Unexpected auth error:", error);
      }
    }
  }

  return (
    <>
      {errorMessage && <p role="alert">{errorMessage}</p>}
      <button onClick={handleLogin}>Continue with SocketFi</button>
    </>
  );
}
```

### Error codes reference

| Code                    | Cause                                                                                    | Recommended action                                                             |
| ----------------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| `POPUP_BLOCKED`         | The browser blocked the popup because it wasn't triggered directly by a user interaction | Display a message asking the user to allow popups, then let them retry         |
| `USER_CANCELLED`        | The user closed the popup before completing authentication                               | Treat as a no-op; don't show an error                                          |
| `INVALID_CLIENT_ID`     | The `clientId` is incorrect, inactive, or doesn't match the selected network             | Verify the value in your `.env` file matches your Developer Portal application |
| `AUTHENTICATION_FAILED` | The passkey verification flow could not be completed                                     | Ask the user to retry on a supported device with passkey capabilities          |
| `TOKEN_EXPIRED`         | The SocketFi session token has expired                                                   | Call `authenticate()` again to obtain a fresh session                          |
