template-monorepo/internal/model/member/domain/usecase/totp.go

55 lines
2.3 KiB
Go
Raw Permalink Normal View History

2026-05-20 13:03:59 +00:00
package usecase
import "context"
// TOTPUseCase manages business-tier RFC 6238 TOTP enrollment and verification.
//
// The contract mirrors internal/model/member/SDD.md §3.5: enrollment is split
// in two steps (start → confirm) so the secret is only committed after the
// user proves possession; backup codes are returned exactly once on
// confirmation and replenished via RegenerateBackupCodes.
2026-05-20 13:03:59 +00:00
type TOTPUseCase interface {
// StartEnroll generates a fresh secret, stashes it in a short-lived cache,
// and returns the otpauth URL for QR rendering. Calling it twice replaces
// the staged secret.
StartEnroll(ctx context.Context, tenantID, uid, account string) (*EnrollStartDTO, error)
// ConfirmEnroll validates the first user-supplied code against the staged
// secret, persists the encrypted secret on the profile, and returns the
// plaintext backup codes (only returned here once).
ConfirmEnroll(ctx context.Context, tenantID, uid, code string) ([]string, error)
// VerifyCode validates a code (TOTP or backup) for step-up. Backup codes
// are single-use; replays of a successful TOTP code are rejected.
VerifyCode(ctx context.Context, tenantID, uid, code string) error
// Disable clears the secret and backup codes; intended to be guarded by a
// step-up token in the logic layer.
Disable(ctx context.Context, tenantID, uid string) error
// RegenerateBackupCodes replaces existing backup codes with a new batch.
RegenerateBackupCodes(ctx context.Context, tenantID, uid string) ([]string, error)
// Status reports the current enrollment state for UI/admin views.
Status(ctx context.Context, tenantID, uid string) (*TOTPStatusDTO, error)
}
// EnrollStartDTO is returned by TOTPUseCase.StartEnroll. Secret material is
// never returned in plaintext outside the otpauth URL.
type EnrollStartDTO struct {
OtpauthURL string `json:"otpauth_url"`
Issuer string `json:"issuer"`
Account string `json:"account"`
Digits int `json:"digits"`
PeriodSec int `json:"period_seconds"`
ExpiresIn int `json:"expires_in"`
}
// TOTPStatusDTO reports whether TOTP is active for the member and how many
// backup codes remain unused.
type TOTPStatusDTO struct {
Enrolled bool `json:"enrolled"`
EnrolledAt int64 `json:"enrolled_at,omitempty"`
BackupCodesRemaining int `json:"backup_codes_remaining"`
}