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

# Backend Verification: Validate SocketFi Tokens Server-Side

> Learn how to verify SocketFi access tokens server-side using @socketfi/server, extract user identity and wallet address, and protect your API endpoints.

Client-side authentication state is convenient, but it is never authoritative. A user's browser can claim any identity — the only way to know you are talking to who you think you are is to verify the session token on your server before granting access to any protected resource. SocketFi makes this straightforward with the `@socketfi/server` package, which validates the cryptographic signature on every access token and returns the verified user identity and wallet address.

<Warning>
  **Always verify tokens server-side.** Never trust a wallet address or user ID supplied directly by the client. Granting access based solely on client-provided identity claims is a critical security vulnerability. Every request to a protected endpoint must pass through `verifyAuth()` before your application logic runs.
</Warning>

***

## Installation

Install the SocketFi Server SDK in your backend project:

<CodeGroup>
  ```bash npm theme={null}
  npm install @socketfi/server
  ```

  ```bash yarn theme={null}
  yarn add @socketfi/server
  ```

  ```bash pnpm theme={null}
  pnpm add @socketfi/server
  ```
</CodeGroup>

The package is a pure Node.js library with no browser-side dependencies. It works in any Node.js server framework — Express, Fastify, Next.js API routes, Hono, and others.

***

## Verifying a Token

Import `verifyAuth` and pass it the raw JWT string extracted from the `Authorization: Bearer` header:

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

const token = req.headers.authorization?.replace("Bearer ", "");

const result = await verifyAuth(token);
```

`verifyAuth` is asynchronous. It validates the token's cryptographic signature, checks all standard JWT claims (expiration, issuer, audience), and returns a typed result object.

***

## Verification Results

`verifyAuth` always returns one of two shapes — a successful result or a failure result. Check `result.valid` before accessing any other fields.

### Successful Verification

```typescript theme={null}
if (result.valid) {
  console.log(result.user.id);         // "usr_01HXYZ"
  console.log(result.wallet.address);  // "CDXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}
```

```typescript theme={null}
// Full success result type
interface VerifyAuthSuccess {
  valid: true;
  user: {
    id: string;  // Stable user identifier — use as your foreign key
  };
  wallet: {
    address: string;  // The user's smart wallet address
  };
}
```

### Failed Verification

```typescript theme={null}
if (!result.valid) {
  console.error(result.error.code);  // e.g. "TOKEN_EXPIRED"
}
```

```typescript theme={null}
// Full failure result type
interface VerifyAuthFailure {
  valid: false;
  error: {
    code: string;  // See error codes table below
  };
}
```

***

## Error Codes

| Code                | Meaning                                                   | Recommended Response                                                 |
| ------------------- | --------------------------------------------------------- | -------------------------------------------------------------------- |
| `TOKEN_REQUIRED`    | No token was provided (empty or undefined input)          | Return HTTP `401 Unauthorized`                                       |
| `INVALID_SIGNATURE` | The token's signature does not match — possible tampering | Return HTTP `401 Unauthorized`; consider logging as a security event |
| `TOKEN_EXPIRED`     | The token's `exp` claim has passed                        | Return HTTP `401 Unauthorized`; client should re-authenticate        |
| `INVALID_ISSUER`    | The token was not issued by SocketFi                      | Return HTTP `401 Unauthorized`; reject and do not process further    |

***

## Express Middleware

The cleanest way to protect your Express routes is an authentication middleware that runs `verifyAuth` before your route handlers:

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

const app = express();

// Authentication middleware
async function requireAuth(
  req: express.Request,
  res: express.Response,
  next: express.NextFunction
) {
  const authHeader = req.headers.authorization;

  if (!authHeader?.startsWith("Bearer ")) {
    return res.status(401).json({ error: "TOKEN_REQUIRED" });
  }

  const token = authHeader.replace("Bearer ", "");
  const result = await verifyAuth(token);

  if (!result.valid) {
    return res.status(401).json({ error: result.error.code });
  }

  // Attach verified identity to the request for downstream handlers
  req.socketfi = {
    userId: result.user.id,
    walletAddress: result.wallet.address,
  };

  next();
}

// Apply to protected routes
app.get("/api/profile", requireAuth, (req, res) => {
  res.json({
    userId: req.socketfi.userId,
    walletAddress: req.socketfi.walletAddress,
  });
});

app.post("/api/transfer", requireAuth, async (req, res) => {
  const { walletAddress } = req.socketfi;
  // walletAddress is verified — safe to use as the authoritative sender identity
  // ...
});
```

<Note>
  Extend the `express.Request` type to include `socketfi` so TypeScript recognizes the property in downstream handlers:

  ```typescript theme={null}
  declare global {
    namespace Express {
      interface Request {
        socketfi: {
          userId: string;
          walletAddress: string;
        };
      }
    }
  }
  ```
</Note>

***

## Extracting User Identity and Wallet Address

Once verification succeeds, `result.user.id` and `result.wallet.address` are the authoritative identifiers for the requesting user. Use them to look up records, authorize operations, and attribute on-chain activity.

```typescript theme={null}
const result = await verifyAuth(token);

if (result.valid) {
  const { id: userId } = result.user;
  const { address: walletAddress } = result.wallet;

  // Use userId as the foreign key into your own database
  const userRecord = await db.users.findOne({ socketfiId: userId });

  // Use walletAddress as the canonical on-chain identity
  // Never accept wallet address from the client request body
  const balance = await getOnChainBalance(walletAddress);
}
```

<Tip>
  Store `result.user.id` as your foreign key in your own database rather than `result.wallet.address`. The user ID is stable across credential rotations. The wallet address is stable too, but querying by the SocketFi user ID is the more semantically correct way to link your records.
</Tip>

***

## Full Verification Example

Here is a complete, self-contained example that covers token extraction, verification, error handling, and identity usage:

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

export async function handleProtectedRequest(req: Request): Promise<Response> {
  // 1. Extract the token from the Authorization header
  const authHeader = req.headers.get("Authorization");
  if (!authHeader?.startsWith("Bearer ")) {
    return new Response(JSON.stringify({ error: "TOKEN_REQUIRED" }), {
      status: 401,
      headers: { "Content-Type": "application/json" },
    });
  }

  const token = authHeader.replace("Bearer ", "");

  // 2. Verify the token
  const result = await verifyAuth(token);

  if (!result.valid) {
    // 3a. Verification failed — return the error code to help the client recover
    return new Response(JSON.stringify({ error: result.error.code }), {
      status: 401,
      headers: { "Content-Type": "application/json" },
    });
  }

  // 3b. Verification succeeded — use the verified identity
  const { id: userId } = result.user;
  const { address: walletAddress } = result.wallet;

  // Your protected business logic goes here
  const data = await fetchUserData(userId, walletAddress);

  return new Response(JSON.stringify(data), {
    status: 200,
    headers: { "Content-Type": "application/json" },
  });
}
```

***

## Development and Testing Utilities

The `@socketfi/server` package exposes one additional utility that is useful during local development and testing.

### clearKeyCache()

`verifyAuth()` caches the public signing keys it fetches from SocketFi to avoid redundant network requests on every verification call. In production this cache is managed automatically and you never need to touch it.

During local development or in test suites — for example, when rotating keys in a staging environment or writing unit tests that stub the verification flow — you can flush the cache manually:

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

// Call before each test run to ensure fresh key material is fetched
clearKeyCache();
```

<Note>
  `clearKeyCache()` is a development and testing utility. Production applications should never need to call it — the SDK manages key caching automatically and refreshes keys when necessary.
</Note>

***

## Security Checklist

Before shipping to production, confirm that your backend satisfies all of these requirements:

<Steps>
  <Step title="Call verifyAuth on every protected endpoint">
    No exceptions. Every API route that returns user data, reads wallet state, or performs any authenticated action must verify the token before proceeding.
  </Step>

  <Step title="Return 401 for all verification failures">
    Do not leak information about why a token failed. Return `401 Unauthorized` for every `result.valid === false` case regardless of the error code. You can log the error code internally for debugging.
  </Step>

  <Step title="Use result.user.id as your identity source of truth">
    Never trust a user ID or wallet address submitted in a request body or query parameter. The only authoritative identity is what `verifyAuth` returns.
  </Step>

  <Step title="Log INVALID_SIGNATURE and INVALID_ISSUER errors">
    These codes indicate potential token forgery or a misconfigured client. Alert your security monitoring system when they appear.
  </Step>

  <Step title="Run your server over HTTPS">
    Tokens transmitted over plain HTTP can be intercepted. All production endpoints must use TLS.
  </Step>
</Steps>
