bus-api-provider-auth
Auth Provider For Bus API
bus-api-provider-auth is the authentication provider used by Bus API
deployments that need account registration, approval, and API token issuing.
Users register by email, verify email ownership with an OTP, wait for admin
approval, and then request short-lived Bus API JWTs for services such as LLM
hosting, billing, containers, events, and terminal access. Email is never the
account ID, so an email change does not change the identity seen by API
providers or billing systems.
The public flow is passwordless but approval-gated. OTP verification returns an
auth-service token with aud=ai.hg.fi/auth; it does not grant model access.
Only an approved verified user can request a Bus API token with
aud=ai.hg.fi/api and feature scopes such as llm:proxy.
Approved users can request the same API-audience JWT with domain scopes such as
vm:read and container:run, not broad event-pattern scopes. Those tokens use
aud=ai.hg.fi/api and work for both REST APIs and Events API endpoints. The
provider only issues user API scopes allowed by BUS_AUTH_API_USER_SCOPES.
The provider is enabled through bus-api as an explicit provider named auth.
The local compose example in bus-api-provider-auth/examples/local-compose/
starts PostgreSQL, MailHog, and bus-api with the auth provider enabled. The
generic module path remains available at /local-dev/v1/modules/auth, and the
production-friendly routes are available at /local-dev/v1/api/v1/auth/* and
/local-dev/v1/api/internal/auth/*. It uses only non-secret development defaults and
prefers the published ghcr.io/busdk/bus-api:latest image by default. MailHog
exposes its HTTP UI/API on http://127.0.0.1:8025 so operators can read local
OTP email messages.
Start the standalone auth compose example from the BusDK superproject root
after confirming Docker Compose is available, ports 8080 and 8025 are free,
and the published ghcr.io/busdk/bus-api:latest image can be pulled or the
local build can complete:
docker compose -f bus-api-provider-auth/examples/local-compose/docker-compose.yml up -d --build
curl -fsS http://127.0.0.1:8080/local-dev/v1/healthz
The readiness request should return JSON with "ok":true.
The BusDK superproject root compose.yaml uses the same auth provider through
bus-api, together with the broader local AI Platform stack. In that stack,
nginx exposes /api/v1/auth/* and /api/internal/auth/* on
http://127.0.0.1:${LOCAL_AI_PLATFORM_PORT:-8080}. The default SMTP host is
MailHog, and BUS_AUTH_API_USER_SCOPES controls which feature scopes an
approved local user may request.
For a hosted AI Platform smoke check, use the hosted auth API and read the OTP
from the deployment’s configured email sender. For the local compose examples,
use the local auth URL shown above and read the OTP from MailHog instead. The
hosted flow requires the Bus CLI to be installed; BUS_AUTH_API_URL selects
the auth provider, and bus auth verify stores the verified auth-service
session under the normal Bus config directory. The flow starts by verifying the
email account:
export BUS_AUTH_API_URL=https://ai.hg.fi/api/v1/auth
bus auth login --email user@example.com
OTP="<code-from-hosted-email-sender>"
bus auth verify --email user@example.com --otp "$OTP"
Then an operator approves the verified account through
bus operator auth approve or the deployment’s normal approval workflow:
the token file must contain an auth-service admin JWT with
waitlist:approve or admin:manage, provisioned by the deployment’s operator
secret flow.
bus operator auth \
--api-url https://ai.hg.fi/api/v1/auth \
--token-file /run/secrets/bus-auth-admin.token \
approve --email user@example.com
After approval, request the hosted API token and use it against
https://ai.hg.fi/v1:
bus auth token --scope "llm:proxy"
curl -fsS -H "Authorization: Bearer $(cat ~/.config/bus/auth/api-token)" \
https://ai.hg.fi/v1/models
The final request should return the deployment’s model catalog. Do not depend on developer-specific checkout paths or external JWT-issuing commands.
Use bus-api-provider-auth --help for operator-facing module help. The help
output follows Git-style sections for name, synopsis, description, options,
environment, examples, and related documentation.
Minimal hosted configuration uses a durable signing secret, PostgreSQL store, SMTP OTP delivery, an internal shared key for service-token issuing when used, and an explicit user-scope allow-list:
BUS_AUTH_HS256_SECRET="$(cat /run/secrets/bus-auth-hs256)"
BUS_AUTH_POSTGRES_DSN='postgres://bus_auth:password@postgres.example.internal:5432/bus_auth?sslmode=require'
BUS_AUTH_OTP_SENDER=smtp
BUS_AUTH_SMTP_HOST=smtp.example.internal
BUS_AUTH_SMTP_PORT=587
BUS_AUTH_SMTP_FROM=auth@example.test
BUS_AUTH_SMTP_USERNAME=bus-auth
BUS_AUTH_SMTP_PASSWORD="$(cat /run/secrets/bus-auth-smtp-password)"
BUS_AUTH_INTERNAL_SHARED_KEY="$(cat /run/secrets/bus-auth-internal-key)"
BUS_AUTH_API_USER_SCOPES="llm:proxy billing:read billing:setup vm:read container:read container:run"
BUS_AUTH_HS256_SECRET
Sets the JWT signing secret.
Use a deployment secret value of at least 32 bytes. Plain values are raw text
even when they look like base64; use base64:<value> only for intentionally
base64-encoded secrets.
BUS_AUTH_INTERNAL_TOKEN_TTL_SECONDS
Sets the lifetime for trusted internal service tokens issued by
/api/internal/auth/token.
The default is 600 seconds. Raise it only for trusted long-running workers that cannot rotate tokens more frequently.
BUS_AUTH_INTERNAL_SHARED_KEY
Protects /api/internal/auth/token.
Set this as a deployment secret when trusted services or operator automation
need the auth provider to mint internal or service tokens. If the key is unset
or the request omits the matching X-Bus-Internal-Key header, internal token
issuing fails with 403 Forbidden. Store the value in a secret manager or
untracked local operator configuration, not in command-line arguments.
BUS_AUTH_STORE_PATH
Enables file-backed persistence for account identities and revocations.
When neither file-backed nor PostgreSQL persistence is configured, deployments use in-memory auth state. In-memory state is suitable only for local development because users, approvals, sessions, and revocations disappear on restart. Use PostgreSQL for production deployments that need durable auth state.
BUS_AUTH_POSTGRES_DSN
Enables PostgreSQL persistence.
Store the DSN in deployment secrets or untracked operator configuration when it contains credentials. The value is a PostgreSQL URL such as:
postgres://bus_auth:password@postgres.example.internal:5432/bus_auth?sslmode=require
The configured role needs permission to create and update the auth provider’s
tables and indexes in the selected database/schema. Use
sslmode=disable only for trusted local development networks.
BUS_AUTH_OTP_SENDER
Selects the OTP sender.
Supported values are memory, console, and smtp; the default is memory.
Use smtp for hosted email delivery. Use console only for local development;
the console sender writes OTP codes to stdout with a BUS_AUTH_OTP prefix.
BUS_AUTH_SMTP_HOST
Sets the SMTP host for email OTP delivery.
Set this with BUS_AUTH_SMTP_FROM when BUS_AUTH_OTP_SENDER=smtp. Optional
settings are BUS_AUTH_SMTP_PORT, BUS_AUTH_SMTP_USERNAME, and
BUS_AUTH_SMTP_PASSWORD; the sender uses standard SMTP authentication when a
username is provided. Use the relay’s TLS/port policy, such as port 587 for
authenticated submission or MailHog port 1025 for local development. Missing
host/from configuration makes OTP delivery fail instead of silently falling
back to another sender.
BUS_AUTH_SMTP_FROM
Sets the sender address used for OTP email.
BUS_AUTH_API_USER_SCOPES
Sets the end-user API scopes the provider is allowed to issue after approval.
The value is a space-separated list such as
llm:proxy billing:read vm:read container:read container:run. If the variable
is empty, the deployment default applies. A token request that asks for a scope
outside this allow-list fails with a deterministic authorization error instead
of silently widening access.
Production deployments should set this allow-list explicitly before enabling
user token issuing. The root local AI Platform compose stack sets it from
BUS_LOCAL_TOKEN_SCOPES so local users can exercise LLM, billing, VM,
container, usage, events, work, and development-task paths with non-secret
development tokens.
Endpoint paths in this section are full route paths. The standalone auth
compose example serves them under the capability prefix
http://127.0.0.1:8080/local-dev/v1; the root local AI Platform gateway serves
them at http://127.0.0.1:${LOCAL_AI_PLATFORM_PORT:-8080}; hosted deployments
serve them under their public API origin.
POST /api/v1/auth/register
Creates or finds a registration candidate for an email address. Send
Content-Type: application/json with {"email":"user@example.com"}. Success
returns 200 OK with the user status, usually waitlisted. Registration
alone does not issue paid API access. Invalid email returns 400 Bad Request.
New users start waitlisted.
POST /api/v1/auth/otp/request
Creates a short-lived OTP challenge and sends it through the configured OTP
provider. Send {"email":"user@example.com"}. Success returns 200 OK with a
challenge status and no OTP secret in the response. Rate-limit failures return
429 Too Many Requests.
The console OTP provider is for local development only.
POST /api/v1/auth/otp/verify
Verifies the OTP and returns an auth-service token when verification succeeds.
Send {"email":"user@example.com","otp":"123456"}. Success returns 200 OK
with a token, expiry, user UUID, verification status, and waitlist status.
Wrong, expired, or reused OTPs return 401 Unauthorized or 400 Bad Request
depending on the failure.
The returned token uses audience ai.hg.fi/auth; it is not an LLM or container
API token.
GET /api/v1/auth/status
Returns the current user’s verification and approval status. Send
Authorization: Bearer <auth-service JWT>. Success returns 200 OK with
verified, status, user_id, and account_id when approved.
Use it after OTP verification to see whether the account is still waitlisted, approved, or rejected.
POST /api/v1/auth/token
Issues a Bus API token for an approved user. Send
Authorization: Bearer <auth-service JWT> and a JSON body such as
{"scope":"llm:proxy billing:read","ttl_seconds":3600}. Success returns
200 OK with the API JWT, expiry, audience ai.hg.fi/api, and granted scope.
Waitlisted, rejected, unverified, or underscoped users receive 403 Forbidden.
The provider only grants scopes allowed by BUS_AUTH_API_USER_SCOPES and the
account policy.
POST /api/v1/auth/token/refresh
Refreshes an auth-service session when refresh is allowed by deployment
configuration. Send Authorization: Bearer <auth-service JWT>. Success returns
a replacement auth-service token. Expired or revoked sessions return
401 Unauthorized.
POST /api/v1/auth/logout
Revokes or ends the current auth session. Send
Authorization: Bearer <auth-service JWT>. Success returns 204 No Content
or a small JSON confirmation. Clients should also clear local browser or CLI
session storage.
GET /api/v1/auth/me
Returns the current auth-service user identity and account information. Send
Authorization: Bearer <auth-service JWT>. Success returns 200 OK with user
UUID, email, verification state, approval status, and account UUID when one is
active.
Email remains auth-service data. API providers use the stable account UUID.
GET /api/v1/auth/check
Validates a bearer JWT and returns parsed claims. Send
Authorization: Bearer <JWT>. Success returns 200 OK with sub, aud,
scope, iat, and exp. Invalid signatures, wrong audience, missing expiry,
or revoked tokens return 401 Unauthorized.
This is a diagnostic auth-service endpoint. Domain API providers still enforce their own audience, scope, account ownership, and billing rules.
GET /api/v1/auth/admin/waitlist
Lists waitlisted users for an authorized operator. Send
Authorization: Bearer <auth-service admin JWT>. Requires
waitlist:read; deployments often mint this through bus operator token issue
or an internal operator secret store. Success returns 200 OK with a list of
emails, verification state, status, and timestamps.
POST /api/v1/auth/admin/approve
Approves a verified waitlisted user. Send
Authorization: Bearer <auth-service admin JWT> with
{"email":"user@example.com"}. Requires waitlist:approve or
admin:manage. Success returns 200 OK with the stable account_id.
Approval creates or activates the stable account UUID used as API token sub.
POST /api/v1/auth/admin/reject
Rejects a waitlisted user. Send
Authorization: Bearer <auth-service admin JWT> with
{"email":"user@example.com","reason":"optional operator note"}. Requires
waitlist:approve or admin:manage. Success returns 200 OK with status
rejected.
Rejected users cannot request paid feature API tokens.
POST /api/internal/auth/token
Issues trusted internal service tokens.
This endpoint is separate from the public user flow and is protected by the configured internal shared key. Send `X-Bus-Internal-Key: