Skip to content

Add TOTP/MFA (Google Authenticator-style time-based OTP)#5

Merged
veverkap merged 3 commits into
mainfrom
copilot/add-totp-mfa-functionality
Apr 17, 2026
Merged

Add TOTP/MFA (Google Authenticator-style time-based OTP)#5
veverkap merged 3 commits into
mainfrom
copilot/add-totp-mfa-functionality

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 16, 2026

Implements RFC 6238 TOTP support with a TOTPStore interface for secret persistence, plus HTTP handlers covering the full enrollment and verification lifecycle.

auth package

  • TOTPStore interfaceCreateTOTPSecret, GetTOTPSecret, DeleteTOTPSecret; consuming apps implement against their DB layer
  • TOTPSecret typeID, UserID, Secret (unpadded base32; apps may store encrypted), CreatedAt
  • ErrTOTPNotFound / ErrInvalidTOTPCode sentinel errors
  • GenerateTOTPSecret() — 20-byte CSPRNG secret as unpadded base32
  • TOTPProvisioningURI(secret, accountName, issuer) — standard otpauth://totp/… URI for QR display
  • ValidateTOTP(secret, code) — HMAC-SHA1/RFC 4226 with ±1 step clock-skew window

handler package

TOTPHandler — router-agnostic, mirrors the existing handler patterns:

Endpoint Description
GET /totp/status {"enrolled": bool}
POST /totp/generate Returns {secret, provisioning_uri}; no DB write — secret is unconfirmed
POST /totp/enroll Accepts {secret, code}, verifies code, then persists
POST /totp/verify Validates a code against the stored secret
DELETE /totp Removes the enrolled secret

Enrollment is intentionally two-step: generate → user scans QR → enroll with first code, ensuring the authenticator is correctly configured before the secret is committed.

r.Get("/totp/status",    totpHandler.Status)
r.Post("/totp/generate", jwtMiddleware(totpHandler.Generate))
r.Post("/totp/enroll",   jwtMiddleware(totpHandler.Enroll))
r.Post("/totp/verify",   jwtMiddleware(totpHandler.Verify))
r.Delete("/totp",        jwtMiddleware(totpHandler.Disable))

- Add TOTPSecret type, TOTPStore interface, and ErrTOTPNotFound / ErrInvalidTOTPCode
  sentinel errors to auth/types.go
- Add auth/totp.go with GenerateTOTPSecret, TOTPProvisioningURI, ValidateTOTP (RFC 6238)
  and the internal hotpCode implementing RFC 4226
- Add auth/totp_test.go with 13 tests including RFC 4226 Appendix D vectors
- Add handler/totp.go with TOTPHandler (Status, Generate, Enroll, Verify, Disable)
- Add handler/totp_test.go with 20 tests covering all handler endpoints and error paths

Agent-Logs-Url: https://github.com/amalgamated-tools/goauth/sessions/505e8648-be11-46b2-b751-332f1be5ea2b

Co-authored-by: veverkap <22348+veverkap@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds RFC 6238 TOTP-based MFA support to the auth and handler packages, introducing secret generation/validation utilities plus HTTP endpoints and tests to support enrollment, verification, status checks, and disabling.

Changes:

  • Introduces auth-level TOTP primitives (GenerateTOTPSecret, TOTPProvisioningURI, ValidateTOTP) and HOTP implementation.
  • Adds TOTPStore/TOTPSecret types and new sentinel errors for store/verification flows.
  • Implements handler.TOTPHandler endpoints and comprehensive handler/unit tests for the lifecycle.
Show a summary per file
File Description
auth/types.go Adds TOTP store interface, secret model, and sentinel errors.
auth/totp.go Implements secret generation, provisioning URI creation, and TOTP validation.
auth/totp_test.go Adds unit coverage for TOTP/HOTP generation and validation behavior.
handler/totp.go Adds HTTP handlers for generate/enroll/verify/status/disable TOTP flows.
handler/totp_test.go Adds handler tests covering success/error paths across endpoints.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 5/5 changed files
  • Comments generated: 4

Comment thread handler/totp.go
Comment thread handler/totp.go
Comment thread auth/types.go Outdated
Comment thread auth/totp_test.go
@veverkap
Copy link
Copy Markdown
Contributor

/grumpy

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 17, 2026

😤 Fine. Grumpy Code Reviewer 🔥 finished the review. It wasn't completely terrible. I guess. 🙄

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TOTP implementation is structurally sound — RFC 4226 vectors pass, the enrollment flow is two-step, tests exist. But there are real problems that need fixing before this merges: no replay protection (a fundamental MFA requirement), leaking database/sql through the interface abstraction, copy-pasted crypto in tests, and a status handler that silently lies when the store errors. Fix those and I'll grudgingly admit it's adequate.

😤 Reluctantly reviewed by Grumpy Code Reviewer 🔥

To install this agentic workflow, run

gh aw add amalgamated-tools/biblioteka/.github/workflows/grumpy-reviewer.md@0dff8ccb0ced8d634877b4201f25795e659dced0

Comment thread auth/totp.go
Comment thread handler/totp.go
Comment thread auth/totp.go Outdated
Comment thread handler/totp.go Outdated
Comment thread handler/totp_test.go Outdated
- Lowercase ErrTOTPNotFound message to follow Go error string conventions
- Use math.Pow10(totpDigits) and %0*d in hotpCode to respect the constant
- Document replay-protection gap in ValidateTOTP warning comment
- Export GenerateTOTPCode for testing and tooling use
- Fix TestValidateTOTPPreviousStep to skip near step boundaries (flaky)
- Remove database/sql import from handler layer; check only auth.ErrTOTPNotFound
- Return 500 from Status on unexpected store errors instead of silently reporting enrolled=false
- Enforce minimum 20-byte decoded secret length in Enroll
- Replace copy-pasted currentCode() in handler tests with auth.GenerateTOTPCode
- Add TestTOTPStatusStoreError to cover the new 500 path
@veverkap veverkap requested a review from a team April 17, 2026 00:16
Kept TOTP sentinel errors (ErrTOTPNotFound, ErrInvalidTOTPCode) and
TOTP types/store alongside main's new ErrNotFound sentinel,
PasswordResetToken type, and PasswordResetStore interface.
Updated TOTPStore.GetTOTPSecret doc to drop stale sql.ErrNoRows reference.
@veverkap veverkap merged commit 2dfd695 into main Apr 17, 2026
1 check passed
@veverkap veverkap deleted the copilot/add-totp-mfa-functionality branch April 17, 2026 01:03
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.

3 participants