-
Notifications
You must be signed in to change notification settings - Fork 0
API Authentication
title: "API — Authentication" category: "api" version: "1.0" last_updated: "2024-01-15" standards: ["NIST-SP-800-53", "FSTEC"] related_pages: ["API-Overview", "API-MFA", "Security-Authentication-Model", "Database-Identity-DB"] ai_summary: "VaultFlower authentication API endpoints. POST /auth/login (Kerberos SSO → JWT pair), POST /auth/refresh (refresh access token), POST /auth/logout (revoke current session), POST /auth/logout-all (revoke all sessions), GET /auth/me (current user context). JWT Access Token TTL 30min, Refresh Token TTL 2h. Vault Blocklist for instant revocation."
https://vfw-core.contoso.com/api/v1/auth/
Authentication endpoints do not include {tenant-slug} in the path. Tenant context is established during login via the tenant_slug field in the request body.
Authenticate via Kerberos SSO and receive JWT token pair.
POST /api/v1/auth/login
Content-Type: application/json
X-Request-ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890{
"tenant_slug": "acme-corp",
"kerberos_token": "YIIFjDCCA3igAwIBBaEDAgEWooIE..."
}| Field | Type | Required | Description |
|---|---|---|---|
tenant_slug |
string | Yes | Tenant identifier from URL (e.g. acme-corp) |
kerberos_token |
string | Yes | Base64-encoded Kerberos service ticket from browser |
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 1800,
"refresh_expires_in": 7200,
"user": {
"id": "f1e2d3c4-b5a6-7890-fedc-ba0987654321",
"upn": "kozlov@contoso.com",
"display_name": "Kozlov Ivan",
"tenant_slug": "acme-corp",
"roles": ["Operator"],
"mfa_enrolled": true,
"mfa_methods": ["password"],
"session_expires_at": "2024-01-15T10:00:00Z"
}
}| Field | Description |
|---|---|
access_token |
JWT for API calls (TTL: 30 minutes) |
refresh_token |
JWT for token refresh (TTL: 2 hours) |
token_type |
Always Bearer
|
expires_in |
Access token TTL in seconds (1800 = 30 min) |
refresh_expires_in |
Refresh token TTL in seconds (7200 = 2 hours) |
user.mfa_enrolled |
Whether user has set up VaultFlower password |
user.mfa_methods |
Active MFA methods (password, totp, webauthn, smartcard) |
{
"error": {
"code": "KERBEROS_INVALID",
"message": "Kerberos ticket validation failed",
"request_id": "a1b2c3d4-..."
}
}{
"error": {
"code": "ACCOUNT_LOCKED",
"message": "Account is locked. Contact your administrator.",
"request_id": "a1b2c3d4-..."
}
}Possible codes: ACCOUNT_LOCKED, ACCOUNT_INACTIVE, TENANT_INACTIVE, USER_NOT_FOUND
Note: Error messages are intentionally vague — never reveal whether the user exists or why specifically access is denied.
{
"error": {
"code": "TENANT_NOT_FOUND",
"message": "Tenant not found",
"request_id": "a1b2c3d4-..."
}
}| Event | Severity | Trigger |
|---|---|---|
auth.login_success |
INFO | Login successful |
auth.login_failed |
WARN | Kerberos valid but user not found |
auth.account_locked |
WARN | is_locked = TRUE |
auth.account_inactive |
WARN | is_active = FALSE |
If user has never set VaultFlower password:
Response 200 with:
user.mfa_enrolled: false
Client must redirect to password setup:
POST /api/v1/{tenant}/mfa/password/enroll
Until password is set:
User cannot perform checkout operations
User can only access: /auth/me, /mfa/password/enroll
Obtain a new access token using the refresh token.
POST /api/v1/auth/refresh
Content-Type: application/json
X-Request-ID: b2c3d4e5-f6a7-8901-bcde-f01234567890
Cookie: refresh_token={httpOnly cookie set on login}{
"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
}Note: Refresh token can be sent either as httpOnly cookie (preferred — browser handles automatically) or in request body (for non-browser clients).
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 1800,
"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_expires_in": 7200
}Token rotation: A new refresh token is issued on every refresh call. The old refresh token is added to the Vault Blocklist immediately.
{
"error": {
"code": "REFRESH_TOKEN_INVALID",
"message": "Refresh token is invalid or expired",
"request_id": "b2c3d4e5-..."
}
}Codes: REFRESH_TOKEN_INVALID, REFRESH_TOKEN_EXPIRED, REFRESH_TOKEN_REVOKED
| Event | Severity | Trigger |
|---|---|---|
auth.token_refreshed |
INFO | Refresh successful |
Revoke current session tokens. Both access and refresh tokens are added to Vault Blocklist.
POST /api/v1/auth/logout
Authorization: Bearer {access_token}
X-Request-ID: c3d4e5f6-a7b8-9012-cdef-012345678901No request body required.
No body. Tokens are immediately invalidated via Vault Blocklist.
On logout:
1. access_token.jti → Vault Blocklist (TTL: remaining token lifetime)
2. refresh_token.jti → Vault Blocklist (TTL: remaining token lifetime)
3. httpOnly cookie cleared
4. User cannot make authenticated requests with these tokens
Immediate effect:
Any in-flight request with the same token → 401 Unauthorized
Even if token has not expired yet
| Event | Severity | Trigger |
|---|---|---|
auth.token_revoked |
INFO | Logout successful |
Revoke ALL active sessions for the current user across all devices.
POST /api/v1/auth/logout-all
Authorization: Bearer {access_token}
X-Request-ID: d4e5f6a7-b8c9-0123-defa-123456789012No request body required.
All tokens for this user are immediately invalidated.
1. User suspects account compromise → logout all devices
2. Admin forces logout: POST /api/v1/{tenant}/users/{id}/force-logout
(Admin can force logout any user)
3. Security incident response
| Event | Severity | Trigger |
|---|---|---|
auth.token_revoked |
INFO | Per token revoked |
auth.force_logout |
CRITICAL | Admin forced logout (via admin endpoint) |
Return current authenticated user's context and permissions.
GET /api/v1/auth/me
Authorization: Bearer {access_token}
X-Request-ID: e5f6a7b8-c9d0-1234-efab-234567890123{
"id": "f1e2d3c4-b5a6-7890-fedc-ba0987654321",
"upn": "kozlov@contoso.com",
"display_name": "Kozlov Ivan",
"email": "kozlov@contoso.com",
"tenant_slug": "acme-corp",
"tenant_name": "ACME Corporation",
"is_active": true,
"is_locked": false,
"last_login_at": "2024-01-15T08:00:00Z",
"roles": [
{
"id": "uuid",
"name": "Operator",
"is_system": true,
"expires_at": null
}
],
"scopes": [
{
"id": "uuid",
"scope_type": "zone",
"scope_ref_id": "uuid",
"scope_name": "OT-DMZ-Север",
"expires_at": null
},
{
"id": "uuid",
"scope_type": "zone",
"scope_ref_id": "uuid",
"scope_name": "Control-Zone-Север",
"expires_at": null
}
],
"mfa_enrolled": true,
"mfa_methods": ["password", "smartcard"],
"session_expires_at": "2024-01-15T10:00:00Z",
"permissions": [
"checkout:request",
"checkout:approve",
"task:create",
"task:execute",
"credential:view"
]
}| Field | Description |
|---|---|
roles |
Active role assignments (expired roles excluded) |
scopes |
Active scope assignments with names |
mfa_methods |
Enrolled MFA methods available for checkout |
permissions |
Flattened permission list derived from roles |
session_expires_at |
When refresh token expires (full re-auth needed) |
GET /auth/me is called by the Blazor Portal on every page load to:
- Verify session is still valid
- Build role-based navigation menu
- Pre-populate user context for UI decisions
Also useful for API clients to verify current permissions
before attempting operations.
Force logout any user. Admin only.
POST /api/v1/acme-corp/users/f1e2d3c4-b5a6-7890-fedc-ba0987654321/force-logout
Authorization: Bearer {admin_access_token}
X-Request-ID: f6a7b8c9-d0e1-2345-fabc-345678901234{
"reason": "Security incident — suspected account compromise"
}All sessions for the target user are immediately invalidated.
| Event | Severity | Trigger |
|---|---|---|
auth.force_logout |
CRITICAL | Admin forced user logout |
{
"sub": "f1e2d3c4-b5a6-7890-fedc-ba0987654321",
"upn": "kozlov@contoso.com",
"tenant": "acme-corp",
"tenant_id": "uuid",
"roles": ["Operator"],
"jti": "unique-token-id",
"iat": 1705305600,
"exp": 1705307400,
"iss": "vaultflower",
"aud": "vaultflower-api"
}On every API request:
1. Decode JWT (no DB call needed for structure validation)
2. Verify RS256 signature
3. Check exp claim (expired = 401)
4. Check jti in Vault:
vault read vfw/tokens/blocklist/{jti}
Found = token revoked = 401
Not found = token valid
5. Proceed with authorization
- API-Overview — conventions, headers, error format
- API-MFA — MFA enrollment and verification
- Security-Authentication-Model — Kerberos SSO, JWT, Vault Blocklist
- Database-Identity-DB — users and sessions schema