Documentation
¶
Overview ¶
Package stores provides Redis-backed, short-lived record stores for security-sensitive authentication flows: password reset, email verification, and MFA login challenges.
Design ¶
Each store persists a versioned, binary-encoded record in Redis with a TTL. Mutation operations (Consume, RecordFailure) use WATCH/MULTI optimistic transactions with automatic retry on contention. Records are single-use: consumed or deleted on success, and enforce attempt limits to resist brute-force attacks. Secret comparisons use constant-time compare.
Architecture boundaries ¶
This package owns persistence and concurrency control for transient challenge records. It does NOT generate tokens/OTPs, enforce rate limits, or make authentication decisions — those responsibilities belong to the flow functions in internal/flows.
What this package must NOT do ¶
- Import goAuth or any sibling internal package.
- Log or expose plaintext secrets.
- Use non-constant-time comparisons for secret matching.
Index ¶
- Constants
- Variables
- type EmailVerificationRecord
- type EmailVerificationStore
- type MFALoginChallenge
- type MFALoginChallengeStore
- func (s *MFALoginChallengeStore) Delete(ctx context.Context, challengeID string) (bool, error)
- func (s *MFALoginChallengeStore) Get(ctx context.Context, challengeID string) (*MFALoginChallenge, error)
- func (s *MFALoginChallengeStore) RecordFailure(ctx context.Context, challengeID string, maxAttempts int) (bool, error)
- func (s *MFALoginChallengeStore) Save(ctx context.Context, challengeID string, record *MFALoginChallenge, ...) error
- type PasswordResetRecord
- type PasswordResetStore
- func (s *PasswordResetStore) Consume(ctx context.Context, tenantID, resetID string, providedHash [32]byte, ...) (*PasswordResetRecord, error)
- func (s *PasswordResetStore) Get(ctx context.Context, tenantID, resetID string) (*PasswordResetRecord, error)
- func (s *PasswordResetStore) Save(ctx context.Context, tenantID, resetID string, record *PasswordResetRecord, ...) error
- type WebAuthnSession
- type WebAuthnSessionStore
- func (s *WebAuthnSessionStore) Consume(ctx context.Context, ceremonyID string) (*WebAuthnSession, error)
- func (s *WebAuthnSessionStore) Delete(ctx context.Context, ceremonyID string) error
- func (s *WebAuthnSessionStore) Save(ctx context.Context, ceremonyID string, record *WebAuthnSession, ...) error
Constants ¶
const ( WebAuthnPurposeRegistration byte = 1 WebAuthnPurposeLogin byte = 2 )
WebAuthn ceremony purposes. A session saved for one purpose must never be consumable for the other.
Variables ¶
var ( ErrVerificationNotFound = errors.New("verification record not found") ErrVerificationSecretMismatch = errors.New("verification secret mismatch") ErrVerificationAttemptsExceeded = errors.New("verification attempts exceeded") )
var ( ErrMFALoginChallengeNotFound = errors.New("mfa challenge not found") ErrMFALoginChallengeExpired = errors.New("mfa challenge expired") ErrMFALoginChallengeExceeded = errors.New("mfa challenge attempts exceeded") ErrMFALoginChallengeBackend = errors.New("mfa challenge backend unavailable") )
var ( ErrResetNotFound = errors.New("reset record not found") ErrResetSecretMismatch = errors.New("reset secret mismatch") ErrResetAttemptsExceeded = errors.New("reset attempts exceeded") )
var ( ErrWebAuthnSessionNotFound = errors.New("webauthn ceremony session not found") ErrWebAuthnSessionBackend = errors.New("webauthn ceremony backend unavailable") )
Functions ¶
This section is empty.
Types ¶
type EmailVerificationRecord ¶
type EmailVerificationStore ¶
type EmailVerificationStore struct {
// contains filtered or unexported fields
}
func NewEmailVerificationStore ¶
func NewEmailVerificationStore(redisClient redis.UniversalClient, prefix string) *EmailVerificationStore
func (*EmailVerificationStore) Consume ¶
func (s *EmailVerificationStore) Consume( ctx context.Context, tenantID, verificationID string, providedHash [32]byte, expectedStrategy int, maxAttempts int, ) (*EmailVerificationRecord, error)
func (*EmailVerificationStore) Save ¶
func (s *EmailVerificationStore) Save( ctx context.Context, tenantID, verificationID string, record *EmailVerificationRecord, ttl time.Duration, ) error
type MFALoginChallenge ¶
type MFALoginChallengeStore ¶
type MFALoginChallengeStore struct {
// contains filtered or unexported fields
}
func NewMFALoginChallengeStore ¶
func NewMFALoginChallengeStore(redisClient redis.UniversalClient, prefix string) *MFALoginChallengeStore
func (*MFALoginChallengeStore) Get ¶
func (s *MFALoginChallengeStore) Get(ctx context.Context, challengeID string) (*MFALoginChallenge, error)
func (*MFALoginChallengeStore) RecordFailure ¶
func (*MFALoginChallengeStore) Save ¶
func (s *MFALoginChallengeStore) Save( ctx context.Context, challengeID string, record *MFALoginChallenge, ttl time.Duration, ) error
type PasswordResetRecord ¶
type PasswordResetStore ¶
type PasswordResetStore struct {
// contains filtered or unexported fields
}
func NewPasswordResetStore ¶
func NewPasswordResetStore(redisClient redis.UniversalClient, prefix string) *PasswordResetStore
func (*PasswordResetStore) Consume ¶
func (s *PasswordResetStore) Consume( ctx context.Context, tenantID, resetID string, providedHash [32]byte, expectedStrategy int, maxAttempts int, ) (*PasswordResetRecord, error)
func (*PasswordResetStore) Get ¶
func (s *PasswordResetStore) Get(ctx context.Context, tenantID, resetID string) (*PasswordResetRecord, error)
func (*PasswordResetStore) Save ¶
func (s *PasswordResetStore) Save( ctx context.Context, tenantID, resetID string, record *PasswordResetRecord, ttl time.Duration, ) error
type WebAuthnSession ¶ added in v0.4.0
WebAuthnSession is a pending WebAuthn ceremony: the library session data (challenge et al.) bound to the user and purpose it was begun for.
type WebAuthnSessionStore ¶ added in v0.4.0
type WebAuthnSessionStore struct {
// contains filtered or unexported fields
}
WebAuthnSessionStore persists pending ceremony sessions in Redis with a TTL. Sessions are single-use: Consume atomically reads and deletes.
func NewWebAuthnSessionStore ¶ added in v0.4.0
func NewWebAuthnSessionStore(redisClient redis.UniversalClient, prefix string) *WebAuthnSessionStore
func (*WebAuthnSessionStore) Consume ¶ added in v0.4.0
func (s *WebAuthnSessionStore) Consume(ctx context.Context, ceremonyID string) (*WebAuthnSession, error)
Consume atomically fetches and deletes the ceremony session, enforcing single use: a replayed ceremony ID observes ErrWebAuthnSessionNotFound.
func (*WebAuthnSessionStore) Delete ¶ added in v0.4.0
func (s *WebAuthnSessionStore) Delete(ctx context.Context, ceremonyID string) error
Delete removes a pending ceremony session (best-effort cleanup).
func (*WebAuthnSessionStore) Save ¶ added in v0.4.0
func (s *WebAuthnSessionStore) Save( ctx context.Context, ceremonyID string, record *WebAuthnSession, ttl time.Duration, ) error