Skip to content

Auth: config-selected pluggable password-factor provider (TIGER-242 core seam) - #315

Merged
WebTigers merged 1 commit into
mainfrom
feat/auth-credential-adapter
Sep 26, 2026
Merged

WebTigers merged 1 commit into
mainfrom
feat/auth-credential-adapter

Conversation

@WebTigers

Copy link
Copy Markdown
Owner

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_Mail transport / Tiger_Log sinks.

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.provider names the adapter; unset or db → no adapter, so every ordinary install runs the existing DB path unchanged. Modules register by name (::register(), like Tiger_Location).
  • Tiger_Auth_Credential_Adapter_Abstract — appliesTo() / verify() (+ optional isLockedOut/recordFailure/recordSuccess). A provider chain: an adapter owns only the users it appliesTo(); 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 only appliesTo() users (chain fall-through); unregistered name → DB; throwing adapter → DB.

Follow-on (TigerServer, after this releases)

The server adapter (verify via the loopback agent → crypt-compare /etc/shadow, owner-only) + injecting tiger.auth.credential.provider=server into account installs. Part of TIGER-242; account installs need the released core version first.

🤖 Generated with Claude Code

https://claude.ai/code/session_01ASauLLscjqdsNqBNsx2Typ

…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
@WebTigers
WebTigers merged commit a070576 into main Sep 26, 2026
14 checks passed
@WebTigers
WebTigers deleted the feat/auth-credential-adapter branch September 26, 2026 16:38
@WebTigers WebTigers mentioned this pull request Sep 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant