Auth: config-selected pluggable password-factor provider (TIGER-242 core seam) - #315
Merged
Merged
Conversation
…2 core seam) The password check becomes a provider-agnostic, config-driven adapter — the same house pattern as Tiger_Location / Tiger_Mail transport / Tiger_Log sinks — so a deployment can point the password factor at an authority other than the DB user_credential. The motivating case (TIGER-242): TigerServer will verify an account owner's web login against the OS/system credential, so an account has ONE password (the operator's chpasswd reset governs the panel login too; drift becomes impossible). Core half (this PR): - Tiger_Auth_Credential — a registry + config selector. tiger.auth.credential.provider names the adapter; UNSET or 'db' resolves to NO adapter, so every ordinary install runs the existing DB path entirely unchanged. Modules register an adapter by name (Tiger_Auth_Credential::register(), like Tiger_Location::register()). - Tiger_Auth_Credential_Adapter_Abstract — appliesTo()/verify() (+ optional isLockedOut/recordFailure/ recordSuccess). A PROVIDER CHAIN: the adapter owns only the users it appliesTo(); a declined user (e.g. an invited non-owner) falls back to the DB. Fail-safe: an unregistered name or a throwing adapter resolves to null (DB), never breaks login. - Wired into Tiger_Service_Authentication::login() and unlock(): when a provider owns the user, the password factor verifies there and the DB-credential block is skipped; otherwise the DB path is byte-for-byte as before. TOTP/2FA, brute-force lockout, login audit, and session issuance are unchanged and shared by both paths (only the password factor is pluggable). Tests: tests/Unit/Auth/CredentialTest.php — unset/db → DB path; 'db'/'' can't be registered; a configured adapter owns only appliesTo() users (chain fall-through); unregistered name → DB; throwing adapter → DB (fail-safe). 5 pass locally. The TigerServer 'server' adapter (verify via the loopback agent → crypt-compare /etc/shadow, owner-only) + injecting tiger.auth.credential.provider=server into account installs land in TigerServer once this core seam is released (account installs need the core version that carries it). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ASauLLscjqdsNqBNsx2Typ
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The core seam for TIGER-242 — "one account password" — as a config-selected, provider-agnostic password-factor adapter, the same house pattern as
Tiger_Location/Tiger_Mailtransport /Tiger_Logsinks.Why
On TigerServer an account has two password stores that drift: the Linux system user (SSH; reset via the operator "Reset account password" →
chpasswd) and the account's own Tiger web-admin login. Resetting one doesn't touch the other. The fix (Beau's call) is to make the account's auth verify the owner against the system credential — one password, no drift. That needs a core seam so a deployment can point the password factor at another authority; this PR is that seam.What
Tiger_Auth_Credential— registry + config selector.tiger.auth.credential.providernames the adapter; unset ordb→ no adapter, so every ordinary install runs the existing DB path unchanged. Modules register by name (::register(), likeTiger_Location).Tiger_Auth_Credential_Adapter_Abstract—appliesTo()/verify()(+ optionalisLockedOut/recordFailure/recordSuccess). A provider chain: an adapter owns only the users itappliesTo(); a declined user (an invited non-owner) falls back to DB. Fail-safe — an unregistered name or a throwing adapter resolves to null (DB), never breaks login.Tiger_Service_Authentication::login()+unlock()— when a provider owns the user, the password factor verifies there and the DB block is skipped; otherwise the DB path is byte-for-byte as before. TOTP/2FA, brute-force lockout, login audit and session issuance are unchanged and shared — only the password factor is pluggable.Tests
tests/Unit/Auth/CredentialTest.php(5, pass locally): unset/db→ DB path;db/''can't be registered; a configured adapter owns onlyappliesTo()users (chain fall-through); unregistered name → DB; throwing adapter → DB.Follow-on (TigerServer, after this releases)
The
serveradapter (verify via the loopback agent → crypt-compare/etc/shadow, owner-only) + injectingtiger.auth.credential.provider=serverinto account installs. Part of TIGER-242; account installs need the released core version first.🤖 Generated with Claude Code
https://claude.ai/code/session_01ASauLLscjqdsNqBNsx2Typ