Endpoints and CLI

Endpoints and CLI

Bohurupee speaks the OAuth 2.0 authorization-code flow and OpenID Connect discovery. Every {provider} is a URL slug (google, github, acme, …). The same persona through different slugs gets different subjects (google:alice vs acme:alice).

Base URL (default): http://127.0.0.1:4190


HTTP routes

Method Path Purpose
GET / Dashboard (personas, copy-paste URLs)
GET /{provider}/authorize Consent / auto-approve / deny
POST /{provider}/token Code or refresh → tokens
GET /{provider}/userinfo Bearer profile JSON
GET /{provider}/.well-known/openid-configuration OIDC discovery
GET /{provider}/jwks JWKS
GET /{provider}/auth/keys JWKS alias
GET/POST /__login Test helper: set loginAs cookie
POST /__reload Reload YAML (loopback only)

Provider profiles may add aliases (for example GET /facebook/me).

CORS (loopback Origin only) is enabled on discovery, token, and userinfo so a browser app on another localhost port can finish the code flow.


GET /{provider}/authorize

Starts login. On success Bohurupee redirects (or form-posts) to redirect_uri with code and state. On user cancel or validation errors with a valid redirect, it returns error / error_description (RFC 6749).

Query parameters

Parameter Required Description
client_id yes Any string when openClient is true
redirect_uri yes Absolute http or https URL, no fragment
response_type yes Must be code
state yes Opaque value echoed back
scope no Space-separated; include openid for id_token (unless idToken: always or profile forces it)
nonce no Copied into id_token when present
response_mode no query (default) or form_post
code_challenge depends on pkce PKCE challenge
code_challenge_method no S256 (preferred) or plain
auto no Persona id — skip consent and issue a code
deny no Truthy (1, true, …) — error=access_denied
prompt no login or select_account forces the consent UI (ignores last-persona and loginAs cookies; auto= still wins)
login_hint no Persona id to highlight on the consent page

Example

http://127.0.0.1:4190/google/authorize?client_id=dev-client&redirect_uri=http%3A%2F%2F127.0.0.1%3A9999%2Fcallback&response_type=code&state=xyz&scope=openid%20profile%20email

Auto-approve Alice:

…&auto=alice

POST /{provider}/token

Content-Type: application/x-www-form-urlencoded.

Client auth: HTTP Basic or client_id / client_secret form fields (secret accepted, not checked).

Authorization code grant

Field Required
grant_type authorization_code
code yes
redirect_uri yes (must match authorize)
client_id yes (unless Basic)
code_verifier when PKCE was used

Success (default, no refresh):

{
  "access_token": "…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "openid profile email",
  "id_token": "…"
}

With refreshTokens: true, a refresh_token field is added.

Errors are JSON: {"error":"invalid_grant","error_description":"…"}.

How codes, access tokens, refresh tokens, and id_token are generated: How the Laravel adapter works — How the binary issues tokens.

Refresh grant (opt-in)

Requires refreshTokens: true in YAML.

Field Required
grant_type refresh_token
refresh_token yes
client_id yes

GET /{provider}/userinfo

Authorization: Bearer <access_token>

Returns the merged persona / profile JSON. Generic shape:

{
  "id": "google:alice",
  "sub": "google:alice",
  "email": "[email protected]",
  "email_verified": true,
  "name": "Alice Admin",
  "nickname": "alice",
  "avatar": "https://api.dicebear.com/9.x/identicon/svg?seed=alice"
}

Provider templates add fields (login, avatar_url, …). See Provider profiles.


Discovery and JWKS

GET /{provider}/.well-known/openid-configuration
GET /{provider}/jwks

Issuer is http://127.0.0.1:4190/{provider} (or whatever host you bound). Discovery lists authorize, token, userinfo, jwks, supported grants, PKCE methods, and claims_supported.


POST /__login

Test helper used by Playwright / PHP loginAs. Sets an HttpOnly cookie so the next authorize in that cookie jar skips consent.

Field Required Notes
persona yes Must exist in config
provider no Echoed in the JSON response
curl -s -c cookies.txt -X POST http://127.0.0.1:4190/__login \
  -d 'persona=alice&provider=google'

POST /__reload

Loopback only (127.0.0.1, ::1). Reloads the YAML path used at startup.

curl -s -X POST http://127.0.0.1:4190/__reload

Example success body:

{
  "ok": true,
  "config": "bohurupee.yaml",
  "personas": ["alice", "bob", "carol"],
  "refreshTokens": false
}

CLI

bohurupee [flags]
bohurupee init [--config path] [--force]
bohurupee --version
Flag Default Purpose
--config ./bohurupee.yaml if present YAML path
--bind 127.0.0.1 (or file) Listen host
--port 4190 (or file) Listen port
--dangerously-bind-all-interfaces off Allow non-loopback bind
--version Print version and exit

bohurupee init

Writes a starter config (same shape as bohurupee.example.yaml). Refuses to overwrite unless --force.

bohurupee init
bohurupee init --config ./dev.yaml
bohurupee init --force

Environment

Variable Effect
BOHURUPEE_AUTO_APPROVE Truthy (1, true, yes, alice) — every authorize auto-picks the default persona (unless prompt=login or auto= / deny)
BOHURUPEE_IN_DOCKER Set by the official image; allows 0.0.0.0 bind inside the container; upgrades loopback bind from YAML
BOHURUPEE_CONFIG Absolute/relative path to YAML when --config is omitted

Lifetimes

Item TTL
Authorization code 2 minutes, single use
Access token 1 hour
Refresh token 24 hours (when enabled)

All grants are in-process memory and disappear when the binary exits.

Edit this page