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

# Custom JWT Integration

> Authenticate Privacy Boost users with your own auth system using custom JWTs

# Custom JWT Integration

If you already have an authentication system that issues JWTs (Auth0, Firebase Auth, Supabase Auth, Clerk, or a custom backend), you can use the `custom_jwt` auth method to connect it to Privacy Boost. Your backend validates the user's identity with your auth provider and forwards a JWT to Privacy Boost, which verifies it via your JWKS endpoint.

## How It Works

<img className="block dark:hidden" src="https://mintcdn.com/sunnysidelabs/npTSPV0krdyxjvTn/images/sdk/custom-jwt-flow.svg?fit=max&auto=format&n=npTSPV0krdyxjvTn&q=85&s=6b585143e296ed216a2bf8832f8fd0f6" alt="Custom JWT Authentication Flow" width="800" height="400" data-path="images/sdk/custom-jwt-flow.svg" />

<img className="hidden dark:block" src="https://mintcdn.com/sunnysidelabs/npTSPV0krdyxjvTn/images/sdk/custom-jwt-flow-dark.svg?fit=max&auto=format&n=npTSPV0krdyxjvTn&q=85&s=e007055f679e6c8e6891cacc97747435" alt="Custom JWT Authentication Flow" width="800" height="400" data-path="images/sdk/custom-jwt-flow-dark.svg" />

## Server-Side Setup

Your app must be configured with the `custom_jwt` auth method. Contact the Privacy Boost team with:

* **JWKS URL** — The HTTPS endpoint serving your JSON Web Key Set (e.g., `https://your-auth.com/.well-known/jwks.json`). Privacy Boost fetches this to verify JWT signatures.
* **Audience** — Required expected `aud` claim in your JWTs. Only tokens with a matching audience are accepted.
* **Issuer** — Required expected `iss` claim in your JWTs. Only tokens with a matching issuer are accepted.

The backend configuration looks like:

```json theme={null}
{
  "jwks_url": "https://your-auth.com/.well-known/jwks.json",
  "audience": "your-app-id",
  "issuer": "https://your-auth.com"
}
```

### JWT Requirements

Your JWT must:

* Be signed with RS256, RS384, RS512, ES256, ES384, or ES512
* Include a `kid` (Key ID) header matching a key in your JWKS
* Include a `sub` or `user_id` claim identifying the user
* Include an `exp` claim and be valid (not expired)
* Match the configured `aud` and `iss`

## Client-Side Integration

### 1. Implement a Token Provider

Your token provider routes the SDK's login payload through your backend, which attaches the custom JWT:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const tokenProvider = async (loginPayload: any) => {
    // Get your auth token (from your auth system)
    const yourAuthToken = await getYourAuthToken();

    // Send both to your backend
    const res = await fetch('https://your-backend.com/api/privacy-boost/auth', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${yourAuthToken}`,
      },
      body: JSON.stringify(loginPayload),
    });

    return await res.json(); // { token, expiresIn }
  };
  ```

  ```swift iOS theme={null}
  class CustomJWTTokenProvider: TokenProvider {
      func getToken(loginPayloadJson: String) async throws -> TokenResponse {
          let yourAuthToken = try await getYourAuthToken()

          var request = URLRequest(url: URL(string: "https://your-backend.com/api/privacy-boost/auth")!)
          request.httpMethod = "POST"
          request.setValue("application/json", forHTTPHeaderField: "Content-Type")
          request.setValue("Bearer \(yourAuthToken)", forHTTPHeaderField: "Authorization")
          request.httpBody = loginPayloadJson.data(using: .utf8)

          let (data, _) = try await URLSession.shared.data(for: request)
          let json = try JSONSerialization.jsonObject(with: data) as! [String: Any]
          return TokenResponse(
              token: json["token"] as! String,
              expiresIn: json["expiresIn"] as! UInt64
          )
      }
  }
  ```

  ```kotlin Android theme={null}
  class CustomJWTTokenProvider : TokenProvider {
      override suspend fun getToken(loginPayloadJson: String): TokenResponse {
          val yourAuthToken = getYourAuthToken()

          val response = httpClient.post("https://your-backend.com/api/privacy-boost/auth") {
              contentType(ContentType.Application.Json)
              header("Authorization", "Bearer $yourAuthToken")
              setBody(loginPayloadJson)
          }
          val json = Json.parseToJsonElement(response.bodyAsText()).jsonObject
          return TokenResponse(
              token = json["token"]!!.jsonPrimitive.content,
              expiresIn = json["expiresIn"]!!.jsonPrimitive.long.toULong()
          )
      }
  }
  ```
</CodeGroup>

### 2. Pass the Token Provider to authenticate()

<CodeGroup>
  ```typescript TypeScript theme={null}
  await sdk.auth.authenticate(adapter, {
    type: 'walletDerived',
    tokenProvider,
  });
  ```

  ```swift iOS theme={null}
  try await sdk.authenticate(
      wallet: walletDelegate,
      tokenProvider: CustomJWTTokenProvider()
  )
  ```

  ```kotlin Android theme={null}
  sdk.authenticate(
      wallet = walletDelegate,
      tokenProvider = CustomJWTTokenProvider()
  )
  ```
</CodeGroup>

### 3. Implement the Backend Endpoint

Your backend endpoint receives the SDK's login payload, attaches your JWT, and forwards to Privacy Boost:

```typescript theme={null}
// Express.js example
app.post('/api/privacy-boost/auth', async (req, res) => {
  // 1. Verify your own auth (e.g., check the Authorization header)
  const user = await verifyYourAuth(req.headers.authorization);
  if (!user) return res.status(401).json({ error: 'Unauthorized' });

  // 2. Get a JWT for this user from your auth system
  const customJWT = await issueJWTForUser(user.id);

  // 3. Forward to Privacy Boost with the custom JWT attached
  const pbResponse = await fetch('https://optimism.sepolia.privacyboost.io/auth/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      ...req.body,            // SDK's login payload (mpk, viewauth_signature, nonce, etc.)
      custom_jwt_token: customJWT,  // Your JWT
    }),
  });

  const data = await pbResponse.json();
  res.json({
    token: data.access_token,
    expiresIn: data.expires_in,
  });
});
```

## Testing

For development, you can use `app_id_only` to skip the token provider entirely. Switch to `custom_jwt` when moving to production.

## Next Steps

Continue with setup:

* [Key Management](/sdk/concepts/key-management) — Configure key persistence for returning users
* [Error Handling](/sdk/concepts/error-handling) — Handle auth and operation errors

Or explore other auth methods:

* [Privy](/sdk/guides/privy) — For Privy social login and embedded wallets
* [Dynamic](/sdk/guides/dynamic) — For Dynamic wallet connection and embedded wallets
* [API Secret](/sdk/guides/api-secret) — For server-to-server with client credentials
