Flow diagram
The flow, step by step
1
Send the user to Fanvue to authorise
Your app redirects the user to
https://auth.fanvue.com/oauth2/auth. The URL carries your client_id, your redirect_uri, the scopes you need, a random state value and a PKCE code challenge. The state value protects the callback against cross-site request forgery.2
The user logs in and grants scopes
On Fanvue, the user signs in and approves the scopes your app asked for. Scopes the user’s account type cannot use are dropped from the grant. The user can revoke access later.
3
Fanvue redirects back with an authorisation code
Fanvue sends the user back to your redirect URI with a one-time authorisation code in the query string. The code is a voucher, not a credential. Check that the returned
state matches the one you sent.4
Exchange the code for tokens
Your backend calls
https://auth.fanvue.com/oauth2/token with your client_id and client_secret in an HTTP Basic Auth header (client_secret_basic). The body carries the authorisation code and the PKCE code verifier. Fanvue returns an access token and a refresh token. The implementation guide shows the exact request.5
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, use the refresh token to get a new one without sending the user through the flow again.A token in action
Every API call carries the access token and the version header:PKCE is required
PKCE (Proof Key for Code Exchange) is mandatory on every Fanvue OAuth flow. It stops an intercepted authorisation code from being exchanged for tokens.1
Generate a verifier and a challenge
Before redirecting the user, your app creates a random
code_verifier and a code_challenge, the SHA-256 hash of the verifier. You send the challenge in the authorise URL and keep the verifier server-side.2
Prove it on token exchange
When you exchange the code for tokens, you send the original
code_verifier. Fanvue hashes it and compares the result with the challenge. No match, no tokens.How tokens work
The token exchange returns two tokens with different jobs:- Access token: a short-lived Bearer credential 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. 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
Each refresh token is accepted once. Persist the new refresh token from every refresh response and discard the old one. A short grace window after rotation absorbs a retry or a race, and each call accepted inside it returns a new token pair. Reuse detection protects creators. If a refresh token is stolen, the first stale use after the grace window breaks the chain, so a stolen token can’t keep a session alive past the current access token’s expiry. Rules for your implementation:- Overwrite your stored refresh token after every refresh. Treat the old value as dead.
- Serialise refreshes with a lock or a shared promise so two requests never present the same refresh token. The grace window is a safety net, not a substitute.
- If a refresh response is lost or times out, re-present the same refresh token straight away. Waiting past the grace window and retrying then breaks the chain.
- Never run several workers off one refresh token without coordinating which one refreshes.
- Treat
invalid_granton a refresh as a broken chain. Don’t retry the refresh; redirect the user to authorise again.