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

# Server-Side Token Verification Example with SocketFi

> A complete Express backend with SocketFi token verification middleware, TypeScript request augmentation, and multiple protected route patterns.

Client-side authentication proves to your frontend that a user is who they claim to be — but your backend needs its own proof before serving sensitive data or executing business logic. Every protected API call should include the `socketfiAccessToken` in an `Authorization: Bearer` header, and every protected route should call `verifyAuth()` before trusting any user-supplied data. This example gives you a production-ready Express server with centralized middleware, full TypeScript types, and several protected route patterns to copy from.

## Architecture

```text theme={null}
React app authenticates  →  receives { socketfiAccessToken, userProfile }
       ↓
Frontend attaches token: Authorization: Bearer <token>
       ↓
Express receives request
       ↓
authMiddleware calls verifyAuth(token)
       ↓
{ valid: true, user: { id }, wallet: "CDXXX…" } attached to req.user
       ↓
Route handler executes business logic with verified identity
```

## Installation

```bash theme={null}
npm install @socketfi/server express
npm install --save-dev @types/express typescript
```

## Project structure

```text theme={null}
src/
├── middleware/
│   └── auth.ts           # verifyAuth() middleware
├── routes/
│   ├── profile.ts        # Protected user profile route
│   └── wallet.ts         # Protected wallet info route
├── types/
│   └── express.d.ts      # Extend Express Request with req.user
├── app.ts                # Express app setup
└── server.ts             # HTTP server entry point
```

## Step-by-step files

<Steps>
  ### express.d.ts — Extend the Request type

  Add `user` to the Express `Request` interface once, globally. Every route handler and middleware then gets full type safety on `req.user` without casting.

  ```typescript src/types/express.d.ts theme={null}
  export {};

  declare global {
    namespace Express {
      interface Request {
        user?: {
          id: string;
          wallet: string;
        };
      }
    }
  }
  ```

  <Note>
    TypeScript merges this declaration with Express's built-in `Request` type. The `export {}` at the top is required to make this file a module (not a script), which enables the `declare global` block.
  </Note>

  ### auth.ts — Authentication middleware

  Extract the Bearer token, call `verifyAuth()`, and attach the verified identity to `req.user`. Any route that uses this middleware is guaranteed to have a valid, server-verified user object.

  ```typescript src/middleware/auth.ts theme={null}
  import type { Request, Response, NextFunction } from "express";
  import { verifyAuth } from "@socketfi/server";

  export async function authMiddleware(
    req: Request,
    res: Response,
    next: NextFunction
  ): Promise<void> {
    try {
      const authHeader = req.headers.authorization;

      if (!authHeader?.startsWith("Bearer ")) {
        res.status(401).json({ message: "Authorization header required." });
        return;
      }

      const token = authHeader.slice("Bearer ".length);
      const result = await verifyAuth(token);

      if (!result.valid) {
        res.status(401).json({ message: "Invalid token." });
        return;
      }

      req.user = {
        id: result.user.id,
        wallet: result.wallet,
      };

      next();
    } catch (err) {
      // Log the error (never log the raw token)
      console.error("Auth verification error:", (err as Error).message);
      res.status(401).json({ message: "Unauthorized." });
    }
  }
  ```

  <Warning>
    Never log the raw token value — it is a credential. Log the error message only.
  </Warning>

  ### routes/profile.ts — Protected profile route

  Return the verified user's ID and wallet address. Because `authMiddleware` has already called `verifyAuth()`, you can trust `req.user` completely.

  ```typescript src/routes/profile.ts theme={null}
  import { Router } from "express";
  import type { Request, Response } from "express";

  const router = Router();

  // GET /api/profile
  router.get("/profile", async (req: Request, res: Response) => {
    // req.user is guaranteed by authMiddleware
    res.json({
      id: req.user!.id,
      wallet: req.user!.wallet,
    });
  });

  export default router;
  ```

  ### routes/wallet.ts — Protected wallet route

  Demonstrates how to use `req.user.wallet` to scope a database query to only the authenticated user's data — the canonical ownership-validation pattern.

  ```typescript src/routes/wallet.ts theme={null}
  import { Router } from "express";
  import type { Request, Response } from "express";

  const router = Router();

  // GET /api/wallet
  router.get("/wallet", async (req: Request, res: Response) => {
    const wallet = req.user!.wallet;

    // Example: fetch user-owned positions from your database
    // const positions = await db.positions.findMany({ where: { wallet } });

    res.json({
      wallet,
      // positions,
    });
  });

  // GET /api/wallet/positions/:address — validate caller owns the address
  router.get("/wallet/positions/:address", async (req: Request, res: Response) => {
    const requestedAddress = req.params.address;
    const verifiedWallet = req.user!.wallet;

    if (requestedAddress !== verifiedWallet) {
      res.status(403).json({ message: "Forbidden." });
      return;
    }

    // const positions = await db.positions.findMany({ where: { wallet: verifiedWallet } });
    res.json({ wallet: verifiedWallet, positions: [] });
  });

  export default router;
  ```

  ### app.ts — Express app setup

  Apply `authMiddleware` globally to all `/api` routes. Public routes (health check, webhooks) sit outside the middleware scope.

  ```typescript src/app.ts theme={null}
  import express from "express";
  import { authMiddleware } from "./middleware/auth";
  import profileRoutes from "./routes/profile";
  import walletRoutes from "./routes/wallet";

  const app = express();

  app.use(express.json());

  // Public route — no auth required
  app.get("/health", (_req, res) => {
    res.json({ status: "ok" });
  });

  // All /api routes require a valid SocketFi token
  app.use("/api", authMiddleware, profileRoutes);
  app.use("/api", authMiddleware, walletRoutes);

  export default app;
  ```
</Steps>

## Starting the server

```typescript src/server.ts theme={null}
import app from "./app";

const PORT = process.env.PORT ?? 3000;

app.listen(PORT, () => {
  console.log(`Server listening on port ${PORT}`);
});
```

## Calling protected endpoints from the frontend

Attach the access token from the authenticated session to every request:

```typescript theme={null}
import { useAuth } from "../auth/useAuth";

function useApi() {
  const { session } = useAuth();

  const get = async (path: string) => {
    const res = await fetch(path, {
      headers: {
        Authorization: `Bearer ${session?.socketfiAccessToken}`,
        "Content-Type": "application/json",
      },
    });
    if (!res.ok) throw new Error(`API error ${res.status}`);
    return res.json();
  };

  return { get };
}
```

## Verification result shape

`verifyAuth()` returns:

```typescript theme={null}
interface VerifyResult {
  valid: boolean;
  user: { id: string };
  wallet: string;
}
```

Use `result.wallet` — not any wallet address sent in the request body — as the authoritative identity for all business logic.

## Ownership validation pattern

A common pattern: ensure the caller is acting on their own resources, not someone else's.

```typescript theme={null}
router.delete("/positions/:positionId", async (req, res) => {
  const position = await db.positions.findById(req.params.positionId);

  if (!position) {
    return res.status(404).json({ message: "Not found." });
  }

  // Only the wallet that owns this position may delete it
  if (position.wallet !== req.user!.wallet) {
    return res.status(403).json({ message: "Forbidden." });
  }

  await db.positions.delete(req.params.positionId);
  return res.json({ deleted: true });
});
```

## Applying middleware per-route instead of globally

If your app has a mix of public and private endpoints, apply `authMiddleware` at the route level:

```typescript theme={null}
import { authMiddleware } from "./middleware/auth";

// Public
app.get("/api/public-data", publicHandler);

// Private
app.get("/api/profile", authMiddleware, profileHandler);
app.get("/api/wallet", authMiddleware, walletHandler);
app.post("/api/transaction", authMiddleware, transactionHandler);
```

## Using with Fastify, NestJS, or Hono

`verifyAuth()` is framework-agnostic — the same function works wherever you can extract a Bearer token from a request header.

<CodeGroup>
  ```typescript Fastify theme={null}
  import Fastify from "fastify";
  import { verifyAuth } from "@socketfi/server";

  const app = Fastify();

  app.addHook("preHandler", async (request, reply) => {
    const token = request.headers.authorization?.replace("Bearer ", "");
    if (!token) return reply.status(401).send({ message: "Unauthorized." });

    const result = await verifyAuth(token);
    if (!result.valid) return reply.status(401).send({ message: "Unauthorized." });

    (request as any).user = { id: result.user.id, wallet: result.wallet };
  });
  ```

  ```typescript Hono theme={null}
  import { Hono } from "hono";
  import { verifyAuth } from "@socketfi/server";

  const app = new Hono();

  app.use("/api/*", async (c, next) => {
    const token = c.req.header("Authorization")?.replace("Bearer ", "");
    if (!token) return c.json({ message: "Unauthorized." }, 401);

    const result = await verifyAuth(token);
    if (!result.valid) return c.json({ message: "Unauthorized." }, 401);

    c.set("user", { id: result.user.id, wallet: result.wallet });
    await next();
  });
  ```
</CodeGroup>

## Clearing the key cache during testing

`@socketfi/server` caches public keys internally to reduce network round-trips on every `verifyAuth()` call. In automated tests and CI environments, you can call `clearKeyCache()` to flush the cache and force fresh key retrieval — useful when rotating keys between test runs.

```typescript theme={null}
import { clearKeyCache } from "@socketfi/server";

// Call this in a beforeEach / afterAll hook when testing token verification
clearKeyCache();
```

<Note>
  `clearKeyCache()` is intended for testing environments. You do not need to call it in production code — the cache refreshes automatically.
</Note>

## Security checklist

<CardGroup cols={2}>
  <Card title="Always call verifyAuth()" icon="shield-check">
    Every protected endpoint must verify the token server-side. Never trust wallet addresses or user IDs sent from the client.
  </Card>

  <Card title="Use HTTPS" icon="lock">
    Bearer tokens in transit must be protected by TLS. HTTPS is required in production.
  </Card>

  <Card title="Validate ownership" icon="user-check">
    After verifying identity, confirm the user owns the resource they're accessing before executing any write operation.
  </Card>

  <Card title="Never log tokens" icon="eye-slash">
    Access tokens are credentials. Log error messages, not token values.
  </Card>
</CardGroup>
