Skip to main content
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.
MethodEndpointPurpose
GET/oauth2/authStart an authorization request (browser redirect).
POST/oauth2/tokenExchange a code for tokens, or refresh an existing session.
POST/oauth2/revokeRevoke an access or refresh token.
GET/.well-known/openid-configurationOIDC discovery document.
GET/.well-known/jwks.jsonPublic 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:
ParameterValue
response_typecode
client_idYour app’s client_id
redirect_uriByte-identical to a registered URI
scopeSpace-separated scope list
stateCSRF guard — at least 16 random bytes, bound to the user’s session
code_challengebase64url(sha256(code_verifier))
code_challenge_methodS256plain 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.