The OAuth2 surface is split into two hosts: protocol endpoints at the issuer (login.busha.io) and the resource API (api.busha.io). OAuth app management — creating apps, revealing secrets, editing redirect URIs — is dashboard-only at app.busha.io.
| Method | Endpoint | Purpose |
|---|
GET | /oauth2/auth | Start an authorization request (browser redirect). |
POST | /oauth2/token | Exchange a code for tokens, or refresh an existing session. |
POST | /oauth2/revoke | Revoke an access or refresh token. |
GET | /.well-known/openid-configuration | OIDC discovery document. |
GET | /.well-known/jwks.json | Public keys for JWT signature verification. |
GET /oauth2/auth
Browser redirect — this is where you send the user’s browser to start authorization. Never call this server-to-server.
Required query parameters:
| Parameter | Value |
|---|
response_type | code |
client_id | Your app’s client_id |
redirect_uri | Byte-identical to a registered URI |
scope | Space-separated scope list |
state | CSRF guard — at least 16 random bytes, bound to the user’s session |
code_challenge | base64url(sha256(code_verifier)) |
code_challenge_method | S256 — plain is not accepted; missing challenges are rejected |
Success: Redirects to your callback with ?code=...&state=...
User cancelled: Redirects with ?error=access_denied&state=...
POST /oauth2/token
Server-to-server. Authenticate with HTTP Basic using client_id:client_secret. Never call this from the browser.
Authorization code → tokens
Refresh token → new pair
Important details:
id_token is returned only when openid is in the granted scopes.
refresh_token is returned only when offline_access is granted.
redirect_uri must exactly match a registered URI.
- Authorization codes are single-use and expire in 10 minutes.
- Token exchange is not idempotent — if your callback handler runs twice, the second call returns
invalid_grant.
Accepted grant types: authorization_code (with PKCE-S256) and refresh_token only. Implicit, resource-owner password, and client_credentials are not supported.
POST /oauth2/revoke
Revoke an access or refresh token. Call this when a user disconnects your integration.
Returns 200 with an empty body regardless of whether the token was valid — this is intentional to avoid leaking token validity information.
Revoking a refresh token kills future refreshes immediately but does not invalidate a currently-issued access token. The access token continues to validate until exp.
GET /.well-known/openid-configuration
The OIDC discovery document. Contains the canonical issuer string and the URLs of all protocol endpoints.
Read the issuer value from this document at startup and pin your JWT validation against it. Do not hardcode the issuer string.
GET /.well-known/jwks.json
The public key set used to verify JWT signatures.
Cache the response for ~5 minutes. Refetch when you encounter a kid in a JWT header that is not present in your cache.