Skip to content

API Authentication

Maks Zaikin edited this page Jul 1, 2026 · 1 revision

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."

🔑 API — Authentication

Base Path

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.


POST /auth/login

Authenticate via Kerberos SSO and receive JWT token pair.

Request

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

Response 200 — Success

{
  "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)

Response 401 — Authentication Failed

{
  "error": {
    "code": "KERBEROS_INVALID",
    "message": "Kerberos ticket validation failed",
    "request_id": "a1b2c3d4-..."
  }
}

Response 403 — Account Issues

{
  "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.

Response 404 — Tenant Not Found

{
  "error": {
    "code": "TENANT_NOT_FOUND",
    "message": "Tenant not found",
    "request_id": "a1b2c3d4-..."
  }
}

SIEM Events

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

First Login Behavior

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

POST /auth/refresh

Obtain a new access token using the refresh token.

Request

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).

Response 200 — Success

{
  "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.

Response 401 — Refresh Failed

{
  "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

SIEM Events

Event Severity Trigger
auth.token_refreshed INFO Refresh successful

POST /auth/logout

Revoke current session tokens. Both access and refresh tokens are added to Vault Blocklist.

Request

POST /api/v1/auth/logout
Authorization: Bearer {access_token}
X-Request-ID: c3d4e5f6-a7b8-9012-cdef-012345678901

No request body required.

Response 204 — Success

No body. Tokens are immediately invalidated via Vault Blocklist.

Behavior

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

SIEM Events

Event Severity Trigger
auth.token_revoked INFO Logout successful

POST /auth/logout-all

Revoke ALL active sessions for the current user across all devices.

Request

POST /api/v1/auth/logout-all
Authorization: Bearer {access_token}
X-Request-ID: d4e5f6a7-b8c9-0123-defa-123456789012

No request body required.

Response 204 — Success

All tokens for this user are immediately invalidated.

Use Cases

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

SIEM Events

Event Severity Trigger
auth.token_revoked INFO Per token revoked
auth.force_logout CRITICAL Admin forced logout (via admin endpoint)

GET /auth/me

Return current authenticated user's context and permissions.

Request

GET /api/v1/auth/me
Authorization: Bearer {access_token}
X-Request-ID: e5f6a7b8-c9d0-1234-efab-234567890123

Response 200 — Success

{
  "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)

Usage

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.

POST /api/v1/{tenant}/users/{id}/force-logout

Force logout any user. Admin only.

Request

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"
}

Response 204 — Success

All sessions for the target user are immediately invalidated.

SIEM Events

Event Severity Trigger
auth.force_logout CRITICAL Admin forced user logout

JWT Structure

Access Token Claims

{
  "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"
}

Vault Blocklist Check

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

Related Pages

Clone this wiki locally