Authentication
Authentication
Section titled “Authentication”dbward supports two authentication methods: API tokens (simple, self-hosted) and OIDC (SSO with your identity provider). You can use either or both.
Authentication
Section titled “Authentication”dbward supports two authentication methods that work simultaneously:
- API Tokens (
dbw_...): Always accepted. Used by CLI, agents, and CI/CD. - OIDC JWTs (
eyJ...): Accepted when[auth.oidc]is configured and a Team license is active.
When [auth.oidc] is present, both methods are accepted. When absent, only API tokens work.
API Tokens
Section titled “API Tokens”Creating tokens
Section titled “Creating tokens”# Via CLIdbward token create --subject alice --scope-roles requesterdbward token create --subject bob --scope-roles requester,dba --expires 90ddbward token create --subject prod-agent --subject-type agent --no-scope-ceiling# Via REST API (requires token.create or token.create_agent permission)curl -X POST http://localhost:3000/api/tokens \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "subject_id": "bob", "subject_type": "user", "scope_ceiling": {"roles": ["requester", "dba"]}, "name": "Bob CI token", "expires_at": "2026-09-01T00:00:00Z" }'Token options
Section titled “Token options”| Field | Type | Description |
|---|---|---|
subject_id | string | Required. User or service identifier. |
subject_type | string | Required. user or agent. |
scope_ceiling | object | Max roles this token can activate. Format: {"roles": ["role1", "role2"]}. Effective roles = intersection of scope_ceiling.roles and the user’s assigned roles (direct + group-derived). Set null for agent tokens (--no-scope-ceiling). Optional (auto-derived from resolved roles when omitted) for user tokens. |
name | string | Human-readable label. |
expires_at | datetime | Absolute expiry (RFC 3339). Unset = no expiration. |
Token lifecycle
Section titled “Token lifecycle”Create → Active → [Expired | Revoked]- Expiration: Tokens with
expires_atare rejected after the deadline. - Revocation: Immediate via
DELETE /api/tokens/{id}. - Self-revoke: Users with
token.revokepermission can revoke their own tokens. - No rotate API: Revoke the old token and create a new one.
Revoking tokens
Section titled “Revoking tokens”# Admin can revoke any tokencurl -X DELETE http://localhost:3000/api/tokens/$TOKEN_ID \ -H "Authorization: Bearer $ADMIN_TOKEN"
# Users can revoke their own tokenscurl -X DELETE http://localhost:3000/api/tokens/$TOKEN_ID \ -H "Authorization: Bearer $MY_TOKEN"Using tokens
Section titled “Using tokens”# In dbward.toml (client config)[server]url = "https://dbward.internal:3000"token = "dbw_a1b2c3..."Or via environment variable:
[server]url = "https://dbward.internal:3000"token = "${DBWARD_TOKEN}"Role resolution
Section titled “Role resolution”Tokens do not embed roles directly. The server resolves roles dynamically at request time:
- Direct roles: Roles assigned to the user via
dbward user add --roleordbward user update --role - Group-derived roles: Roles inherited from group membership (
[[auth.groups]]withrolesfield in config) - Default role: Falls back to
[auth].default_roleif no roles found
The token’s scope_ceiling intersects with the resolved roles to produce effective permissions.
For details on roles and permissions, see Authorization Reference.
Inspecting tokens
Section titled “Inspecting tokens”Check a token’s current effective permissions:
dbward token inspect <ID># Via REST API (owner or token.list permission)curl http://localhost:3000/api/tokens/$TOKEN_ID/inspect \ -H "Authorization: Bearer $MY_TOKEN"OIDC (SSO) (Team)
Section titled “OIDC (SSO) (Team)”Server configuration
Section titled “Server configuration”[auth.oidc]issuer = "https://accounts.google.com"client_id = "123456789.apps.googleusercontent.com"# # client_secret_env is not supported # Optional: env var name# jwks_uri = "http://keycloak:8080/realms/dbward/protocol/openid-connect/certs" # Override for Dockerdefault_role = "approver" # Role when no mapping matches (default: approver)Client configuration
Section titled “Client configuration”# In dbward.toml (client config)[server]url = "https://dbward.internal:3000"
[server.oidc]issuer = "https://accounts.google.com"client_id = "123456789.apps.googleusercontent.com"# discovery_url = "..." # Override discovery endpoint# browser_url = "..." # Override authorize URL (for Docker)# backchannel_url = "..." # Override token endpoint (for Docker)Login flow
Section titled “Login flow”# Browser-based login (opens browser for OAuth flow)dbward login
# Device flow (for headless environments / SSH)dbward login --device
# Check current identitydbward whoami
# Logout (revokes token locally)dbward logoutRole mappings
Section titled “Role mappings”Map IdP claims to dbward roles:
# Map by group membership[[auth.oidc.role_mappings]]claim = "groups"value = "db-admins"role = "admin"
[[auth.oidc.role_mappings]]claim = "groups"value = "backend-team"role = "requester"How it works:
- All matching mappings are collected (a user can have multiple roles)
- If no mapping matches,
[auth.oidc].default_roleis used, then[auth].default_role - Roles grant specific permissions (see Authorization Reference)
Supported IdPs
Section titled “Supported IdPs”| IdP | Notes |
|---|---|
| Google Workspace | Set up OAuth consent screen + credentials |
| Okta | Create OIDC app, add groups claim to ID token |
| Keycloak | Create client, enable groups mapper |
| Azure AD (Entra) | Register app, configure groups claim |
| Auth0 | Add groups via Rules/Actions (custom claim namespace) |
User management
Section titled “User management”CLI-managed users
Section titled “CLI-managed users”Users are managed via the dbward user CLI commands:
# Add a user with a roledbward user add alice --role requester
# Add a user to a group (inherits group roles)dbward user add bob --role dba --group backend-team
# Suspend a user (revokes tokens, cancels pending requests)dbward user suspend alice
# Reactivatedbward user activate alice
# Remove entirelydbward user rm aliceRoles are assigned directly to users via dbward user add --role or inherited from group membership (groups.roles in config).
OIDC users
Section titled “OIDC users”OIDC users are created automatically on first login with source = "oidc". To disable an OIDC user:
- CLI suspend:
dbward user suspend <id>— immediately blocks all requests (the server checksis_suspendedon every request, even with a valid JWT). - IdP-side disable: Prevents new JWT issuance. Existing JWTs are still checked against
is_suspendedper-request, so suspend is effective regardless of JWT lifetime.
API suspend vs CLI suspend
Section titled “API suspend vs CLI suspend”| Method | Effect | Persistence |
|---|---|---|
POST /api/users/{id}/suspend | Immediate suspend + revoke | Persistent in DB |
dbward user suspend <id> | Immediate suspend + revoke | Persistent in DB |
Groups
Section titled “Groups”Groups enable team-based approval workflows. They come from the groups claim in the OIDC JWT.
How groups flow
Section titled “How groups flow”IdP (groups: ["dba-team", "backend"]) │ │ JWT with groups claim ▼dbward server │ │ Reads groups directly from JWT (no sync, no storage) ▼Workflow evaluation │ │ "Does this user belong to group:dba-team?" ▼Approval decisionUsing groups in workflows
Section titled “Using groups in workflows”[[workflows]]database = "primary"environment = "production"
[[workflows.steps]]type = "approval"[[workflows.steps.approvers]]group = "dba-team" # Anyone in the IdP "dba-team" group can approvemin = 1Sync model
Section titled “Sync model”There is no sync. dbward reads groups from the JWT on every request.
| Question | Answer |
|---|---|
| When do group changes take effect? | When the user gets a new JWT (re-login) |
| Can the IdP push changes? | No. JWT-based, stateless. |
| How fast is the update? | Depends on JWT lifetime (IdP setting, typically 5min–1hr) |
| Is there a manual refresh? | dbward logout && dbward login |
Recommendation: Set your IdP’s token lifetime to 5–15 minutes for near-real-time group updates.
Groups vs Roles
Section titled “Groups vs Roles”| Groups | Roles | |
|---|---|---|
| Source | IdP groups claim | role_mappings conversion |
| Purpose | Workflow approver matching | API access control |
| Example | group:dba-team | admin, requester |
| Stored in dbward? | No (JWT only) | No (JWT only) |
Both are extracted from the JWT on every request. Neither is stored in dbward’s database.
Agent authentication
Section titled “Agent authentication”Agents use API tokens with subject_type = "agent" and --no-scope-ceiling. Agents always resolve to the agent-default role regardless of ceiling:
dbward token create --subject prod-agent --subject-type agent --no-scope-ceilingThe --no-scope-ceiling flag removes the scope ceiling restriction, allowing the agent token to activate all roles assigned to the agent user. This is the recommended setup for agents since their permissions are fully controlled via user roles.
Note:
--no-scope-ceilingconflicts with--scope-roles— they cannot be used together.
When [auth.oidc] is configured, both API tokens and OIDC JWTs are accepted. Agents always use API tokens.
Security recommendations
Section titled “Security recommendations”- Use TTL on all tokens — Set
expires_atto 90 days max. Rotate before expiry. - Use OIDC for humans — Avoid sharing long-lived tokens between team members.
- Separate agent tokens — One token per agent. Revoke individually if compromised.
- Short JWT lifetime — Configure your IdP to issue tokens with 5–15 minute expiry.
- Configure [auth.oidc] — OIDC for humans, API tokens for agents. Best of both worlds.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause | Fix |
|---|---|---|
401 invalid token | Token revoked or wrong | Check dbward token list |
401 token expired | TTL exceeded | Create a new token |
401 OIDC not configured | JWT sent but [auth.oidc] not configured | Add [auth.oidc] section to server.toml |
JWT verification failed | Wrong issuer/audience/expired | Check issuer and client_id match IdP |
| JWKS fetch timeout | Server can’t reach IdP | Check network, or set jwks_uri override |
See also
Section titled “See also”- Server setup — Full server configuration
- Workflows — Group-based approval rules
- Agent setup — Agent token configuration