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

# Authentication Overview

> How the Fanvue API uses OAuth 2.0 so your app can act on a creator's behalf, no API keys required.

The Fanvue API uses **OAuth 2.0**, the standard protocol that lets a user grant your app access to their account without ever handing over their password. The user authorizes your app, Fanvue gives you an **access token** (a short-lived credential called a Bearer token), and you call the API on their behalf by sending that token on every request:

```http theme={null}
Authorization: Bearer <access_token>
```

There are no API keys to copy, paste, or leak. Access is always tied to a specific user and the scopes they approved.

## The flow at a glance

```mermaid theme={null}
sequenceDiagram
    participant User
    participant App as Your App
    participant Fanvue as Fanvue Authorize (auth.fanvue.com)
    participant API as Fanvue API (api.fanvue.com)

    User->>App: 1. Clicks "Connect Fanvue"
    App->>Fanvue: 2. Redirects to authorize URL (with PKCE challenge)
    User->>Fanvue: 3. Logs in and grants the requested scopes
    Fanvue->>App: 4. Redirects back to your redirect URI with an authorization code
    App->>Fanvue: 5. Exchanges the code for tokens (with PKCE verifier)
    Fanvue->>App: 6. Returns access token + refresh token
    App->>API: 7. Calls the API with Authorization: Bearer <token>
    API->>App: 8. Returns the requested data
```

## The flow, step by step

<Steps>
  <Step title="Send the user to Fanvue to authorize">
    Your app redirects the user to the Fanvue **authorize URL** at `https://auth.fanvue.com/oauth2/auth`. The URL carries your `client_id`, the **redirect URI** (the page on your app Fanvue sends the user back to), the **scopes** you need, a random `state` value (to prevent cross-site request forgery), and a PKCE code challenge.
  </Step>

  <Step title="The user logs in and grants scopes">
    On Fanvue, the user signs in and approves the exact permissions your app asked for. They stay in control: they only grant the scopes you requested, and they can revoke access later.
  </Step>

  <Step title="Fanvue redirects back with an authorization code">
    Fanvue sends the user back to your redirect URI with a short-lived **authorization code** in the query string. This code is a one-time voucher, not a usable credential on its own. Verify the returned `state` matches the one you sent.
  </Step>

  <Step title="Exchange the code for tokens">
    Your backend makes a server-to-server call to `https://auth.fanvue.com/oauth2/token`, authenticating with your `client_id` and `client_secret` in an HTTP Basic Auth header (`client_secret_basic`), and sending the authorization code and PKCE code verifier in the body. Fanvue returns an **access token** and a **refresh token**. See the [Implementation Guide](/docs/authentication/implementation-guide#token-exchange) for the exact request.
  </Step>

  <Step title="Call the API on the user's behalf">
    Send the access token on every request to `https://api.fanvue.com` in the `Authorization: Bearer <token>` header. When the access token expires (typically after an hour), use the refresh token to get a new one without sending the user through the flow again.
  </Step>
</Steps>

## How tokens work

The token exchange (step 4) returns two tokens that do different jobs:

* **Access token** — a short-lived Bearer credential (typically valid for **1 hour**) that you send on every API request. It carries the scopes the user approved. When it expires, the API responds with `401 Unauthorized`.
* **Refresh token** — a longer-lived credential you keep server-side and use to obtain a new access token when the current one expires, without sending the user back through the authorize flow. Request the `offline_access` scope to receive one.

To refresh, your backend calls the token endpoint with `grant_type=refresh_token`. The response returns a new access token **and a new refresh token**.

### Refresh tokens rotate and are single-use

<Warning>
  **Each refresh token can be used exactly once.** Every refresh returns a new refresh token, and the previous one stops working straight away. Always persist the new refresh token from the response and discard the old one.

  **A 30-second grace period covers retries and races.** If you present the same refresh token again **within 30 seconds** of it being rotated, Fanvue replays the original response — you get back the exact same access token and refresh token the first call returned, and the chain is not broken. This makes refreshes idempotent within the window, so a network retry or two requests firing at once won't lock you out.

  **Reusing a refresh token after the grace period breaks the refresh chain.** If a token that has already been used is presented again **once the 30-second window has passed** (or a token that is otherwise invalid is presented), Fanvue treats it as a sign the token may be compromised, **invalidates the entire refresh token chain** (every refresh token issued from that authorization) and the refresh fails. Access tokens are short-lived JWTs that aren't revoked — any access token you already hold keeps working until it expires — but you can no longer mint new ones, so once the current access token lapses you're locked out. The only way to recover is to send the user through the **full OAuth authorization flow again** from step 1.
</Warning>

This is standard OAuth refresh token rotation with reuse detection. It protects creators: if a refresh token is ever stolen, the moment either the attacker or your app uses the stale copy **after the grace period**, the refresh chain is invalidated — no new access tokens can be minted, so the session can't be kept alive past the current access token's expiry.

**What this means for your implementation:**

* After every refresh, **overwrite your stored refresh token** with the new one from the response. Treat the old value as dead.
* **Still avoid refreshing the same token twice.** Serialise concurrent refreshes (use a lock or a shared promise) so two requests can't both fire with the same refresh token. The 30-second grace period now absorbs accidental double-use and retries — a duplicate call inside the window replays the same tokens instead of breaking the chain — but treat that as a safety net, not a substitute for serialising: a double-use that lands after the window still breaks the chain.
* **Never run multiple workers off one shared refresh token** without coordinating who refreshes.
* If a refresh fails with `invalid_grant`, assume the refresh chain is broken and **redirect the user to re-authorize**. Do not retry the refresh.

See the [Implementation Guide](/docs/authentication/implementation-guide#token-refresh) for the exact refresh request, response shape, and a client that handles rotation, locking, and retry safely.

## PKCE is required

**PKCE (Proof Key for Code Exchange) is mandatory on every Fanvue OAuth flow.** It closes a gap in the authorization code flow: if someone intercepts your authorization code, they still cannot exchange it for tokens.

<Steps>
  <Step title="Generate a verifier and a challenge">
    Before redirecting the user, your app creates a random `code_verifier` and a `code_challenge`, which is the SHA-256 hash of the verifier. You send the challenge in the authorize URL (step 1) and keep the verifier server-side.
  </Step>

  <Step title="Prove it on token exchange">
    When you exchange the code for tokens (step 4), you send the original `code_verifier`. Fanvue hashes it and confirms it matches the challenge from step 1. No match, no tokens.
  </Step>
</Steps>

<Warning>
  Store the `code_verifier` server-side (in a session, Redis, or an `HttpOnly` cookie) between the redirect and the token exchange. Never put it in local storage, a URL, or client-side JavaScript.
</Warning>

## A token in action

Once you hold an access token, every API call looks like this:

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.fanvue.com/users/me \
    -H "Authorization: Bearer eyJhbGc..."
  ```

  ```javascript Node.js theme={null}
  const res = await fetch("https://api.fanvue.com/users/me", {
    headers: { Authorization: `Bearer ${accessToken}` },
  });
  const user = await res.json();
  ```

  ```python Python theme={null}
  import requests

  res = requests.get(
      "https://api.fanvue.com/users/me",
      headers={"Authorization": f"Bearer {access_token}"},
  )
  user = res.json()
  ```
</CodeGroup>

<Note>
  The full request and response shapes for the authorize and token endpoints, including refresh and error handling, live in the [Implementation Guide](/docs/authentication/implementation-guide).
</Note>

## Where to go next

<CardGroup cols={3}>
  <Card title="Quick Start" icon="rocket" href="/docs/introduction/quick-start">
    Create an OAuth app and run the full flow end to end with a working Next.js example.
  </Card>

  <Card title="Implementation Guide" icon="code" href="/docs/authentication/implementation-guide">
    Every endpoint, parameter, and code sample: PKCE, token exchange, refresh, and error handling.
  </Card>

  <Card title="Scopes" icon="key" href="/docs/authentication/scopes">
    The full list of permissions your app can request and what each one unlocks.
  </Card>
</CardGroup>
