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

# Authentication

> Connect a wallet, derive keys, and log in to Privacy Boost

# Authentication

Authentication is a single `authenticate()` call that connects the user's wallet, derives their privacy keys, and obtains an access token from the server. After authentication, the user has a **privacy address** — a public identifier (separate from their Ethereum address) that others use to send them private transfers. See [Key Management](/sdk/concepts/key-management) for details on key derivation.

## Choosing an Auth Method

Before integrating authentication, choose how your app verifies users. See the [auth method comparison table](/sdk/concepts/app-setup#which-method-should-i-use) in App Setup.

For development, the SDK authenticates directly — no backend needed. For production, you'll route authentication through your backend using a [token provider](#token-providers).

## User Experience

From the user's perspective, authentication looks like this:

1. **Your app calls `authenticate()`**
2. **User sees 1-2 wallet popups** (depending on key source — see below)
3. **User is logged in** — SDK handles everything else behind the scenes

Once authenticated, the SDK automatically attaches credentials to all API requests. The user does not need to sign anything again until they log out or their session expires.

### Wallet Popups

The number of wallet signature requests depends on how keys are derived:

| Key Source      | What the user sees                                         | Popups |
| --------------- | ---------------------------------------------------------- | ------ |
| `walletDerived` | "Sign message" popup + "Sign typed data" popup             | 2      |
| `mnemonic`      | "Sign typed data" popup only (keys come from the mnemonic) | 1      |
| `rawSeed`       | "Sign typed data" popup only (keys come from the seed)     | 1      |

<Info>
  The first popup (when using `walletDerived`) asks the user to sign a message to derive their privacy keys. The second popup registers an authorization key on-chain. Both are one-time per session.
</Info>

### Returning Users

When a user returns to your app and their session is still saved (via persistence or manual session import), they can re-authenticate with **zero wallet popups**. The SDK uses the stored keys and only refreshes the server token.

## Basic Usage

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

  if (result.status === 'authenticated') {
    console.log(result.privacyAddress);
  }
  ```

  ```swift iOS theme={null}
  let wallet = MyWalletDelegate()
  let result = try await sdk.authenticate(wallet: wallet, keySource: .walletDerived())

  switch result {
  case .authenticated(let login):
      print(login.privacyAddress)
  case .credentialRequired(let challenge):
      // Handle PIN/password prompt (see below)
      break
  }
  ```

  ```kotlin Android theme={null}
  val wallet = MyWalletDelegate()
  val result = sdk.authenticate(wallet, keySource = KeySource.WalletDerived())

  when (result) {
      is AuthResult.Authenticated -> println(result.loginResult.privacyAddress)
      is AuthResult.CredentialRequired -> { /* Handle PIN/password */ }
  }
  ```

  ```bash CLI theme={null}
  export PRIVACY_BOOST_APP_ID=app_abc123xyz
  privacy-boost login
  ```
</CodeGroup>

## PIN / Password Unlock

If you've configured persistence with `pin` or `password` unlock, `authenticate()` may return `credentialRequired` instead of logging in immediately. This happens when:

* **First time** — The SDK needs a PIN/password to encrypt the key vault
* **Returning user** — The SDK needs the PIN/password to decrypt the stored keys

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

  if (result.status === 'credentialRequired') {
    // Show a PIN or password input to the user
    const pin = await showPinDialog(result.action); // 'setup' or 'unlock'
    const login = await result.submit(pin);
    console.log(login.privacyAddress);
  }
  ```

  ```swift iOS theme={null}
  let result = try await sdk.authenticate(wallet: wallet, keySource: .walletDerived())

  switch result {
  case .credentialRequired(let challenge):
      // challenge.action: .setup (first time) or .unlock (returning)
      // challenge.unlockType: .pin or .password
      let pin = showPinDialog(for: challenge)
      let login = try await sdk.submitCredential(pin)
  case .authenticated(_):
      break
  }
  ```
</CodeGroup>

<Info>
  If persistence uses `biometric` or `passkey` unlock, the platform prompt appears automatically — no extra code needed.
</Info>

## Token Providers

A token provider is a function you pass to `authenticate()` that routes the login request through your backend. Your backend adds credentials (API secret, Privy token, or custom JWT) before forwarding to Privacy Boost. This keeps secrets out of client-side code.

**When do you need one?** Only if your app uses API secret, Privy, or custom JWT authentication. For direct auth (development), no token provider is needed. See [App Setup](/sdk/concepts/app-setup) for details.

### How it works

<img className="block dark:hidden" src="https://mintcdn.com/sunnysidelabs/npTSPV0krdyxjvTn/images/sdk/token-provider-flow.svg?fit=max&auto=format&n=npTSPV0krdyxjvTn&q=85&s=0304c40b540db4e4b3d4e7aa6647b15b" alt="Token Provider Flow" width="720" height="320" data-path="images/sdk/token-provider-flow.svg" />

<img className="hidden dark:block" src="https://mintcdn.com/sunnysidelabs/npTSPV0krdyxjvTn/images/sdk/token-provider-flow-dark.svg?fit=max&auto=format&n=npTSPV0krdyxjvTn&q=85&s=69c5b0e10f64bc811d0203b6e4167e43" alt="Token Provider Flow" width="720" height="320" data-path="images/sdk/token-provider-flow-dark.svg" />

### Implementation

<CodeGroup>
  ```typescript TypeScript theme={null}
  const tokenProvider = async (payload) => {
    const res = await fetch('https://your-server.com/api/privacy-auth', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
    });
    return await res.json(); // { token, expiresIn }
  };

  await sdk.auth.authenticate(adapter, { keySource: { type: 'walletDerived' }, tokenProvider });
  ```

  ```swift iOS theme={null}
  class MyTokenProvider: TokenProvider {
      func getToken(loginPayloadJson: String) async throws -> TokenResponse {
          var request = URLRequest(url: URL(string: "https://your-server.com/api/privacy-auth")!)
          request.httpMethod = "POST"
          request.setValue("application/json", forHTTPHeaderField: "Content-Type")
          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
          )
      }
  }

  try await sdk.authenticate(wallet: wallet, tokenProvider: MyTokenProvider())
  ```

  ```kotlin Android theme={null}
  class MyTokenProvider : TokenProvider {
      override suspend fun getToken(loginPayloadJson: String): TokenResponse {
          val response = httpClient.post("https://your-server.com/api/privacy-auth") {
              contentType(ContentType.Application.Json)
              setBody(loginPayloadJson)
          }
          val json = Json.parseToJsonElement(response.bodyAsText()).jsonObject
          return TokenResponse(
              token = json["token"]!!.jsonPrimitive.content,
              expiresIn = json["expiresIn"]!!.jsonPrimitive.long.toULong()
          )
      }
  }

  sdk.authenticate(wallet, tokenProvider = MyTokenProvider())
  ```
</CodeGroup>

The token provider receives the SDK's login payload (a JSON object with all the cryptographic proofs) and must return `{ token: string, expiresIn: number }`.

<Warning>
  Never expose API secrets in client-side code. Use a custom token provider to route authentication through your backend.
</Warning>

## Logging Out

| Method           | What it does                                   | Next login                      |
| ---------------- | ---------------------------------------------- | ------------------------------- |
| `clearSession()` | Clears the auth token but keeps keys in memory | Fast — no wallet popups needed  |
| `logout()`       | Clears everything (keys, token, connection)    | Full re-auth with wallet popups |

Use `clearSession()` when you want to expire the token but let the user quickly re-login (e.g., switching accounts on the server side). Use `logout()` for a full sign-out.

<CodeGroup>
  ```typescript TypeScript theme={null}
  sdk.auth.clearSession();  // Quick re-login possible
  sdk.auth.logout();        // Full sign-out
  ```

  ```swift iOS theme={null}
  sdk.clearSession()
  sdk.logout()
  ```

  ```kotlin Android theme={null}
  sdk.clearSession()
  sdk.logout()
  ```
</CodeGroup>

## Next Steps

If you're using a production auth method, set up your integration next:

<CardGroup cols={2}>
  <Card title="API Secret" href="/sdk/guides/api-secret">
    Server-to-server authentication with client credentials
  </Card>

  <Card title="Custom JWT" href="/sdk/guides/custom-jwt">
    Auth0, Firebase, Supabase, Clerk, or any OIDC provider
  </Card>

  <Card title="Privy" href="/sdk/guides/privy">
    Social login and embedded wallets via Privy
  </Card>

  <Card title="Dynamic" href="/sdk/guides/dynamic">
    Wallet connection and embedded wallets via Dynamic
  </Card>
</CardGroup>

Then continue with setup:

* [Key Management](/sdk/concepts/key-management) — Choose a key source and configure persistence
* [Error Handling](/sdk/concepts/error-handling) — Handle auth and operation errors
* [Keys & Encryption](/technical-explainer/keys-and-encryption) — Deep dive into how auth keys, viewing keys, and privacy addresses work
