feat(auth): TOTP second factor for the local provider - #779
feat(auth): TOTP second factor for the local provider#779Hashir-Ashraf-Awan wants to merge 1 commit into
Conversation
Adds optional TOTP (RFC 6238) on top of the local email/password provider, opt-in per account through ADMIN_TOTP_SECRET and USER_TOTP_SECRET. SSO already covered MFA by delegating to the identity provider (docs/OIDC.md); this closes the gap for deployments that authenticate locally. Verification is in-tree and dependency-free: HOTP is a truncated HMAC and base32 is a 32-character alphabet, so an OTP library would add supply-chain surface to the one code path that exists to raise the cost of a compromise. HMAC-SHA-1 is RFC 6238's default and the only algorithm authenticator apps interoperate on for a bare otpauth URI. Secrets are env vars rather than enrolled state: the chart and the image both run on a read-only filesystem, and a second factor that silently degraded when the data dir was unwritable would be worse than none. A value that is not base32 is an AuthConfigError, so a typo stops login with a 503 naming the variable instead of quietly dropping the factor or rejecting every correct code. Notable properties: - The password alone never creates a session, and an accepted code cannot be replayed inside its 90-second window (RFC 6238 5.2). Both are asserted in tests/security/mfa-second-factor.test.ts, now control 1.6 in docs/SECURITY.md. - Replying "code required" is not the enumeration oracle control 1.5 removes: it is reachable only with a correct password, and without MFA that same request would have returned a session. - Being asked for a code costs no rate-limit budget; a wrong code spends both buckets. login_client allows five failures per five minutes, so charging the prompt would cap legitimate users at five logins per window. - NEXT_PUBLIC_AUTH_PROVIDER=oidc changes what the login page renders, not what POST /api/auth/login accepts. An OIDC deployment that still sets ADMIN_PASSWORD keeps a route that never reaches the issuer; these variables are honoured in that mode too, which is how it is closed. Documented rather than assumed away. - The chart carries the secret in its Secret and references it from the pod, so it never lands in the Deployment spec the way extraEnv would. Both refs stay optional even in strict mode, so an unasked-for factor cannot block startup. Chart version bumped to 0.1.63 per the packaged-file rule (libredb#167); 0.1.62 is already released. Coverage is 100% on all four touched source files. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
| GitGuardian id | GitGuardian status | Secret | Commit | Filename | |
|---|---|---|---|---|---|
| - | - | Generic High Entropy Secret | 4444029 | tests/unit/lib/totp.test.ts | View secret |
| 36754989 | Triggered | Generic Password | 4444029 | tests/api/auth/login.test.ts | View secret |
🛠 Guidelines to remediate hardcoded secrets
- Understand the implications of revoking this secret by investigating where it is used in your code.
- Replace and store your secrets safely. Learn here the best practices.
- Revoke and rotate these secrets.
- If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.
To avoid such incidents in the future consider
- following these best practices for managing and storing secrets including API keys and other credentials
- install secret detection on pre-commit to catch secret before it leaves your machine and ease remediation.
🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.
Adds optional TOTP (RFC 6238) on top of the local email/password provider, opt-in per account through ADMIN_TOTP_SECRET and USER_TOTP_SECRET. SSO already covered MFA by delegating to the identity provider (docs/OIDC.md); this closes the gap for deployments that authenticate locally.
Verification is in-tree and dependency-free: HOTP is a truncated HMAC and base32 is a 32-character alphabet, so an OTP library would add supply-chain surface to the one code path that exists to raise the cost of a compromise. HMAC-SHA-1 is RFC 6238's default and the only algorithm authenticator apps interoperate on for a bare otpauth URI.
Secrets are env vars rather than enrolled state: the chart and the image both run on a read-only filesystem, and a second factor that silently degraded when the data dir was unwritable would be worse than none. A value that is not base32 is an AuthConfigError, so a typo stops login with a 503 naming the variable instead of quietly dropping the factor or rejecting every correct code.
Notable properties:
Chart version bumped to 0.1.63 per the packaged-file rule (#167); 0.1.62 is already released. Coverage is 100% on all four touched source files.
Description
Type of Change
Related Issue
Closes #
Changes Made
Testing
Test Environment
Screenshots (if applicable)
Checklist
bun run test:coverageandbun run coverage:check)src/lib/db/providers/, I updated the matchingdocs/providers/documentation andtests/integration/db/tests in the same PR (provider triad)Additional Notes