The flow at a glance
The flow, step by step
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.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.
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.Exchange the code for tokens
Your backend makes a server-to-server call to
https://auth.fanvue.com/oauth2/token, sending the authorization code, your client_id, your client_secret, and the PKCE code verifier. Fanvue returns an access token and a refresh token.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_accessscope to receive one.
grant_type=refresh_token. The response returns a new access token and a new refresh token.
Refresh tokens rotate and are single-use
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.
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.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.A token in action
Once you hold an access token, every API call looks like this:The full request and response shapes for the authorize and token endpoints, including refresh and error handling, live in the Implementation Guide.
Where to go next
Quick Start
Create an OAuth app and run the full flow end to end with a working Next.js example.
Implementation Guide
Every endpoint, parameter, and code sample: PKCE, token exchange, refresh, and error handling.
Scopes
The full list of permissions your app can request and what each one unlocks.