gh_UserManager/docs/authentication.md
bermooda-company 54d5891edf user
2026-08-23 23:59:14 +03:30

8.2 KiB

Authentication

Overview

Identity Platform implements OAuth 2.0 + OpenID Connect with RS256 asymmetric signing. The platform is the Authorization Server; products are Resource Servers / Relying Parties.

Token Flow (Authorization Code + PKCE)

┌──────────┐                          ┌──────────────────┐
│  Product │                          │ Identity Platform │
└────┬─────┘                          └────────┬─────────┘
     │                                             │
     │ 1. GET /.well-known/openid-configuration    │
     │◄────────────────────────────────────────────│
     │                                             │
     │ 2. Redirect user to /authorize              │
     │────────────────────────────────────────────►│
     │                                             │
     │ 3. User authenticates (login/MFA)           │
     │                                             │
     │ 4. User consents to scopes                  │
     │                                             │
     │ 5. Redirect back with ?code=...             │
     │◄────────────────────────────────────────────│
     │                                             │
     │ 6. POST /api/v1/auth/token (code + verifier)│
     │────────────────────────────────────────────►│
     │                                             │
     │ 7. { access_token, refresh_token, id_token }│
     │◄────────────────────────────────────────────│
     │                                             │
     │ 8. Product verifies access_token via JWKS   │
     │                                             │
     │ 9. API calls with Authorization: Bearer ... │

Endpoints

Endpoint Method Description
/.well-known/openid-configuration GET OIDC Discovery
/.well-known/jwks.json GET JWKS (public keys)
/api/v1/auth/login POST Username/password → tokens
/api/v1/auth/refresh POST Refresh token → new access
/api/v1/auth/logout POST Revoke session + refresh
/api/v1/users/me GET Current user info

Login Request

curl -X POST https://id.example.com/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"user@acme.com","password":"••••••••"}'

Response:

{
  "access": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "bearer",
  "expires_in": 900
}

Refresh Request

curl -X POST https://id.example.com/api/v1/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{"refresh":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."}'

Logout Request

curl -X POST https://id.example.com/api/v1/auth/logout \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{"refresh":"<refresh_token>"}'

Token Details

Access Token (JWT, RS256)

{
  "user_id": "uuid",
  "email": "user@acme.com",
  "org_id": "uuid",
  "scopes": ["openid", "profile", "org:read"],
  "session_id": "uuid",
  "token_type": "access",
  "exp": 1700000000,
  "iat": 1699999100,
  "iss": "https://id.example.com",
  "aud": "bermooda-client-id"
}
  • Lifetime: 15 minutes (configurable)
  • Signed by: Platform private key
  • Verified by: Product via JWKS

Refresh Token (JWT, RS256)

{
  "user_id": "uuid",
  "session_id": "uuid",
  "token_type": "refresh",
  "exp": 1702591100,
  "iat": 1699999100
}
  • Lifetime: 30 days (configurable)
  • Stored hashed in DB (not reversible)
  • Rotation: New refresh token issued on each use
  • Revocable: Instantly via /auth/logout or session revocation

ID Token (OIDC)

{
  "sub": "uuid",
  "email": "user@acme.com",
  "email_verified": true,
  "name": "User Name",
  "preferred_username": "user",
  "org_id": "uuid",
  "iat": 1699999100,
  "exp": 1700000000,
  "iss": "https://id.example.com",
  "aud": "bermooda-client-id"
}

JWKS (JSON Web Key Set)

curl https://id.example.com/.well-known/jwks.json
{
  "keys": [
    {
      "kty": "RSA",
      "use": "sig",
      "kid": "identity-platform-1",
      "alg": "RS256",
      "n": "...",
      "e": "AQAB"
    }
  ]
}

Products cache JWKS and rotate keys automatically.

Product Integration

1. Register Application

curl -X POST https://id.example.com/api/v1/applications \
  -H "Authorization: Bearer <admin_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Bermooda",
    "redirect_uris": ["https://bermooda.example.com/callback"],
    "grant_types": ["authorization_code", "refresh_token"],
    "response_types": ["code"],
    "scopes": ["openid", "profile", "email", "org:read"]
  }'

Response: client_id, client_secret (shown once).

2. Configure Product

# Product .env
OIDC_ISSUER=https://id.example.com
OIDC_CLIENT_ID=bermooda-client-id
OIDC_CLIENT_SECRET=••••••••
OIDC_REDIRECT_URI=https://bermooda.example.com/callback
OIDC_SCOPES=openid profile email org:read

3. Verify Tokens (Middleware Example)

# Python/DRF example
from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework_simplejwt.backends import TokenBackend

token_backend = TokenBackend(
    algorithm="RS256",
    signing_key=None,  # uses JWKS
    jwks_url="https://id.example.com/.well-known/jwks.json",
    audience="bermooda-client-id",
    issuer="https://id.example.com",
)

def verify_token(token: str) -> dict:
    return token_backend.decode(token, verify=True)

Security Features

Feature Implementation
RS256 Signing Private key in platform only
PKCE Required for public clients
Token Rotation New refresh token on each refresh
Session Revocation Instant, cascades to all tokens
Brute-force Protection Rate limit + auto-lockout (5 failed → 15min)
Audit Log Every auth event recorded
Device Tracking Fingerprint + IP + User-Agent

MFA (Roadmap)

  • TOTP (Google Authenticator, Authy)
  • WebAuthn / Passkeys (future)
  • Recovery codes
  • Per-user enrollment

Session Management

# List active sessions
GET /api/v1/sessions
Authorization: Bearer <access_token>

# Revoke session (current or other)
DELETE /api/v1/sessions/{session_id}
Authorization: Bearer <access_token>

Session object:

{
  "id": "uuid",
  "user": "uuid",
  "device_name": "Chrome on macOS",
  "ip_address": "192.168.1.1",
  "user_agent": "Mozilla/5.0...",
  "created_at": "2024-01-15T10:30:00Z",
  "last_activity": "2024-01-15T10:45:00Z",
  "status": "active",
  "type": "browser"
}

Rate Limits

Endpoint Limit
/auth/login 5/min per IP, 10/min per user
/auth/refresh 20/min per user
/auth/logout 10/min per user
API (general) 100/min per user

Exceeding → 429 Too Many Requests with Retry-After header.

Error Responses

{
  "error": "invalid_grant",
  "error_description": "Invalid or expired refresh token",
  "error_code": "TOKEN_EXPIRED"
}

Common OIDC errors: invalid_request, invalid_client, invalid_grant, unauthorized_client, unsupported_grant_type, invalid_scope.

Testing Checklist

  • Login returns access + refresh + id_token
  • Access token verified via JWKS
  • Refresh rotates token, old refresh revoked
  • Logout revokes session + refresh token
  • Expired access token → 401
  • Revoked session → 401 on API calls
  • Rate limit triggers 429
  • Failed login recorded in audit log
  • PKCE required for public clients