Skip to content

Authentication and Security

overthelex edited this page May 17, 2026 · 3 revisions

Authentication & Security

SecondLayer implements a multi-layered authentication system supporting web users, mobile app users, API clients, MCP protocol clients (ChatGPT, Claude Desktop), and national digital identity (Diia). The system uses a "Dual-Auth" strategy combining JWT/OAuth for interactive sessions and Bearer tokens for programmatic access.

Authentication Methods

1. Google OAuth

Primary authentication for web users:

  • OAuth 2.0 authorization code flow via Passport.js
  • Handles both registration and login
  • Returns JWT token for subsequent requests
  • Supports multiple callback hosts (legal.org.ua, platform.legal.org.ua, mcp.legal.org.ua, preview.legal.org.ua)
  • Mobile apps authenticate via POST /auth/google/mobile using idToken from Google Sign-In SDK
  • On registration, provisions user in Authentik (SSO) and Nextcloud

2. Email & Password

Standard credential-based authentication:

  • Registration with email + password (POST /auth/register)
  • Login with email + password (POST /auth/login)
  • Email verification flow (token sent via email)
  • Password reset flow (POST /auth/forgot-password, POST /auth/reset-password)
  • Account lockout after repeated failed login attempts (15-minute cooldown)
  • Passwords hashed with bcrypt

3. Diia (Дія.Підпис)

Ukrainian national digital identity integration:

  • Users authenticate via the Diia mobile app
  • Provides strong identity verification (equivalent to qualified electronic signature)
  • Callback routes: GET /auth/diia/callback (browser redirect) and POST /auth/diia/callback (API webhook)
  • Company registered as TOV "Lex EyAy" in Diia system
  • Supports mobile same-device flow (deep linking with session cookie)
  • Diia signing (Дія.Підпис): POST /auth/diia/sign initiates document signing sessions
  • Polling endpoint: GET /auth/diia/status/:sessionId
  • Creates users with null google_id (separate from OAuth users)

4. WebAuthn (Passkeys)

FIDO2/WebAuthn passwordless authentication:

  • Uses @simplewebauthn/server library
  • Supports both platform authenticators (fingerprint, Face ID) and cross-platform (security keys)
  • Registration requires existing JWT session (user adds passkey to their account)
  • Login via passkey is public (no existing session needed)
  • Challenge stored in Redis cache with 5-minute TTL
  • Credentials stored in database with friendly names, last-used timestamps
  • Users can list and delete their registered credentials
  • Multi-origin support for multi-domain setups

5. Authentik OIDC (SSO)

OpenID Connect integration with Authentik identity provider:

  • Authorization Code Flow with PKCE (code_challenge + code_verifier)
  • State stored in Redis for CSRF protection (10-minute TTL)
  • Used for SSO across Nextcloud and other Authentik-protected services
  • OIDC login route also supports direct password auth (POST /auth/oidc/login)
  • On registration, provisions user in Authentik and Nextcloud
  • Conditionally enabled when OIDC_DISCOVERY_URL and OIDC_CLIENT_ID are set

6. JWT Authentication

  • Used for authenticated web/mobile sessions after any login method
  • HS256 algorithm for token signing
  • Token includes user ID, email, role
  • Token refresh via POST /auth/refresh
  • Configured via JWT_SECRET environment variable

7. API Key Authentication

  • Bearer token for programmatic/API access
  • Two tiers:
    • Phase 2 billing API keys (stored in database, user-scoped, with rate limits and expiry)
    • Legacy environment keys stored in SECONDARY_LAYER_KEYS (comma-separated)
  • Used by MCP clients (Claude Desktop), external integrations, and SaaS developers
  • Format: Authorization: Bearer <key>
  • Timing-safe comparison for legacy key validation

8. OAuth 2.0 (MCP Protocol)

OAuth 2.0 Authorization Code Flow for ChatGPT and other MCP clients:

  • Authorization endpoint: GET /oauth/authorize
  • Token endpoint: POST /oauth/token
  • Tokens prefixed with mcp_token_ for identification
  • Used to authenticate SSE (Server-Sent Events) MCP connections
  • Supports PKCE (code_challenge, code_challenge_method)

Authentication Flow

sequenceDiagram
    participant Client
    participant Middleware as Dual-Auth Middleware
    participant Google as Google OAuth
    participant Password as Password Auth
    participant Diia as Diia Auth
    participant WebAuthn as WebAuthn/Passkey
    participant OIDC as Authentik OIDC
    participant JWT as JWT Service
    participant API as API Key Check
    participant OAuth as OAuth 2.0 (MCP)

    Client->>Middleware: Request with credentials
    alt Has JWT token (contains dots)
        Middleware->>JWT: Validate token (HS256)
        JWT-->>Middleware: User context
    else Has Bearer API key (no dots)
        Middleware->>API: Check Phase 2 keys (DB), then legacy keys (env)
        API-->>Middleware: API client context
    else Has mcp_token_ prefix
        Middleware->>OAuth: Verify OAuth access token
        OAuth-->>Middleware: User ID + scope
    else OAuth login
        Middleware->>Google: Authorization code
        Google-->>Middleware: User info + JWT
    else Password login
        Middleware->>Password: Email + password (bcrypt verify)
        Password-->>Middleware: User context + JWT
    else Diia login
        Middleware->>Diia: Auth request (deeplink + polling)
        Diia-->>Middleware: Signed identity + JWT
    else WebAuthn login
        Middleware->>WebAuthn: Challenge-response (FIDO2)
        WebAuthn-->>Middleware: Credential verification + JWT
    else OIDC login
        Middleware->>OIDC: Authorization code (PKCE)
        OIDC-->>Middleware: User info + JWT
    end
    Middleware->>Resource: Authenticated request
Loading

Authorization & Roles

The system uses a two-dimensional role model:

Role (account type)

Role Description
user Standard individual platform user
company Law firm / organization account
administrator Platform administrator (full access)

Role is stored in role column (migration 090). Constraint enforces valid values.

User Type (professional classification)

User Type Description
individual Regular user (default)
attorney Verified attorney with extended features
company_admin Administrator of a company account

Attorneys go through an onboarding flow with offer acceptance (POST /auth/accept-attorney-offer). Developers accept a separate developer offer (POST /auth/accept-developer-offer).

Access Gate (Paid Feature Control)

Access to paid features (chat, document upload) is controlled by the Access Gate. A user is allowed if ANY of:

  • User is an administrator
  • User has is_beta_tester = true
  • User has positive balance (UAH or USD)
  • User has at least one successful Monobank top-up

API key callers bypass the access gate (they authenticate as service accounts).

Security Measures

Data Encryption

  • Document vault supports client-side encryption
  • Retroactive encryption panel for migrating existing documents
  • TLS for all data in transit
  • Encryption at rest for sensitive database fields

Rate Limiting

  • Per-user and per-IP rate limits enforced at the middleware level
  • Redis-backed with in-memory fallback when Redis is unavailable
  • Separate rate limiters for auth endpoints, password reset, upload, and general API
  • Localhost/internal traffic (Prometheus, health monitors) exempted
  • Budget-aware limits tied to billing balance

Email Verification

  • Required for protected endpoints (file upload)
  • Enforcement via requireEmailVerified middleware (placed after auth middleware)
  • API key (B2B) clients bypass email verification requirement

Account Lockout

  • Failed login attempts are tracked per email
  • Account locked after threshold exceeded (15-minute cooldown)
  • Prevents brute-force attacks on password authentication

MCP Token Security

  • OAuth 2.0 access tokens (mcp_token_ prefix) provide scoped MCP protocol access
  • Separate from API keys -- issued via OAuth authorization code flow
  • Used for ChatGPT and Claude Desktop MCP connections
  • Tokens are verifiable and revocable via the OAuth service

Developer API Access

  • Developers building SaaS products get separate API keys (Phase 2 billing keys)
  • Must accept Developer Offer or API Terms before key generation
  • Developer contract tracked in database (migration 104)
  • Per-key rate limits (per-minute and per-day) stored in database
  • Key usage count and last-used timestamp tracked
  • Keys can have expiration dates

User Provisioning

  • On registration (any method), users are automatically provisioned in:
    • Authentik (SSO access to ecosystem services)
    • Nextcloud (file storage and collaboration)
  • Welcome bonus credited on first registration

Environment Variables

Variable Purpose
JWT_SECRET JWT signing secret (required, server refuses to start without it)
SECONDARY_LAYER_KEYS Comma-separated legacy API keys
GOOGLE_CLIENT_ID Google OAuth client ID
GOOGLE_CLIENT_SECRET Google OAuth client secret
DIIA_BASE_URL Diia API base URL
DIIA_ACQUIRER_TOKEN Diia acquirer token
DIIA_AUTH_ACQUIRER_TOKEN Diia auth acquirer token (enables Diia routes)
DIIA_BRANCH_ID Diia branch identifier
DIIA_OFFER_ID Diia offer identifier
OIDC_DISCOVERY_URL Authentik OIDC discovery URL (enables OIDC routes)
OIDC_CLIENT_ID OIDC client identifier
OIDC_CLIENT_SECRET OIDC client secret
OIDC_REDIRECT_URI OIDC redirect URI
AUTHENTIK_API_URL Authentik API URL for user provisioning
AUTHENTIK_API_TOKEN Authentik API token for provisioning
WEBAUTHN_RP_ID WebAuthn Relying Party ID (domain)
WEBAUTHN_RP_NAME WebAuthn Relying Party display name
WEBAUTHN_ORIGIN WebAuthn allowed origins (comma-separated for multi-domain)
ALLOWED_ORIGINS CORS allowed origins

Route Summary

Route Method Auth Description
/auth/login POST Public Email + password login
/auth/register POST Public Email + password registration
/auth/verify-email POST Public Email verification
/auth/forgot-password POST Public Request password reset
/auth/reset-password POST Public Reset password with token
/auth/google GET Public Initiate Google OAuth
/auth/google/callback GET Public Google OAuth callback
/auth/google/mobile POST Public Mobile Google Sign-In
/auth/oidc GET Public Initiate Authentik OIDC
/auth/oidc/callback GET Public OIDC callback
/auth/oidc/login POST Public OIDC direct password login
/auth/diia GET Public Initiate Diia auth
/auth/diia/callback POST/GET Public Diia webhook/redirect
/auth/diia/status/:id GET Public Poll Diia session
/auth/diia/sign POST JWT Initiate Diia signing
/auth/diia/sign/status/:id GET JWT Poll signing session
/auth/webauthn/register/options POST JWT Generate passkey registration challenge
/auth/webauthn/register/verify POST JWT Verify passkey registration
/auth/webauthn/auth/options POST Public Generate passkey login challenge
/auth/webauthn/auth/verify POST Public Verify passkey login
/auth/webauthn/credentials GET JWT List user credentials
/auth/webauthn/credentials/:id DELETE JWT Remove a credential
/auth/me GET JWT Current user profile
/auth/refresh POST JWT Refresh token
/auth/profile PUT JWT Update profile
/auth/logout POST JWT Logout
/auth/accept-attorney-offer POST JWT Accept attorney offer
/auth/accept-developer-offer POST JWT Accept developer offer
/auth/my-contracts GET JWT List signed contracts
/oauth/authorize GET Public OAuth 2.0 authorization (MCP)
/oauth/token POST Public OAuth 2.0 token exchange (MCP)

Clone this wiki locally