ADR-0004: Authenticate YourBOX Users Through Authentik OIDC With A Local Fallback
Status
Section titled “Status”Accepted
Context
Section titled “Context”YourBOX currently authenticates users against a local table with bcrypt password hashes and a “remember me” cookie. The remember me verification compares against two different SHA1 peppers hard coded in the source code, so the implementation has been broken for a long time. There is no second factor, no account lockout, no password reset rate limiting and no audit trail.
The homelab platform runs Authentik as the identity provider on the trusted admin plane. Every internal application is expected to delegate authentication to Authentik via OIDC so that single sign on works across the homelab. YourBOX follows this pattern.
A pure OIDC delegation has two issues for YourBOX:
- The application stores comments, votes and uploaded videos with foreign keys to a local user identifier. Removing the local user table would either denormalize ownership across every record or require synchronizing a shadow table anyway.
- Local development and the VPS smoke test should not require an Authentik instance to log in and exercise the application. A local Symfony login flow is needed for development speed.
Decision
Section titled “Decision”YourBOX uses a hybrid identity model. Authentik is the production identity provider via
OIDC. A minimal local user table mirrors authenticated subjects with the columns id,
oidc_sub, username, email, display_name, created_at, last_seen_at and role.
The first time a subject logs in via OIDC, the application creates the local record and
links it by oidc_sub.
In production the Symfony Security firewall stack uses the oauth2-client-bundle provider
configured for Authentik. Subsequent logins look up by oidc_sub and refresh
display_name, email and last_seen_at.
In development and on the VPS smoke test, a second firewall is enabled when the APP_ENV
environment variable equals dev or vps. That firewall exposes a Symfony form login
backed by an Argon2id password hash stored in the same local user table. The development
user table is seeded with two accounts (admin and user) by a Doctrine data fixture.
Both firewalls share the same User entity, the same Symfony voter for role checks and
the same session cookie name. The two firewalls are mutually exclusive at the URL pattern
level: /oidc/* belongs to the OIDC firewall, /login belongs to the form firewall.
Account lockout and rate limiting are delegated to Authentik in production. In development they are disabled, in line with how Symfony recommends scoping rate limiters by environment.
Consequences
Section titled “Consequences”- Comments, votes and video ownership keep referential integrity through the local user
table, with
oidc_subas the bridge to the identity provider. - Local development and the VPS smoke test do not require an Authentik instance. CI runs functional tests against the form firewall.
- Account recovery in production is handled by Authentik directly, so the YourBOX “forgot password” flow is removed and the corresponding routes deleted.
- Audit logging records the
oidc_suband theusernameon every privileged action, so rotating an Authentik account or merging two accounts can happen without losing history. - Adding a second identity provider later means adding a second OIDC client in Authentik, not a second firewall in YourBOX.