Auth API
Session (JWT)
Register
POST /api/v1/auth/register
Create a new user account.
Body: {email, password, name?}
Returns: {user: {id, email, name}, token, refresh_token}
Login
POST /api/v1/auth/login
Body: {email, password}
Returns: {user: {id, email, name}, token, refresh_token}
Logout
POST /api/v1/auth/logout
Invalidates the current session. Requires Authorization: Bearer <token>.
Refresh
POST /api/v1/auth/refresh
Exchange a refresh token for a new access token.
Body: {refresh_token}
Returns: {token, refresh_token}
User profile
Get current user
GET /api/v1/users/me
Requires auth. Returns the authenticated user's profile.
Get user profile
GET /api/v1/users/{userID}
Requires auth.
Update profile
PUT /api/v1/users/{userID}
Body: {name?, email?}
Get own permissions
GET /api/v1/permissions
Returns the authenticated user's permissions across all repositories and the system domain. Used by the frontend RBAC store.
Admin (role management)
List users
GET /api/v1/admin/users
Permission: user:read. Returns all users.
Assign role
PUT /api/v1/admin/users/roles
Permission: role:manage.
Body: {user_id, role, domain}
Remove role
DELETE /api/v1/admin/users/roles
Permission: role:manage.
Body: {user_id, role, domain}
List permissions
GET /api/v1/admin/permissions
Permission: role:read. Returns all known permissions and their roles.
OAuth 2.1 Endpoints
These are the endpoints an MCP client or programmatic agent uses to obtain access tokens. The server implements OAuth 2.1 with PKCE.
Register client
POST /api/v1/oauth/register
Register an OAuth 2.1 client (typically a "public" client with PKCE). Returns the client credentials.
Authorize
GET /api/v1/oauth/authorize
POST /api/v1/oauth/authorize
The authorization endpoint. The GET returns a server-rendered login + consent HTML page; the POST submits the authorization decision.
Authorize login
POST /api/v1/oauth/authorize/login
Server-rendered login form submission for the OAuth flow.
Token
POST /api/v1/oauth/token
Exchange an authorization code (with PKCE verification) for an access + refresh token. Access tokens are HS256 JWTs.
Revoke
POST /api/v1/oauth/revoke
Revoke a refresh token. Refresh tokens are hashed at rest in okt_system.oauth_refresh_tokens.
Well-known
Authorization server metadata
GET /.well-known/oauth-authorization-server
Returns the OAuth 2.1 authorization server metadata (issuer, endpoints, supported scopes, etc.).
Protected resource metadata
GET /.well-known/oauth-protected-resource
Returns the protected resource metadata.
Bootstrap admin
There are two ways to get a sysadmin on a fresh install — pick one based on whether the deployment is reachable from the public internet.
First-user autopromotion (default, localhost dev)
bootstrap.auto_promote_first_user (default true) makes the first
successful POST /api/v1/auth/register on an empty users table grant the
sysadmin role on the system domain to that user. So:
docker compose up- Open http://localhost:3000 and register.
- You're sysadmin. Done — no env vars, no scripts.
Bindable from .env via OKT_BOOTSTRAP_AUTO_PROMOTE (accepts
true/false/1/0). A log line is emitted when it fires so an
operator notices if it fires unexpectedly.
Explicit admin from env vars (public deployments)
For a publicly-exposed deployment, set OKT_BOOTSTRAP_AUTO_PROMOTE=false
in .env and use the default_admin flag + the
OKT_BOOTSTRAP_DEFAULT_ADMIN_* env vars so an attacker cannot become
sysadmin by registering first:
bootstrap:
auto_promote_first_user: false
default_admin: true
OKT_BOOTSTRAP_DEFAULT_ADMIN_PASSWORD= # generate a strong one
OKT_BOOTSTRAP_DEFAULT_ADMIN_DISPLAY_NAME=Default Admin
The admin is seeded at boot (bootstrap.EnsureDefaultAdmin) when the
users table is empty. Once any user exists, the step is a no-op (the env
vars may be removed or rotated safely).
When both auto_promote_first_user and default_admin are enabled,
default_admin wins: it runs at boot before any Register call, so the
users table is non-empty by the time autopromote's CountUsers() == 1
guard would fire.
Promoting an existing user (dev only)
To promote an already-registered user to system admin (sysadmin role on
the system domain, giving */*):
just bootstrap-admin [email protected]
This is idempotent and restarts the dev API service so the in-memory
enforcer reloads. It targets the dev compose profile
(backend/docker-compose.yml --profile dev); there is no
production-stack equivalent short of psql surgery or having an existing
admin call PUT /api/v1/admin/users/roles. See Operator guide.