Documentation / Authentication

Authentication

OAuth 2.0 for the Configo API — the authorization code flow with PKCE, exchanging and refreshing tokens, token lifetimes, revocation and dynamic client registration.

The Configo API uses OAuth 2.0 authorization code grants. A user signs in to Configo, approves your application, and your application receives tokens that act on that user's behalf, limited to the scopes they approved.

Before you start, register an application under OAuth Apps in your account (see OAuth Applications). You get a client_id, and, unless it is a public client, a client_secret.

Endpoint Purpose
https://configo.org/oauth/authorize The user approves your application
https://configo.org/api/auth/token Exchange a code or a refresh token for tokens
https://configo.org/api/auth/token/revoke Revoke a token
https://configo.org/oauth/register Dynamic client registration (RFC 7591)
https://configo.org/.well-known/oauth-authorization-server Authorization server metadata (RFC 8414)

Only the authorization_code and refresh_token grants are supported. There are no client credentials, password or implicit grants.

Token lifetimes

Token Lifetime Notes
Authorization code 5 minutes Single use
Access token 2 hours Sent as Authorization: Bearer <access_token>
Refresh token 30 days Single use: each refresh returns a new refresh token

1. Send the user to authorize

Redirect the user's browser to:

https://configo.org/oauth/authorize
    ?response_type=code
    &client_id=0199b6ff-0000-7000-8000-000000000001
    &redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
    &scope=projects%20orders%20contacts
    &state=af0ifjsldkj
    &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
    &code_challenge_method=S256
Param Required Description
response_type yes Must be code
client_id yes Your application's ID
redirect_uri yes Must exactly match one of the application's registered redirect URIs
scope yes Space-separated scope names. Unknown names are ignored. Every scope must be enabled for the application, or the request is rejected
state recommended Opaque value returned unchanged; use it against CSRF
code_challenge public clients PKCE challenge: BASE64URL(SHA256(code_verifier))
code_challenge_method no Only S256 is accepted; omitted means S256. plain is rejected

The user signs in if needed and sees a consent screen listing the scopes. Trusted applications skip the consent screen.

After approval, Configo redirects to:

https://app.example.com/callback?code=Splx10BeZQQYbYS6WxSbIA&state=af0ifjsldkj

Invalid parameters (unknown client, redirect URI not registered, a scope the application does not have, a PKCE method other than S256) are shown to the user as an error page and are not redirected back.

PKCE

PKCE protects the code exchange. It is required for public clients and recommended for everyone else. Generate a random code_verifier (43–128 characters from A–Z a–z 0–9 - . _ ~) and send its SHA-256 as the challenge:

import {createHash, randomBytes} from 'node:crypto'

const code_verifier = randomBytes(32).toString('base64url')
const code_challenge = createHash('sha256').update(code_verifier).digest('base64url')

If a code was issued with a challenge, the exchange must include the matching code_verifier.

2. Exchange the code for tokens

POST /api/auth/token with an application/x-www-form-urlencoded body:

Param Required Description
grant_type yes authorization_code
client_id yes Your application's ID
client_secret confidential clients Your application's secret. Omit for public clients
code yes The code from the redirect
redirect_uri yes The same redirect_uri used in step 1
code_verifier with PKCE The verifier whose hash you sent as the challenge
curl https://configo.org/api/auth/token \
  -d grant_type=authorization_code \
  -d client_id=0199b6ff-0000-7000-8000-000000000001 \
  -d client_secret=$CLIENT_SECRET \
  -d code=Splx10BeZQQYbYS6WxSbIA \
  -d redirect_uri=https://app.example.com/callback \
  -d code_verifier=$CODE_VERIFIER

Token Response

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "kFJ4mQ2...40 characters",
  "token_type": "Bearer",
  "expires_in": 7200,
  "scope": "projects orders contacts"
}
Field Type Description
access_token string Bearer token for API requests
refresh_token string Use it to get a new pair
token_type string Always Bearer
expires_in integer Seconds until the access token expires: 7200
scope string Granted scopes, space-separated

Store both tokens securely. Treat the access token as opaque; do not rely on its contents.

3. Refresh the tokens

Before or after the access token expires, POST /api/auth/token:

Param Required Description
grant_type yes refresh_token
client_id yes Your application's ID
client_secret confidential clients Your application's secret
refresh_token yes The current refresh token

The response is a new token pair. Refresh tokens rotate: the old refresh token, and the access token issued with it, stop working immediately. Always replace both with the new ones.

  • If two requests refresh with the same token at once (for example, two tabs), the second one within 60 seconds receives the same new pair instead of an error.
  • A refresh fails if the user's account is no longer active.
  • The new pair carries the scopes the user granted, narrowed to those the application still has. If the application owner removed a scope from the application, it disappears on the next refresh.
  • When a refresh token expires after 30 days, the user has to authorize again.

Revoke a token

POST /api/auth/token/revoke (RFC 7009), form-encoded:

Param Required Description
client_id yes Your application's ID
client_secret confidential clients Your application's secret
token yes An access or refresh token
token_type_hint no access_token or refresh_token

Revoking either token of a pair revokes both. The response is 200 {} even if the token was already invalid.

Token endpoint errors

The token and revoke endpoints follow OAuth 2.0 and return {"error": "...", "message": "..."}:

Status error When
400 invalid_request A required parameter is missing; a public client used a code issued without PKCE
400 invalid_grant The code or refresh token is invalid, expired or already used; redirect_uri does not match; code_verifier is missing or wrong
400 unsupported_grant_type grant_type is not authorization_code or refresh_token
401 invalid_client Unknown client_id, or a wrong or missing client_secret
429 slow_down Too many failed client authentications, see below

Failed client authentication is limited per client_id: after 20 failures within 15 minutes, the token and revoke endpoints return 429 slow_down with Retry-After: 900 for that client until the window passes. A successful authentication resets the count.

Public clients

An application with no backend (a CLI, a desktop or single-page app) cannot keep a secret. Register it as a public client: it gets no client_secret, and PKCE is mandatory for it. Omit client_secret from every request.

Dynamic client registration

MCP clients and similar tools can register themselves with RFC 7591: POST /oauth/register with a JSON body.

{
  "client_name": "My MCP client",
  "redirect_uris": [
    "http://127.0.0.1:33418/callback"
  ],
  "grant_types": [
    "authorization_code",
    "refresh_token"
  ],
  "scope": "projects configurators materials"
}
{
  "client_id": "0199b7aa-0000-7000-8000-000000000001",
  "client_id_issued_at": 1790870000,
  "client_name": "My MCP client",
  "redirect_uris": [
    "http://127.0.0.1:33418/callback"
  ],
  "grant_types": [
    "authorization_code",
    "refresh_token"
  ],
  "response_types": [
    "code"
  ],
  "token_endpoint_auth_method": "none",
  "scope": "projects configurators materials"
}
  • Clients registered this way are always public (token_endpoint_auth_method: none) and marked unverified on the consent screen.
  • Only the scopes projects, materials and configurators can be requested; others are dropped from the response.
  • At most 10 redirect URIs.
  • Unused registrations are deleted after a while; register again if your client_id stops working.
  • Registration is limited to 500 per hour across all callers; over it, the response is 429 slow_down with Retry-After.

Trusted applications

Configo's own applications are trusted: they skip the consent screen and are not limited by scopes. Some endpoints (sessions, passkeys, OAuth app management, support tickets, account deletion, partner tools) are available only to trusted applications and are not part of the public API.