Security Features

Defence-in-depth capabilities of the Orion Identity Platform

Back to Documentation

FIDO2 / WebAuthn

Orion supports passwordless and second-factor authentication via the WebAuthn standard using the Fido2NetLib library.

Registration

  • Users navigate to Account Manager → Edit Account
  • Click "Register Security Key" to initiate a WebAuthn credential creation ceremony
  • The browser prompts for a hardware security key (USB, NFC) or platform authenticator (Touch ID, Windows Hello)
  • The public key credential is stored in the Orion database, linked to the user
  • An initial verification challenge confirms the key works before activation

Authentication

  • During login, if the user has an active FIDO2 authenticator, they are prompted after password entry
  • The server generates a challenge, the browser signs it with the registered credential
  • The server verifies the assertion against the stored public key

Configuration

# Set the Relying Party domain (must match the login domain)
ORION_FIDO2_DOMAIN="login.yourdomain.com"

# Set allowed origins (space or comma-separated absolute URIs)
ORION_FIDO2_ORIGINS="https://login.yourdomain.com"

TOTP (Time-Based One-Time Passwords)

Orion supports TOTP-based two-factor authentication compatible with apps like Google Authenticator, Authy, and 1Password.

Setup

  • Users navigate to Account Manager → Edit Account
  • Click "Register OTP" to generate a new TOTP secret
  • A QR code is displayed containing the otpauth:// URI
  • Scan the QR code with an authenticator app
  • Enter the current 6-digit code to verify and activate

During Login

If TOTP is active, after entering email and password, the user is prompted for a 6-digit code. The code is validated using a standard time-window tolerance (30-second steps with one step grace period).


Account Lockout

Orion protects against brute-force password attacks with configurable account lockout.

ORION_LOCKOUT_MAX_ATTEMPTS Maximum failed attempts before lockout. Default: 5
ORION_LOCKOUT_WINDOW_MINUTES Window in which attempts are counted. Default: 15 minutes
ORION_LOCKOUT_DURATION_MINUTES How long the account is locked. Default: 30 minutes

How it works:

  • Each failed login attempt increments a counter for the user
  • Only attempts within the configured window are counted
  • When the maximum is reached, the account is locked for the configured duration
  • Successful login resets the counter
  • Locked accounts receive a generic error message (no information leakage about lockout state)

PKCE (Proof Key for Code Exchange)

PKCE prevents authorization code interception attacks and is required by default for all OAuth clients in Orion. This follows the current best practice recommendation from RFC 7636 and the OAuth 2.1 draft specification.

How PKCE Works

// 1. Client generates a random code_verifier
code_verifier = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"

// 2. Client computes code_challenge = BASE64URL(SHA256(code_verifier))
code_challenge = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"

// 3. Authorization request includes code_challenge + method
GET /oauth/{org}/authorize?...&code_challenge=E9Mel...&code_challenge_method=S256

// 4. Token exchange includes code_verifier
POST /oauth/{org}/token
  grant_type=authorization_code&code=...&code_verifier=dBjft...

// 5. Server verifies: SHA256(code_verifier) == stored code_challenge

The RequirePkce setting on each application controls whether PKCE is mandatory. It defaults to true. Only disable it for legacy clients that cannot support PKCE.


Client Secret Rotation

Orion supports multiple active client secrets per application, enabling zero-downtime rotation:

1

Create a new secret

In the Application Manager, generate a new client secret with an optional label and expiry date.

2

Deploy the new secret

Update your application's configuration to use the new secret. Both old and new secrets are valid simultaneously.

3

Revoke the old secret

Once all instances are using the new secret, revoke the old one via the Management UI.

Security properties:

  • Secrets are stored as SHA-256 hashes — the plaintext is shown exactly once at creation
  • Expired secrets are automatically rejected during authentication
  • Revoked secrets are immediately invalidated
  • Each secret has a creation timestamp, optional expiry, and optional label for auditing

Token Signing & Key Management

All JWTs (access tokens and ID tokens) are signed using RS256 (RSA with SHA-256). Orion supports three key provider backends:

EnvironmentVariable

RSA key provided as a Base64 environment variable. Simplest option for single-server deployments.

Database

RSA key stored encrypted in PostgreSQL using an AES-256 Key Encryption Key (KEK). Supports key rotation.

AzureKeyVault

RSA key managed by Azure Key Vault. HSM-backed, supports automatic rotation, best for cloud deployments.

The JWKS endpoint (/oauth/{org}/.well-known/jwks.json) publishes the public key(s) so relying parties can validate tokens without calling Orion.


Redirect URI Validation

Redirect URI validation prevents open-redirect attacks during the OAuth flow.

  • Exact match: The redirect_uri in the authorization request must exactly match one of the registered URIs for the application.
  • Wildcard domains: The ORION_ALLOWED_REDIRECT_DOMAINS environment variable allows server-wide wildcard patterns (e.g. *.example.com).
  • No query strings: Redirect URIs must not contain query parameters in the registration.
  • HTTPS required: Production redirect URIs must use HTTPS. http://localhost is allowed for development.

Sovereign Signing

Sovereign Signing provides the highest level of token security by keeping JWT signing keys on external servers completely disconnected from Orion's HTTP traffic. Even with full database and server compromise, an attacker cannot forge tokens.

  • Private key isolation: The Orion server never possesses signing key material. Tokens are signed out-of-band by external agents.
  • Complete trust separation: Pool-specific JWKS endpoints serve only agent keys — server signing keys are never mixed in.
  • Cert chain validation: The SDK validates X.509 cert chains in JWKS against a trusted root held on an HSM. A compromised server cannot inject rogue keys without HSM access.
  • Thumbprint pinning: SHA-256 thumbprints of trusted key material prevent JWKS poisoning even without PKI infrastructure.
  • Embedded key mode: For air-gapped environments, the SDK can be configured with PEM public keys directly — no JWKS fetch, zero server trust.
  • Payload integrity: The server validates that the signed JWT payload is byte-identical to the unsigned payload it sent to the agent.
  • Agent challenges: Agents can issue interactive challenges to users before signing, enabling additional policy enforcement.

See the SDK Integration page for validation mode configuration.


Device Identity & TPM Attestation

Device Identity replaces static API keys with X.509 certificate-based authentication. Each organisation has its own CA; device certificates are issued via a one-time enrollment code.

  • Per-org Certificate Authority: CA private key encrypted at rest (AES-256-GCM under ORION_CA_KEK), separate from the OAuth signing KEK and the sovereign signing trust root.
  • Signed assertions: Devices authenticate via a short-lived JWT (private_key_jwt style) with the cert chain in x5c. Assertions carry jti for replay protection and a tight expiry (default 120s).
  • TPM attestation: Configurable at org/pool/enrollment level. When required, the device must prove (via MakeCredential/ActivateCredential/Certify) that its key is generated in and non-exportable from a genuine TPM. The EK certificate must chain to a trusted vendor root.
  • Instant revocation: Every assertion is validated against the database revocation denylist. No CRL/OCSP needed — Orion is the sole relying party.
  • PKI separation: The device CA is for transport auth only. It is never used or trusted for sovereign token signing, which has its own external trust root.

See the Device Identity Guide for step-by-step setup and TPM attestation details.

← Setup Next: API Reference →