Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.busha.io/llms.txt

Use this file to discover all available pages before exploring further.

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

curl -X POST https://login.busha.io/oauth2/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=authorization_code" \
  --data-urlencode "code=$AUTH_CODE" \
  --data-urlencode "redirect_uri=https://yourapp.example.com/oauth2/callback" \
  --data-urlencode "code_verifier=$CODE_VERIFIER"

Refresh token → new pair

curl -X POST https://login.busha.io/oauth2/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=refresh_token" \
  --data-urlencode "refresh_token=$REFRESH_TOKEN"
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.
curl -X POST https://login.busha.io/oauth2/revoke \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "token=$REFRESH_TOKEN" \
  --data-urlencode "token_type_hint=refresh_token"
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.
curl https://login.busha.io/.well-known/openid-configuration
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.
curl https://login.busha.io/.well-known/jwks.json
Cache the response for ~5 minutes. Refetch when you encounter a kid in a JWT header that is not present in your cache.