OAuth 2.0

Authorization code with PKCE, token lifetimes, refresh, revoke and redirect URI rules.

Draft

All clients use the authorization code flow with PKCE (S256), including confidential clients. Implicit and password grants are not supported.

Client types

  • confidential ("Web app with a backend"): your backend keeps the client secret and exchanges the code. It is the only app type for now.
  • Mobile, desktop and browser-only apps use the same type through a backend ("backend for frontend"): the app opens the system browser on the authorization URL, FleetBold redirects to your backend, and your backend keeps the secret and the tokens; the app talks to your backend. Embedded WebViews are not supported.

Authorization request

Send the user tohttp
GET https://partners-api.fleetbold.com/oauth/authorize
  ?response_type=code
  &client_id=fbci_…
  &redirect_uri=https%3A%2F%2Fapp.example.com%2Foauth%2Fcallback
  &scope=…
  &state=…
  &code_challenge=…
  &code_challenge_method=S256
  • Read the authorization URL from authorization_endpoint in https://partners-api.fleetbold.com/.well-known/oauth-authorization-server (RFC 8414) instead of hard-coding it.
  • redirect_uri must match a registered redirect URI exactly.
  • scope is space-delimited and must be a subset of the scopes your app can request. Scopes added to a live app after its review are pending: only you and your test users can grant them; for other users they are left out and the authorization continues with the rest (read the granted scopes from scope in the token response). See Changing a live app.
  • Scopes your app marks as required are always added to the request, even when scope leaves them out; an empty scope asks for just the required scopes (RFC 6749 §3.3). Editing scope can only drop optional scopes, never add one your app cannot request.
  • The app OAuth & permissions tab builds this URL for your app in the Authorization link card: a template with {state} and {code_challenge} (and {nonce} with openid) placeholders, plus Node.js and Python code that makes fresh values for every login. Never reuse a state or code_challenge.
  • state is required (up to 512 characters); code_challenge is 43–128 characters. Send every parameter once (a repeated one is refused) and keep the URL under about 8 KB.
  • An unknown client_id, an unregistered redirect_uri or a repeated client_id / redirect_uri shows a FleetBold error page and never redirects. Other refusals are shown on a FleetBold page whose Return button redirects to your redirect_uri with error, state, iss and usually error_description.

On FleetBold's page the user signs in with their FleetBold e-mail and password on every authorization (no single sign-on), then always sees the consent page, prefilled with what they shared before: your app, the scopes it asks for and which personal and organization vehicles to share. Scopes required by your app are ticked and locked: the user approves them or denies the connection; the other scopes can be unticked. The first time a user grants a high-risk scope the page asks for an extra confirmation. While your app is in development the page shows a "Development — not reviewed by FleetBold" warning. Approving redirects to your redirect_uri with code, state and iss (https://partners-api.fleetbold.com): check both state and iss. Deny redirects with error=access_denied (user_denied); Cancel on the sign-in page with error=access_denied and no description. Open the page in the system browser, never an embedded WebView, and never ask users for their FleetBold password yourself. To keep access without sending the user back, request offline_access and use refresh tokens.

Required scopes

  • In OAuth & permissions, switch on Required for each scope your app cannot work without. The change applies at once, without a review; on a live app only approved scopes can be required.
  • FleetBold adds the required scopes to every authorization request, so a link that leaves one out keeps working. The consent page marks them *Required by the app* and the user cannot untick them; a high-risk one still needs the extra confirmation.
  • Existing connections that lack a newly required scope keep their tokens: FleetBold asks for the scope at their next authorization. The Overview connections table flags them.
  • Removing a scope from your app also un-requires it. openid marked as required is ignored until FleetBold enables OpenID Connect (the Authorization link leaves it to FleetBold; an openid listed in scope is refused until then).

Tokens

Exchange the codebash
curl https://partners-api.fleetbold.com/oauth/token \
  -d grant_type=authorization_code \
  -d code=… \
  -d redirect_uri=… \
  -d code_verifier=… \
  -u "$CLIENT_ID:$CLIENT_SECRET"   # confidential clients only
  • Authorization codes live 60 seconds and work once.
  • Access tokens are opaque Bearer tokens that live 1 hour.
  • Refresh tokens (scope offline_access) last 90 days, sliding, and are rotated on every use. The previous refresh token keeps working for a short grace window; reusing an older one revokes the whole token family.
  • Revoke with POST /oauth/revoke. Revoking a refresh token disconnects the user.

Redirect URIs

  • Web app with a backend: https://app.example.com/oauth/callback on your backend (also for mobile and desktop apps, which reach it through the system browser).
  • Up to 10 redirect URIs per app. http://localhost redirects are allowed in development only and are rejected when you submit for review. After go-live, keep developing locally with a test app (see Changing a live app).

Disconnections

A user or FleetBold can disconnect your app. When a refresh answers 400 invalid_grant, the connection is gone: ask the user to connect again. 403 app_suspended is not a disconnection.