OAuth apps
Register an OAuth app for your organisation, sign members in with the authorization code flow and PKCE, call the API as the organisation with client credentials, and handle scopes, consent, token lifetimes, refresh rotation and secret rotation.
FurniOps sign-in is an OAuth 2.1 and OpenID Connect server at https://auth.furniops.com. Your
organisation can register its own OAuth apps on it:
- an internal tool or a partner's integration that your members sign in to, acting within each member's role;
- a server that calls the API as your organisation, with a client secret it can rotate instead of an API key.
Kinds
An app's kind decides how it authenticates and which flow it uses. It never changes.
| Kind | For | Client secret | Flow |
|---|---|---|---|
| Web app | A server-side app that signs users in | Yes | Authorization code with PKCE |
| Single-page app | Runs in the browser and signs users in | No (public) | Authorization code with PKCE |
| Native app | A desktop or mobile app that signs users in | No (public) | Authorization code with PKCE |
| Machine | Your server calls the API as the organisation; no user | Yes | Client credentials |
PKCE (S256) is required for every kind that signs users in, with or without a secret. There is
no implicit grant and no password grant.
Register an app
Under Developers › OAuth apps, choose Create an OAuth app. Owners, admins and developers
can manage apps (oauth_apps:write); like API keys, every change needs a sign-in within the last
10 minutes.
- Name and kind.
- Scopes (fixed for life; make a new app to change them):
- About the user (the kinds that sign users in):
openid,profile,emailandoffline_access. - API permissions: what the app may do for the signed-in user, within what that user may do; for a machine app, what your server may do as the organisation (at least one). The choice is limited to the permissions an API key can hold that your own role holds.
- About the user (the kinds that sign users in):
- Redirect URIs (the kinds that sign users in): matched exactly. https, or http on a loopback
host for local development; a native app may also use a private-use scheme in reverse-domain
form (
com.example.app:/callback). - Sign-out redirect URIs (optional), and the links users see on the consent screen: privacy
policy, terms of service, homepage and logo. An app that signs users in with
openidmust link its privacy policy.
The client ID and, for web and machine apps, a client secret are shown. The secret is shown once. An organisation can hold up to 25 apps.
Afterwards you can edit an app's name, redirect URIs and links, rotate or revoke its secrets, and delete it. Deleting an app stops its client ID and secrets at once and ends every user's sign-in and consent for it.
Sign users in: authorization code with PKCE
Discovery documents are at https://auth.furniops.com/.well-known/openid-configuration and
/.well-known/oauth-authorization-server. The endpoints:
| Endpoint | Path |
|---|---|
| Authorization | /oauth/authorize |
| Token | /oauth/token |
| Pushed authorization requests | /oauth/par |
| Revocation | /oauth/revoke |
| Introspection | /oauth/introspect |
| User info | /oauth/userinfo |
| Keys | /oauth/jwks |
| Sign-out | /oauth/end-session |
Send the user to the authorization endpoint with a PKCE challenge, a state, and the API as the
resource the token is for:
https://auth.furniops.com/oauth/authorize
?response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
&scope=openid%20email%20products%3Aread%20events%3Aread
&resource=https%3A%2F%2Fapi.furniops.com
&code_challenge=BASE64URL_SHA256_OF_VERIFIER
&code_challenge_method=S256
&state=RANDOM_STATEThe user signs in to FurniOps (with their password, passkey, second factor or their
organisation's single sign-on) and is asked to consent. Your app is redirected back with a code,
valid for one minute, which you exchange at the token endpoint:
curl https://auth.furniops.com/oauth/token \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-d grant_type=authorization_code \
-d code="$CODE" \
-d redirect_uri=https://app.example.com/callback \
-d code_verifier="$VERIFIER"A public app (single-page, native) sends client_id in the body instead of authenticating. The
answer carries an access_token for the API and, with openid, an id_token. Call the API with
the access token exactly as with a key:
curl https://api.furniops.com/v1/products \
-H "Authorization: Bearer $ACCESS_TOKEN"Treat the access token as an opaque string.
Consent
An organisation's own app is never trusted by default: every user is asked to consent, on a screen that shows the app's name, logo, links and each permission in plain words. A user can remove an app's access later in their account's Connected apps, which ends its tokens in every organisation at once. A password reset also ends every app's access.
The app acts only in your organisation, and its tokens hold the scopes the user consented to intersected with that user's role, read again at every token: a member whose role changes, or who is removed, gets the new answer within seconds.
Machine apps: client credentials
A machine app gets a token for the organisation with no user:
curl https://auth.furniops.com/oauth/token \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-d grant_type=client_credentials \
-d resource=https://api.furniops.com \
-d scope="products:read events:read"Name the API as the resource (or ask for API scopes): a client-credentials request that names no
resource is refused. The token holds the app's scopes and lasts 10 minutes; ask for a new one when
it expires. There is no refresh token.
Token lifetimes
| Artifact | Lifetime |
|---|---|
| Access token (users and machine apps) | 10 minutes |
| ID token | 1 hour |
| Authorization code | 1 minute, used once |
| Sign-in and consent screens | 10 minutes |
| Refresh token | 7 days idle, at most 30 days from the first one of its family |
Refresh tokens
Apps that sign users in get a refresh token when the app was registered with offline_access and
the authorization request asks for it with prompt=consent (OpenID Connect keeps offline_access
only when consent is asked for). Such a token outlives the user signing out of FurniOps, until it
expires, the user removes the app, or their password is reset.
- Every refresh rotates. The answer carries a new refresh token; the old one is spent.
- Reuse revokes the family. Presenting a spent refresh token ends every token descended from the same sign-in. There is no grace window, so refresh from one place at a time: serialise refreshes in your app rather than racing them.
- Each rotation gives 7 more days, never past 30 days from the first token. Then the user signs in again.
- A refresh re-reads the user's organisation and role: a member who was removed cannot refresh.
Rotate a client secret
Rotate secret issues a new secret and keeps the others working for an overlap you choose, 0 to 720 hours (24 by default; 0 ends them now). Deploy the new secret within the overlap. A single secret can also be revoked on its own. Like the first one, a new secret is shown once.
Revoking and introspecting tokens
The revocation and introspection endpoints take the app's own tokens only: an app can revoke or inspect what it was issued, never another app's.
Sign-in for terminals: the device flow
The FurniOps CLI signs people in with the OAuth device flow (RFC 8628): furniops login shows a
code, opens your browser at FurniOps sign-in, and you confirm the same code there. Codes look like
WDJB-MJHT and expire after 10 minutes. The device flow is for the CLI's own client; an
organisation's apps use the flows above. See the CLI.
FurniOps' own apps
The dashboard, the CLI and the operations console are FurniOps' own (first-party) apps. They do not show a consent screen, and the dashboard keeps its tokens on its server, never in your browser. Everything on this page about your apps applies to anyone else's.
API keys
Secret and restricted keys, the key wizard, every scope a key can hold, templates, access policies, expiry, rotation, keys for AI agents, suspicious-activity reviews and what to do when a key leaks.
Organisations
How accounts and organisations relate, creating and switching organisations, the organisation's settings, and leaving one or deleting your account.