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

51 lines
2.0 KiB
Go
Raw Normal View History

2026-05-20 13:03:59 +00:00
package repository
import (
"context"
"time"
)
// TOTPProfileRecord captures the persisted TOTP state for a member.
//
// SecretCipher is the AES-GCM blob; BackupCodesHash holds bcrypt hashes of
// the plaintext backup codes (single-use).
type TOTPProfileRecord struct {
Enrolled bool
SecretCipher []byte
BackupCodesHash []string
EnrolledAt int64
}
// TOTPProfileRepository persists per-member TOTP secrets and backup codes.
//
// Implementations must be safe to call concurrently for distinct (tenant, uid)
// pairs; concurrent calls for the same member should be linearised by the
// caller (the usecase layer) before mutation.
type TOTPProfileRepository interface {
Get(ctx context.Context, tenantID, uid string) (*TOTPProfileRecord, error)
Save(ctx context.Context, tenantID, uid string, rec *TOTPProfileRecord) error
Clear(ctx context.Context, tenantID, uid string) error
// ConsumeBackupCode removes the supplied backup-code hash atomically and
// returns true when removal succeeded.
ConsumeBackupCode(ctx context.Context, tenantID, uid, hash string) (bool, error)
// ReplaceBackupCodes overwrites the backup-code hashes in one shot.
ReplaceBackupCodes(ctx context.Context, tenantID, uid string, hashes []string) error
}
// TOTPEnrollStore stages a freshly generated secret until the user confirms
// the first code. The blob is short-lived (Redis TTL).
type TOTPEnrollStore interface {
Save(ctx context.Context, tenantID, uid string, cipher []byte, ttl time.Duration) error
Get(ctx context.Context, tenantID, uid string) ([]byte, error)
Delete(ctx context.Context, tenantID, uid string) error
}
// TOTPReplayStore prevents replay of a successful TOTP code within its
// allowed verification window.
type TOTPReplayStore interface {
// MarkUsed returns true when the key was newly recorded (i.e. the code
// had not been used yet); false means it was already consumed.
MarkUsed(ctx context.Context, tenantID, uid string, timestep uint64, ttl time.Duration) (bool, error)
}