Skip to main content

Authentication Flow

This document describes how authentication works across the SoftQuantus platform.

User Authentication (OIDC)

All user-facing applications use OIDC Code Flow with QSA as the identity provider.

sequenceDiagram
autonumber
participant U as User
participant PF as Portal Frontend
participant QSA as QSA (IdP)
participant PB as Portal Backend

U->>PF: Open portal / click login
PF->>QSA: OIDC Authorization Request<br/>(response_type=code)
QSA->>U: Show login page
U->>QSA: Enter credentials
QSA->>U: MFA challenge (if required)
U->>QSA: Complete MFA
QSA->>QSA: Evaluate Conditional Access
QSA-->>PF: Redirect with auth code
PF->>QSA: Exchange code for tokens<br/>(POST /oauth2/token)
QSA-->>PF: access_token + id_token + refresh_token
PF->>PB: API call with Bearer token
PB->>QSA: Fetch JWKS (cached)
PB->>PB: Validate JWT signature<br/>Check iss/aud/exp claims
PB-->>PF: Response + user context

JWT Claims Structure

{
"iss": "https://auth.softquantus.com",
"sub": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"aud": ["portal-backend", "admin-frontend"],
"exp": 1704369600,
"iat": 1704366000,
"tenant_id": "550e8400-e29b-41d4-a716-446655440000",
"roles": ["admin", "billing_manager"],
"permissions": ["read:customers", "write:contracts"],
"email": "user@example.com",
"name": "John Doe",
"mfa_verified": true
}

M2M Authentication (Service-to-Service)

Backend services use Client Credentials flow for M2M communication.

sequenceDiagram
autonumber
participant PB as Portal Backend
participant QSA as QSA (IdP)
participant QL as QuantumLock

PB->>QSA: POST /oauth2/token<br/>grant_type=client_credentials<br/>client_id=portal-backend<br/>scope=quantumlock:write
QSA->>QSA: Validate client credentials
QSA-->>PB: access_token (service principal)
PB->>QL: API call with Bearer token<br/>X-Service-Principal: portal-backend
QL->>QSA: Fetch JWKS (cached)
QL->>QL: Validate JWT + scopes
QL-->>PB: Response

Token Request (Client Credentials)

POST /oauth2/token HTTP/1.1
Host: auth.softquantus.com
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&
client_id=portal-backend&
client_secret=<secret>&
scope=quantumlock:write evidence:sign

Response:

{
"access_token": "eyJhbG...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "quantumlock:write evidence:sign"
}

Service Principals

ServiceClient IDScopes
Portal Backendportal-backendquantumlock:write, evidence:sign
Evidence Workerevidence-workerevidence:sign, quantumlock:verify
Admin Serviceadmin-servicequantumlock:admin, evidence:admin

OIDC Discovery

QSA publishes its configuration at the standard discovery endpoint:

GET /.well-known/openid-configuration
{
"issuer": "https://auth.softquantus.com",
"authorization_endpoint": "https://auth.softquantus.com/oauth2/authorize",
"token_endpoint": "https://auth.softquantus.com/oauth2/token",
"jwks_uri": "https://auth.softquantus.com/.well-known/jwks.json",
"userinfo_endpoint": "https://auth.softquantus.com/oauth2/userinfo",
"revocation_endpoint": "https://auth.softquantus.com/oauth2/revoke",
"response_types_supported": ["code", "token", "id_token"],
"grant_types_supported": ["authorization_code", "refresh_token", "client_credentials"],
"scopes_supported": ["openid", "profile", "email", "offline_access"],
"token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post"]
}

Token Validation

Backend services validate tokens by:

  1. Fetch JWKS from jwks_uri (with caching)
  2. Verify signature using the appropriate key
  3. Check claims:
    • iss matches expected issuer
    • aud includes the service
    • exp is in the future
    • iat is not in the future
  4. Extract context:
    • tenant_id for tenant isolation
    • roles/permissions for authorization
    • sub for user identification
# Example validation in Portal Backend
from jose import jwt, JWKClient

jwks_client = JWKClient("https://auth.softquantus.com/.well-known/jwks.json")

async def validate_token(token: str) -> dict:
signing_key = jwks_client.get_signing_key_from_jwt(token)

payload = jwt.decode(
token,
signing_key.key,
algorithms=["RS256", "ES256"],
audience="portal-backend",
issuer="https://auth.softquantus.com"
)

return payload

Security Considerations

Token Lifetime

Token TypeLifetimeRefresh
Access Token1 hourVia refresh token
Refresh Token7 daysSliding window
M2M Token1 hourRequest new token

Required Headers

Authorization: Bearer <access_token>
X-Correlation-Id: <uuid>
X-Tenant-Id: <tenant_id> # Set by backend after auth

Key Rotation

  • JWKS keys are rotated every 90 days
  • Old keys remain valid for 7 days after rotation
  • Services should cache JWKS with TTL of 1 hour