Authentication¶
OxideAuth uses JWT Bearer tokens for authentication and a permission-based authorization system.
Auth API Reference¶
All authentication endpoints (Register, Login, Refresh, Password Reset, Account Confirmation, Token Revocation, and OAuth2) are documented in the Auth API reference.
Token Format¶
Tokens are HS256 (HMAC-SHA256) JWTs signed with the JWT_SECRET environment variable.
Token Claims¶
| Claim | Type | Description |
|---|---|---|
sub | UUID | Account ID of the authenticated user |
ws | UUID | Current workspace ID |
mem | UUID | Membership ID for the current session |
iss | string | Token issuer |
aud | string | Token audience |
exp | integer | Expiration timestamp (Unix epoch) |
iat | integer | Issued-at timestamp (Unix epoch) |
ty | string | Token type (see below) |
mem_ver | integer | Membership token version, checked against cached value during validation |
acc_ver | integer | Account token version, checked against cached value during validation |
sid | UUID (optional) | Session ID; null for single-use tokens |
Token Types¶
| Type | Description | Max Age |
|---|---|---|
Auth | Standard access token | 15 minutes (configurable via ACCESS_TOKEN_MAXAGE) |
Refresh | Token used to obtain a new Auth token | 7 days (configurable via REFRESH_TOKEN_MAXAGE) |
PasswordReset | Single-use token for password reset flows | 24 hours |
AccountConfirm | Token for email verification | 24 hours |
Passing Authentication¶
Include the token in the Authorization header of every request:
Warning
The two health endpoints (GET / and GET /health-check) do not require authentication. All other endpoints return 401 Unauthorized if the token is missing, expired, or revoked.
Permission System¶
Resource:Action Format¶
Permissions use a resource:action naming convention with wildcard support:
| Pattern | Matches | Example |
|---|---|---|
account:readAny | Read any account | View user profiles |
account:updateSelf | Update your own account | Edit profile |
workspace:* | All workspace actions | Full workspace admin |
*:read | Read any resource type | Global read-only |
* | All permissions | Super admin |
Permission Checking Flow¶
sequenceDiagram
participant Client
participant Axum as Axum Server
participant CtxMW as CtxMiddleware
participant Handler
participant Svc as Service Layer
participant AuthV as AuthValidator
participant Store
Client->>Axum: POST /accounts/create (Bearer token)
Axum->>CtxMW: Extract & decode JWT
CtxMW->>CtxMW: Validate version claims (mem_ver/acc_ver/sid)
CtxMW->>Handler: CoreCtx (account_id, workspace, perms)
Handler->>Svc: account_service.create(ctx, params)
Svc->>AuthV: validate_ctx_perms(ctx, "account:create")
AuthV->>AuthV: PermissionEngine.has("account:create")?
AuthV-->>Svc: Allowed / Denied
Svc->>Store: Insert account
Store-->>Svc: Created account
Svc-->>Handler: Account object
Handler-->>Client: { success: true, data: { ... } } Built-in Permission Constants¶
The system defines these permission constants that services require for CRUD operations:
| Category | Permission | Action |
|---|---|---|
| Account | account:readSelf | Read own profile |
| Account | account:updateSelf | Update own profile |
| Account | account:deleteSelf | Delete own account |
| Account | account:readAny | Read any account |
| Account | account:updateAny | Update any account |
| Account | account:deleteAny | Delete any account |
| Workspace | workspace:list | List workspaces |
| Workspace | workspace:create | Create workspace |
| Workspace | workspace:read | Read workspace |
| Workspace | workspace:update | Update workspace |
| Workspace | workspace:delete | Delete workspace |
| Project | project:list | List projects |
| Project | project:create | Create project |
| Project | project:read | Read project |
| Project | project:update | Update project |
| Project | project:delete | Delete project |
| Membership | membership:list | List memberships |
| Membership | membership:invite | Invite member |
| Membership | membership:updateStatus | Change membership status |
| Membership | membership:manageRole | Assign/remove roles |
| Membership | membership:delete | Remove member |
| Membership | membership:readSelf | Read own memberships |
| Role | role:list | List roles |
| Role | role:create | Create role |
| Role | role:update | Update role |
| Role | role:delete | Delete role |
| Permission | permission:read | Read permissions |
| Permission | permission:manageConfig | Create/update/delete permissions |
| Credential | credential:manageSelf | Manage own credentials |
| Credential | credential:resetAny | Reset any credential |
| Token | token:revokeSelf | Revoke own tokens |
Token Revocation¶
Token revocation is session-version-based. There is no blacklist database, no JWT hash table, and no /auth/blacklist endpoint — revocation is purely version/cache based.
Every token carries mem_ver and acc_ver claims (the membership and account token versions) plus a sid (session ID) for session-bound tokens. On every authenticated request, the CtxMiddleware loads the cached auth data for the membership, account, and session, then validates the token against it:
claims.mem_vermust match the cached membership token versionclaims.acc_vermust match the cached account token versionclaims.sidmust match the cached session (if the cached entity carries a session)
If any of these mismatch, the token fails validation with 401 Unauthorized. There is no blacklist lookup — the version comparison is the revocation check.
Revoking a Token¶
Revocation is performed via POST /auth/revoke (documented in the Auth API). The handler decodes the bearer token to recover its claims, verifies the caller owns the token, then increments the session version and purges the membership/account auth-cache keys from Redis. No hash computation and no database write are involved.
When a token is revoked, the session version is incremented and the auth cache is purged. All subsequent requests with the previously-issued tokens fail validation because their cached version claims no longer match.
Workspace Scoping¶
Every authenticated request operates within a workspace context. OxideAuth resolves the target workspace using one of two mechanisms:
1. Token-Scoped Workspace (Standard)¶
For standard user tokens, the workspace ID is embedded in the JWT as the ws claim. The CtxMiddleware extracts the workspace directly from the token, and the service layer enforces that:
- The requested workspace exists
- The caller has
workspace:describepermission on it - The caller has the specific CRUD permission for the operation
No additional header is required — the workspace is determined entirely by the token.
2. Global-Scope Tokens with X-Workspace-Id Header¶
Global (root) tokens operate across all workspaces. Because the token itself does not specify a target workspace, the client must explicitly provide one via the X-Workspace-Id HTTP header.
| Header | Required For | Description |
|---|---|---|
X-Workspace-Id | Global tokens only | UUID of the workspace to operate on |
Behavior:
- If the token is global/root scoped and the
X-Workspace-Idheader is present, the middleware overrides the scoped workspace with the provided UUID. - If the token is global/root scoped and the
X-Workspace-Idheader is missing, the API returns401 Unauthorized: - If the token is workspace-scoped, the
X-Workspace-Idheader is ignored — the token's embedded workspace always takes precedence.
Example: Using Global Tokens¶
POST /accounts/create HTTP/1.1
Authorization: Bearer <global_token>
X-Workspace-Id: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json
{
"email": "alice@example.com",
"password": "SecureP@ssw0rd!",
"name": "Alice Johnson"
}
Tenant Isolation¶
This dual-mode scoping ensures strict tenant isolation — users in one workspace cannot access resources in another. Global tokens are intended for administrative and cross-tenant operations only.