Single sign-on
OIDC against your own directory, configured in-product rather than in a file, with MFA that vitrinka verifies rather than assumes.
Configure it
Settings → Organization → SSO. Enter your issuer, client id and secret, and map your IdP's groups to roles. Two things help before you commit: Check health probes your issuer, and test sign-in shows exactly which claims arrive.
- Entra ID, Keycloak, Okta — all three are in use, but no per-provider walkthrough is written yet — ask, and you will get the exact issuer, scopes and claim mapping for yours.
- Group-to-role mapping — reapplied at every login, so revoking a group in the directory revokes access here.
Forced SSO
Turn it on to require SSO for everyone. There is a documented break-glass escape hatch at /auth/login?local=1 for when your IdP is down, and every use of it is audit-logged.
MFA is verified, not assumed
MFA is enforced at your IdP — Entra conditional access, Keycloak required actions — and checked here. Turn on Require MFA and the server refuses any sign-in whose id_token does not assert a multi-factor amr claim.
The point is that a silently relaxed IdP policy fails loudly instead of quietly dropping the second factor. Entra v2.0 tokens need the amr optional claim configured on the app registration; v1.0 emits it by default.
Break-glass is passkey-first
When the IdP is down, an account with a registered passkey must sign in with it. The password form only works for accounts that never enrolled one, and that weaker fallback writes its own audit row.
Enrol a passkey for at least one admin before you need the emergency path.
Session and header posture
Every response carries a baseline header set — nosniff, frame-ancestors 'self', Referrer-Policy, Permissions-Policy — and HSTS is added automatically when BASE_URL is https.
| Setting | Default | Override |
|---|---|---|
| Absolute session lifetime | 12 hours | VITRINKA_SESSION_TTL |
| Idle timeout | 30 minutes | VITRINKA_SESSION_IDLE |
Both are enforced server-side. Combine them with forced SSO so your IdP's conditional-access policy gates every sign-in.