emailotp

package
v0.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 14, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Overview

Package emailotp provides email-based One-Time Password (OTP) authentication for go-modular-auth, supporting passwordless sign-in, email verification, password reset, and email change flows.

Index

Constants

View Source
const (
	// EventEmailOTPSendBefore is emitted right before dispatching an OTP to the recipient email.
	// Payload: *SendOTPPendingPayload
	EventEmailOTPSendBefore = "emailotp:send:before"

	// EventEmailOTPSent is emitted immediately after an OTP has been successfully dispatched.
	// Payload: *OTPSentPayload
	EventEmailOTPSent = "emailotp:send:after"

	// EventEmailOTPVerifyBefore is emitted right before verifying a submitted OTP code.
	// Payload: *VerifyBeforePayload
	EventEmailOTPVerifyBefore = "emailotp:verify:before"

	// EventEmailOTPVerified is emitted after an OTP code has been successfully verified.
	// Payload: *OTPVerifiedPayload
	EventEmailOTPVerified = "emailotp:verify:after"

	// EventEmailOTPSignInSuccess is emitted when a user successfully authenticates or registers via OTP.
	// Payload: *SignInSuccessPayload
	EventEmailOTPSignInSuccess = "emailotp:sign_in:success"

	// EventEmailOTPPasswordReset is emitted when a user resets their password using an OTP.
	// Payload: *PasswordResetPayload
	EventEmailOTPPasswordReset = "emailotp:password_reset:success"

	// EventEmailOTPChangeEmail is emitted when a user successfully updates their verified email address via OTP.
	// Payload: *EmailChangedPayload
	EventEmailOTPChangeEmail = "emailotp:change_email:success"

	// EventEmailOTPFailed is emitted when an incorrect OTP is submitted or verification fails.
	// Payload: *OTPFailedPayload
	EventEmailOTPFailed = "emailotp:verify:failed"

	// EventEmailOTPAttemptsExceeded is emitted when all allowed attempts on an OTP have been exhausted.
	// Payload: *OTPFailedPayload
	EventEmailOTPAttemptsExceeded = "emailotp:attempts:exceeded"

	// EventEmailOTPExpired is emitted when verification is attempted on an expired OTP.
	// Payload: *OTPFailedPayload
	EventEmailOTPExpired = "emailotp:expired"
)

Event bus topic string constants emitted during Email OTP lifecycle operations.

View Source
const (
	ExtraKeyOTPType      = "email_otp_type"
	ExtraKeyOTPEmail     = "email_otp_email"
	ExtraKeyOTPNewEmail  = "email_otp_new_email"
	ExtraKeyDeviceID     = "device_id"
	ExtraKeyIPAddress    = "ip_address"
	ExtraKeyUserAgent    = "user_agent"
	ExtraKeySessionToken = "session_token"
	ExtraKeyAutoSignIn   = "auto_sign_in"
)

Standard Extra metadata keys that can be set or consumed in Email OTP parameters and Event payloads.

View Source
const (
	ContextKeyEmailOTPPendingPrefix  = "email_otp_pending_"
	ContextKeyEmailOTPVerifiedPrefix = "email_otp_verified_"
)

Context keys stored in plugin.Context for Email OTP state management.

View Source
const PluginID = "email-otp"

PluginID is the unique string identifier for the Email OTP plugin ("email-otp").

Variables

View Source
var (
	// ErrInvalidEmail is returned when an email format validation fails or is empty.
	ErrInvalidEmail = errors.New("emailotp: invalid email address")

	// ErrInvalidOTPType is returned when an unsupported OTP operation type is supplied.
	ErrInvalidOTPType = errors.New("emailotp: invalid OTP type")

	// ErrInvalidOTP is returned when the provided OTP code is incorrect or has already been consumed.
	ErrInvalidOTP = errors.New("emailotp: invalid OTP")

	// ErrOTPExpired is returned when attempting to verify an OTP that has passed its expiration time.
	ErrOTPExpired = errors.New("emailotp: OTP expired")

	// ErrTooManyAttempts is returned when the maximum number of failed attempts on an active OTP has been exceeded.
	ErrTooManyAttempts = errors.New("emailotp: maximum attempt limit reached")

	// ErrUserNotFound is returned when no user matches the queried identifier or email address.
	ErrUserNotFound = errors.New("emailotp: user not found")

	// ErrEmailAlreadyInUse is returned when attempting to assign an email that already belongs to another user.
	ErrEmailAlreadyInUse = errors.New("emailotp: email already in use")

	// ErrChangeEmailDisabled is returned when attempting an email change operation while the feature is disabled.
	ErrChangeEmailDisabled = errors.New("emailotp: change email with OTP is disabled")

	// ErrSameEmail is returned when attempting to change an email to the exact same current address.
	ErrSameEmail = errors.New("emailotp: new email must be different from current email")

	// ErrCurrentEmailNotVerified is returned when verifying the current email OTP is required before requesting change.
	ErrCurrentEmailNotVerified = errors.New("emailotp: OTP is required to verify current email")

	// ErrSendCallbackMissing is returned when attempting to dispatch an OTP without a registered SendVerificationOTP callback.
	ErrSendCallbackMissing = errors.New("emailotp: send verification OTP callback is not configured")

	// ErrCannotRetrieveHashed is returned when trying to read plain text OTP while hashed storage mode is active.
	ErrCannotRetrieveHashed = errors.New("emailotp: OTP is hashed, cannot return plain text OTP")

	// ErrPasswordTooShort is returned when a reset password does not satisfy the minimum length requirement.
	ErrPasswordTooShort = errors.New("emailotp: password is too short")

	// ErrPasswordTooLong is returned when a reset password exceeds the maximum allowed length.
	ErrPasswordTooLong = errors.New("emailotp: password is too long")

	// ErrAccountNotFound is returned when credentials for the requested provider are missing in storage.
	ErrAccountNotFound = errors.New("emailotp: credential account not found")

	// ErrInvalidParameter is returned when a required argument or parameter is missing or malformed.
	ErrInvalidParameter = errors.New("emailotp: required parameter is missing or invalid")
)

Sentinel errors for the Email OTP plugin.

Functions

func DefaultNumericOTPGenerator

func DefaultNumericOTPGenerator(length int) (string, error)

DefaultNumericOTPGenerator generates a cryptographically secure random numeric string of length N (default: 6).

func SplitAtLastColon

func SplitAtLastColon(input string) (string, string)

SplitAtLastColon splits a stored value string into the stored OTP payload and the attempt counter ("<stored_otp>:<attempts>").

func ToChangeEmailOTPIdentifier

func ToChangeEmailOTPIdentifier(currentEmail, newEmail string) string

ToChangeEmailOTPIdentifier formats the composite storage identifier for email change. Format: "change-email-otp-<normalized_current_email>-<normalized_new_email>"

func ToOTPIdentifier

func ToOTPIdentifier(otpType OTPType, email string) string

ToOTPIdentifier formats the standard storage identifier for an OTP given its type and email. Format: "<type>-otp-<normalized_email>" (e.g. "email-verification-otp-user@example.com")

Types

type AESGCMCipher

type AESGCMCipher struct {
	// contains filtered or unexported fields
}

AESGCMCipher implements Cipher using AES-256-GCM with key derivation via SHA-256.

func NewAESGCMCipher

func NewAESGCMCipher(secretKey string) (*AESGCMCipher, error)

NewAESGCMCipher instantiates a new AES-256-GCM cipher using the provided secret key.

func (*AESGCMCipher) Decrypt

func (c *AESGCMCipher) Decrypt(encrypted string) (string, error)

Decrypt decrypts a Base64 Raw URL encoded ciphertext string back into the original plain text OTP.

func (*AESGCMCipher) Encrypt

func (c *AESGCMCipher) Encrypt(plaintext string) (string, error)

Encrypt encrypts the plain text OTP using AES-256-GCM and returns a Base64 Raw URL encoded string.

type ChangeEmailConfig

type ChangeEmailConfig struct {
	// Enabled toggles whether users are permitted to change their email via OTP.
	Enabled bool `json:"enabled"`

	// VerifyCurrentEmail enforces sending and verifying an OTP to the current email address before sending one to the new address.
	VerifyCurrentEmail bool `json:"verify_current_email"`
}

ChangeEmailConfig configures the email change verification flow.

type ChangeEmailParams

type ChangeEmailParams struct {
	// UserID is the ID of the authenticated user.
	UserID string `json:"user_id"`

	// NewEmail is the new verified email address.
	NewEmail string `json:"new_email"`

	// OTP is the confirmation code delivered to the new email.
	OTP string `json:"otp"`

	// Extra holds optional dynamic metadata.
	Extra map[string]any `json:"extra,omitempty"`
}

ChangeEmailParams defines parameters to confirm an email change using the OTP sent to the new email.

type ChangeEmailResult

type ChangeEmailResult struct {
	// Success indicates if the email was successfully changed.
	Success bool `json:"success"`

	// User is the updated user entity with the new email address.
	User *entity.User `json:"user"`
}

ChangeEmailResult reports the outcome of the email change confirmation.

type CheckVerificationOTPParams

type CheckVerificationOTPParams struct {
	// Email is the target email address.
	Email string `json:"email"`

	// Type specifies the OTP workflow type.
	Type OTPType `json:"type"`

	// OTP is the code to check.
	OTP string `json:"otp"`

	// Extra holds optional dynamic metadata.
	Extra map[string]any `json:"extra,omitempty"`
}

CheckVerificationOTPParams defines parameters for validating an OTP without consuming it.

type CheckVerificationOTPResult

type CheckVerificationOTPResult struct {
	// Success indicates whether the OTP is currently valid.
	Success bool `json:"success"`
}

CheckVerificationOTPResult reports whether the tested OTP code is valid.

type Cipher

type Cipher interface {
	// Encrypt encrypts a plain text OTP into a secure string representation.
	Encrypt(otp string) (string, error)

	// Decrypt decrypts an encrypted string back into the original plain text OTP.
	Decrypt(encrypted string) (string, error)
}

Cipher defines the contract for symmetric reversible encryption of OTP codes.

type Config

type Config struct {
	// SendVerificationOTP is the required email delivery callback.
	SendVerificationOTP SendVerificationOTPFunc

	// OTPLength is the number of digits in generated numeric OTPs (default: 6).
	OTPLength int

	// ExpiresIn is the duration after which an unverified OTP expires (default: 5 minutes).
	ExpiresIn time.Duration

	// AllowedAttempts is the maximum number of failed verification tries permitted before invalidating the code (default: 3).
	AllowedAttempts int

	// StoreOTPMode defines how the OTP is persisted ("plain", "hashed", "encrypted", default: "plain").
	StoreOTPMode StoreOTPMode

	// SecretKey is the symmetric key used when StoreOTPMode is "encrypted".
	SecretKey string

	// CustomHasher is an optional custom Hasher implementation for "hashed" mode.
	CustomHasher Hasher

	// CustomCipher is an optional custom Cipher implementation for "encrypted" mode.
	CustomCipher Cipher

	// ResendStrategy specifies behavior on resend requests ("rotate" or "reuse", default: "rotate").
	ResendStrategy ResendStrategy

	// SendVerificationOnSignUp automatically sends an email verification OTP when a user registers.
	SendVerificationOnSignUp bool

	// DisableSignUp prevents creating a new user if an account does not exist during SignInEmailOTP.
	DisableSignUp bool

	// OverrideDefaultEmailVerification overrides default email verification behaviors.
	OverrideDefaultEmailVerification bool

	// AutoSignInAfterVerification automatically generates an authenticated session upon successful email verification (default: true).
	AutoSignInAfterVerification bool

	// RevokeSessionsOnPasswordReset invalidates all active user sessions after a successful password reset (default: true).
	RevokeSessionsOnPasswordReset bool

	// MinPasswordLength is the minimum password length enforced during password resets (default: 8).
	MinPasswordLength int

	// MaxPasswordLength is the maximum password length enforced during password resets (default: 128).
	MaxPasswordLength int

	// ChangeEmail holds email change flow configuration.
	ChangeEmail ChangeEmailConfig

	// RateLimit holds request throttling configuration.
	RateLimit RateLimitConfig

	// GenerateOTP is an optional custom OTP code generator.
	GenerateOTP GenerateOTPFunc
}

Config structures all configuration options for the Email OTP plugin.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns the recommended default configuration for Email OTP authentication.

type CreateVerificationOTPParams

type CreateVerificationOTPParams struct {
	// Email is the target email address.
	Email string `json:"email"`

	// Type specifies the OTP workflow type.
	Type OTPType `json:"type"`

	// Extra holds optional dynamic metadata.
	Extra map[string]any `json:"extra,omitempty"`
}

CreateVerificationOTPParams defines parameters for server-side OTP generation without email dispatch.

type DefaultSHA256Hasher

type DefaultSHA256Hasher struct{}

DefaultSHA256Hasher implements Hasher using SHA-256 encoded in Base64 Raw URL.

func (DefaultSHA256Hasher) Hash

func (h DefaultSHA256Hasher) Hash(otp string) (string, error)

Hash computes the SHA-256 hash of the plain OTP code.

func (DefaultSHA256Hasher) Verify

func (h DefaultSHA256Hasher) Verify(otp, hashed string) bool

Verify compares the plain text OTP against the stored hash using constant-time evaluation.

type EmailChangedPayload

type EmailChangedPayload struct {
	UserID    string         `json:"user_id"`
	OldEmail  string         `json:"old_email"`
	NewEmail  string         `json:"new_email"`
	Timestamp time.Time      `json:"timestamp"`
	Extra     map[string]any `json:"extra,omitempty"`
}

EmailChangedPayload reports details when an email address is changed and confirmed via OTP.

type GenerateOTPFunc

type GenerateOTPFunc func(ctx context.Context, email string, otpType OTPType) (string, error)

GenerateOTPFunc allows overriding the default random numeric code generation routine.

type GetVerificationOTPParams

type GetVerificationOTPParams struct {
	// Email is the target email address.
	Email string `json:"email"`

	// Type specifies the OTP workflow type.
	Type OTPType `json:"type"`

	// Extra holds optional dynamic metadata.
	Extra map[string]any `json:"extra,omitempty"`
}

GetVerificationOTPParams defines parameters for server-side inspection of an active OTP code.

type GetVerificationOTPResult

type GetVerificationOTPResult struct {
	// OTP is the retrieved plain text code.
	OTP string `json:"otp"`
}

GetVerificationOTPResult contains the plain text OTP code retrieved from storage.

type Hasher

type Hasher interface {
	// Hash computes the one-way cryptographic hash of an OTP code.
	Hash(otp string) (string, error)

	// Verify compares a plain text OTP against the stored hash in constant time.
	Verify(otp, hashed string) bool
}

Hasher defines the contract for hashing and verifying OTP codes in constant time.

type OTPFailedPayload

type OTPFailedPayload struct {
	Email             string         `json:"email"`
	Type              OTPType        `json:"type"`
	AttemptsUsed      int            `json:"attempts_used"`
	AttemptsRemaining int            `json:"attempts_remaining"`
	Reason            string         `json:"reason"`
	Extra             map[string]any `json:"extra,omitempty"`
}

OTPFailedPayload reports diagnostic information for failed or expired OTP verification attempts.

type OTPSentPayload

type OTPSentPayload struct {
	Email     string         `json:"email"`
	Type      OTPType        `json:"type"`
	ExpiresAt time.Time      `json:"expires_at"`
	Extra     map[string]any `json:"extra,omitempty"`
}

OTPSentPayload contains confirmation details after an OTP has been dispatched.

type OTPType

type OTPType string

OTPType defines the valid types of OTP operations supported by the plugin.

const (
	// OTPTypeEmailVerification represents email address ownership verification.
	OTPTypeEmailVerification OTPType = "email-verification"

	// OTPTypeSignIn represents passwordless sign-in or auto-registration via OTP.
	OTPTypeSignIn OTPType = "sign-in"

	// OTPTypeForgetPassword represents password recovery and reset.
	OTPTypeForgetPassword OTPType = "forget-password"

	// OTPTypeChangeEmail represents changing a user's verified email address.
	OTPTypeChangeEmail OTPType = "change-email"
)

type OTPVerifiedPayload

type OTPVerifiedPayload struct {
	UserID    string         `json:"user_id"`
	Email     string         `json:"email"`
	Type      OTPType        `json:"type"`
	Timestamp time.Time      `json:"timestamp"`
	Extra     map[string]any `json:"extra,omitempty"`
}

OTPVerifiedPayload reports details of a successful OTP code verification.

type Option

type Option func(*Config)

Option represents a functional option to configure the Email OTP plugin.

func WithAllowedAttempts

func WithAllowedAttempts(attempts int) Option

WithAllowedAttempts sets the maximum number of incorrect attempts before permanently consuming and invalidating the OTP.

func WithAutoSignInAfterVerification

func WithAutoSignInAfterVerification(autoSignIn bool) Option

WithAutoSignInAfterVerification configures whether to issue a session immediately upon successful email verification.

func WithChangeEmail

func WithChangeEmail(enabled, verifyCurrent bool) Option

WithChangeEmail configures whether email changes via OTP are enabled and whether the current email must be verified first.

func WithCustomCipher

func WithCustomCipher(cipher Cipher) Option

WithCustomCipher configures a custom Cipher for symmetric encryption of OTPs.

func WithCustomHasher

func WithCustomHasher(h Hasher) Option

WithCustomHasher configures a custom Hasher for hashing OTPs.

func WithDisableSignUp

func WithDisableSignUp(disable bool) Option

WithDisableSignUp disables automatic user provisioning when verifying a sign-in OTP for an unknown email.

func WithExpiresIn

func WithExpiresIn(d time.Duration) Option

WithExpiresIn sets the duration for which an OTP remains valid.

func WithGenerateOTP

func WithGenerateOTP(fn GenerateOTPFunc) Option

WithGenerateOTP provides a custom code generation callback.

func WithOTPLength

func WithOTPLength(length int) Option

WithOTPLength sets the number of digits in generated numeric OTPs.

func WithOverrideDefaultEmailVerification

func WithOverrideDefaultEmailVerification(override bool) Option

WithOverrideDefaultEmailVerification overrides the framework default verification flow.

func WithPasswordLength

func WithPasswordLength(minLen, maxLen int) Option

WithPasswordLength configures the minimum and maximum password length permitted during password resets.

func WithRateLimit

func WithRateLimit(window time.Duration, max int) Option

WithRateLimit configures rate limiting parameters for OTP dispatch requests.

func WithResendStrategy

func WithResendStrategy(strategy ResendStrategy) Option

WithResendStrategy configures whether to rotate codes or reuse unexpired active codes on resend.

func WithRevokeSessionsOnPasswordReset

func WithRevokeSessionsOnPasswordReset(revoke bool) Option

WithRevokeSessionsOnPasswordReset configures whether all active sessions are revoked when resetting passwords via OTP.

func WithSendVerificationOTP

func WithSendVerificationOTP(fn SendVerificationOTPFunc) Option

WithSendVerificationOTP configures the email delivery callback function.

func WithSendVerificationOnSignUp

func WithSendVerificationOnSignUp(send bool) Option

WithSendVerificationOnSignUp configures whether to automatically dispatch a verification OTP when a user signs up.

func WithStoreOTP

func WithStoreOTP(mode StoreOTPMode, secretKey ...string) Option

WithStoreOTP configures the storage strategy for OTP codes ("plain", "hashed", or "encrypted").

type PasswordResetPayload

type PasswordResetPayload struct {
	UserID    string         `json:"user_id"`
	Email     string         `json:"email"`
	Timestamp time.Time      `json:"timestamp"`
	Extra     map[string]any `json:"extra,omitempty"`
}

PasswordResetPayload reports details when a password is reset via OTP.

type Plugin

type Plugin struct {
	// contains filtered or unexported fields
}

Plugin implements the Email OTP authentication plugin.

func New

func New(repo Repository, opts ...Option) *Plugin

New instantiates a new Email OTP plugin configured with the given repository and options.

func (*Plugin) ChangeEmailEmailOTP

func (p *Plugin) ChangeEmailEmailOTP(ctx context.Context, params *ChangeEmailParams) (*ChangeEmailResult, error)

ChangeEmailEmailOTP confirms changing the user's email address using the OTP delivered to the new email.

func (*Plugin) CheckVerificationOTP

func (p *Plugin) CheckVerificationOTP(ctx context.Context, params *CheckVerificationOTPParams) (*CheckVerificationOTPResult, error)

CheckVerificationOTP verifies whether a submitted OTP is valid without consuming it or changing attempt counts.

func (*Plugin) Config

func (p *Plugin) Config() Config

Config returns the active configuration settings of the Email OTP plugin.

func (*Plugin) CreateVerificationOTP

func (p *Plugin) CreateVerificationOTP(ctx context.Context, params *CreateVerificationOTPParams) (string, error)

CreateVerificationOTP generates and stores an OTP code without triggering the email delivery callback (server API).

func (*Plugin) GetVerificationOTP

func (p *Plugin) GetVerificationOTP(ctx context.Context, params *GetVerificationOTPParams) (*GetVerificationOTPResult, error)

GetVerificationOTP retrieves the plain text OTP code currently stored for an identifier (server API).

func (*Plugin) ID

func (p *Plugin) ID() string

ID returns the unique string identifier for the Email OTP plugin ("email-otp").

func (*Plugin) Init

func (p *Plugin) Init(ctx *plugin.Context) error

Init initializes the plugin within the global GoModularAuth runtime context.

func (*Plugin) Repository

func (p *Plugin) Repository() Repository

Repository returns the underlying storage repository instance.

func (*Plugin) RequestEmailChangeEmailOTP

func (p *Plugin) RequestEmailChangeEmailOTP(ctx context.Context, params *RequestEmailChangeParams) (*RequestEmailChangeResult, error)

RequestEmailChangeEmailOTP initiates the email change procedure by dispatching an OTP to the new email address.

func (*Plugin) RequestPasswordResetEmailOTP

func (p *Plugin) RequestPasswordResetEmailOTP(ctx context.Context, params *RequestPasswordResetParams) (*RequestPasswordResetResult, error)

RequestPasswordResetEmailOTP verifies account existence and dispatches a password recovery OTP.

func (*Plugin) ResetPasswordEmailOTP

func (p *Plugin) ResetPasswordEmailOTP(ctx context.Context, params *ResetPasswordParams) (*ResetPasswordResult, error)

ResetPasswordEmailOTP validates a password reset OTP and updates the user's password hash.

func (*Plugin) SendVerificationOTP

func (p *Plugin) SendVerificationOTP(ctx context.Context, params *SendVerificationOTPParams) (*SendVerificationOTPResult, error)

SendVerificationOTP generates and dispatches a one-time password code to the recipient's email.

func (*Plugin) SignInEmailOTP

func (p *Plugin) SignInEmailOTP(ctx context.Context, params *SignInEmailOTPParams) (*SignInEmailOTPResult, error)

SignInEmailOTP authenticates an existing user or automatically provisions a new user via OTP.

func (*Plugin) VerifyEmailOTP

func (p *Plugin) VerifyEmailOTP(ctx context.Context, params *VerifyEmailOTPParams) (*VerifyEmailOTPResult, error)

VerifyEmailOTP validates an OTP submitted for email verification and marks the user's email as verified.

type RateLimitConfig

type RateLimitConfig struct {
	// Window specifies the duration of the rate-limiting window.
	Window time.Duration `json:"window"`

	// Max specifies the maximum allowed requests within the configured window.
	Max int `json:"max"`
}

RateLimitConfig configures sliding rate limits to protect OTP dispatching from spam abuse.

type Repository

type Repository interface {
	// FindVerificationValue retrieves an active verification record matching the given identifier.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - identifier: The composite OTP key (e.g. "sign-in-otp-user@example.com").
	//
	// Returns:
	//   - *VerificationRecord: The matching record if found.
	//   - error: Nil on success, or database error.
	//
	// Example SQL:
	//   SELECT id, identifier, value, expires_at, created_at, updated_at FROM verification_tokens WHERE identifier = $1 LIMIT 1;
	FindVerificationValue(ctx context.Context, identifier string) (*VerificationRecord, error)

	// CreateVerificationValue creates or replaces a verification record in storage.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - record: The VerificationRecord entity to persist.
	//
	// Returns:
	//   - error: Nil on success, or database error.
	//
	// Example SQL:
	//   INSERT INTO verification_tokens (id, identifier, value, expires_at, created_at, updated_at)
	//   VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (identifier) DO UPDATE SET value = $3, expires_at = $4, updated_at = $6;
	CreateVerificationValue(ctx context.Context, record *VerificationRecord) error

	// UpdateVerificationValue updates the value and expiry of an existing verification record.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - identifier: The composite OTP key.
	//   - value: The updated value payload (e.g. "<stored_otp>:<attempts>").
	//   - expiresAt: The updated expiration timestamp.
	//
	// Returns:
	//   - error: Nil on success, or database error.
	//
	// Example SQL:
	//   UPDATE verification_tokens SET value = $1, expires_at = $2, updated_at = $3 WHERE identifier = $4;
	UpdateVerificationValue(ctx context.Context, identifier, value string, expiresAt time.Time) error

	// DeleteVerificationValue removes a verification record from storage by identifier.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - identifier: The composite OTP key.
	//
	// Returns:
	//   - error: Nil on success, or database error.
	//
	// Example SQL:
	//   DELETE FROM verification_tokens WHERE identifier = $1;
	DeleteVerificationValue(ctx context.Context, identifier string) error

	// ConsumeVerificationValue atomically retrieves and deletes a verification record in a single operation.
	// This ensures strictly single-use anti-replay protection under high concurrency.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - identifier: The composite OTP key.
	//
	// Returns:
	//   - *VerificationRecord: The consumed record if it existed and was not expired.
	//   - error: Nil on success, or database error if not found.
	//
	// Example SQL:
	//   DELETE FROM verification_tokens WHERE identifier = $1 AND expires_at > $2
	//   RETURNING id, identifier, value, expires_at, created_at, updated_at;
	ConsumeVerificationValue(ctx context.Context, identifier string) (*VerificationRecord, error)

	// GetUserByEmail retrieves a user entity matching the provided email address.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - email: The normalized email address to query.
	//
	// Returns:
	//   - *entity.User: The matching user entity if found.
	//   - error: ErrUserNotFound if no record matches, or database error.
	//
	// Example SQL:
	//   SELECT id, name, email, email_verified, role, banned, created_at, updated_at FROM users WHERE LOWER(email) = LOWER($1) LIMIT 1;
	GetUserByEmail(ctx context.Context, email string) (*entity.User, error)

	// GetUserByID retrieves a user entity matching the provided unique identifier.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - id: The user's primary key ID.
	//
	// Returns:
	//   - *entity.User: The matching user entity if found.
	//   - error: ErrUserNotFound if no record matches, or database error.
	//
	// Example SQL:
	//   SELECT id, name, email, email_verified, role, banned, created_at, updated_at FROM users WHERE id = $1 LIMIT 1;
	GetUserByID(ctx context.Context, id string) (*entity.User, error)

	// CreateUser persists a newly registered user in storage.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - params: Parameters containing name, email, and metadata.
	//
	// Returns:
	//   - *entity.User: The created user entity.
	//   - error: Nil on success, or database error.
	//
	// Example SQL:
	//   INSERT INTO users (id, name, email, email_verified, role, created_at, updated_at)
	//   VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id, name, email, email_verified, created_at, updated_at;
	CreateUser(ctx context.Context, params *dto.CreateUserParams) (*entity.User, error)

	// UpdateUser updates modified fields of an existing user profile (e.g. Email, EmailVerified).
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - user: The modified user entity.
	//
	// Returns:
	//   - error: Nil on success, or database error.
	//
	// Example SQL:
	//   UPDATE users SET email = $1, email_verified = $2, updated_at = $3 WHERE id = $4;
	UpdateUser(ctx context.Context, user *entity.User) error

	// GetAccountByUserIDAndProvider retrieves an account matching a given user and authentication provider.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - userID: The target user's ID.
	//   - providerID: The provider identifier (e.g. "credential").
	//
	// Returns:
	//   - *entity.Account: The matching account if found.
	//   - error: ErrAccountNotFound if missing, or database error.
	//
	// Example SQL:
	//   SELECT id, user_id, provider, created_at, updated_at FROM accounts WHERE user_id = $1 AND provider = $2 LIMIT 1;
	GetAccountByUserIDAndProvider(ctx context.Context, userID, providerID string) (*entity.Account, error)

	// CreateAccount associates a new provider authentication account with a user.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - account: The Account entity to persist.
	//
	// Returns:
	//   - error: Nil on success, or database error.
	//
	// Example SQL:
	//   INSERT INTO accounts (id, user_id, provider, created_at, updated_at) VALUES ($1, $2, $3, $4, $5);
	CreateAccount(ctx context.Context, account *entity.Account) error

	// UpdateAccountPassword updates the password hash on a user's credential account.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - userID: The target user's ID.
	//   - passwordHash: The newly calculated password hash string.
	//
	// Returns:
	//   - error: Nil on success, or database error.
	//
	// Example SQL:
	//   UPDATE accounts SET password = $1, updated_at = $2 WHERE user_id = $3 AND provider = 'credential';
	UpdateAccountPassword(ctx context.Context, userID, passwordHash string) error

	// DeleteCredentialAccount removes the credential account for a user adopted via passwordless OTP.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - userID: The target user's ID.
	//
	// Returns:
	//   - error: Nil on success, or database error.
	//
	// Example SQL:
	//   DELETE FROM accounts WHERE user_id = $1 AND provider = 'credential';
	DeleteCredentialAccount(ctx context.Context, userID string) error

	// CreateSession persists a new active user session in storage.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - params: Parameters containing userID, token, expiration, and metadata.
	//
	// Returns:
	//   - *entity.Session: The created session entity.
	//   - error: Nil on success, or database error.
	//
	// Example SQL:
	//   INSERT INTO sessions (id, user_id, token, expires_at, ip_address, user_agent, created_at, updated_at)
	//   VALUES ($1, $2, $3, $4, $5, $6, $7, $8);
	CreateSession(ctx context.Context, params *dto.CreateSessionParams) (*entity.Session, error)

	// DeleteSessionsByUserID invalidates all active sessions for a user (used upon password reset).
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - userID: The target user's ID.
	//
	// Returns:
	//   - error: Nil on success, or database error.
	//
	// Example SQL:
	//   DELETE FROM sessions WHERE user_id = $1;
	DeleteSessionsByUserID(ctx context.Context, userID string) error
}

Repository defines the persistent storage contract required by the Email OTP plugin. Implement this interface on your custom database adapter (e.g. PostgreSQL, MySQL, SQLite, MongoDB, GORM).

type RequestEmailChangeParams

type RequestEmailChangeParams struct {
	// UserID is the ID of the authenticated user requesting the change.
	UserID string `json:"user_id"`

	// NewEmail is the new email address to bind to the account.
	NewEmail string `json:"new_email"`

	// OTP is the verification code for the current email if VerifyCurrentEmail is enabled.
	OTP string `json:"otp,omitempty"`

	// Extra holds optional dynamic metadata.
	Extra map[string]any `json:"extra,omitempty"`
}

RequestEmailChangeParams defines parameters to initiate an email change flow.

type RequestEmailChangeResult

type RequestEmailChangeResult struct {
	// Success indicates if the OTP was successfully dispatched to the new email address.
	Success bool `json:"success"`
}

RequestEmailChangeResult reports whether the email change OTP was dispatched to the new email.

type RequestPasswordResetParams

type RequestPasswordResetParams struct {
	// Email is the account email requesting a password reset.
	Email string `json:"email"`

	// Extra holds optional dynamic metadata.
	Extra map[string]any `json:"extra,omitempty"`
}

RequestPasswordResetParams defines parameters to request a password reset OTP.

type RequestPasswordResetResult

type RequestPasswordResetResult struct {
	// Success indicates if the reset OTP was successfully dispatched.
	Success bool `json:"success"`
}

RequestPasswordResetResult reports the result of the password reset dispatch request.

type ResendStrategy

type ResendStrategy string

ResendStrategy defines the behavior when requesting a new OTP while an active one exists.

const (
	// ResendStrategyRotate always invalidates the previous OTP and generates a fresh code.
	ResendStrategyRotate ResendStrategy = "rotate"

	// ResendStrategyReuse resends the existing active OTP and extends its expiration (plain/encrypted only).
	ResendStrategyReuse ResendStrategy = "reuse"
)

type ResetPasswordParams

type ResetPasswordParams struct {
	// Email is the account email address.
	Email string `json:"email"`

	// OTP is the reset code submitted by the user.
	OTP string `json:"otp"`

	// NewPassword is the new password string to set.
	NewPassword string `json:"new_password"`

	// Extra holds optional dynamic metadata.
	Extra map[string]any `json:"extra,omitempty"`
}

ResetPasswordParams defines parameters for setting a new password using a verified OTP.

func (*ResetPasswordParams) Get

func (p *ResetPasswordParams) Get(key string) (any, bool)

func (*ResetPasswordParams) Set

func (p *ResetPasswordParams) Set(key string, val any)

type ResetPasswordResult

type ResetPasswordResult struct {
	// Success indicates if the password was successfully reset.
	Success bool `json:"success"`
}

ResetPasswordResult reports whether the password was successfully updated.

type SendEmailData

type SendEmailData struct {
	// Email is the destination recipient email address.
	Email string `json:"email"`

	// OTP is the raw numeric OTP verification code to deliver.
	OTP string `json:"otp"`

	// Type indicates the specific operation requiring verification.
	Type OTPType `json:"type"`
}

SendEmailData contains the parameters delivered to the transactional email delivery callback.

type SendOTPPendingPayload

type SendOTPPendingPayload struct {
	Email     string         `json:"email"`
	Type      OTPType        `json:"type"`
	ExpiresAt time.Time      `json:"expires_at"`
	Extra     map[string]any `json:"extra,omitempty"`
}

SendOTPPendingPayload contains recipient and expiration details before dispatching an OTP.

type SendVerificationOTPFunc

type SendVerificationOTPFunc func(ctx context.Context, data SendEmailData) error

SendVerificationOTPFunc defines the callback function invoked when dispatching an OTP to a recipient email.

type SendVerificationOTPParams

type SendVerificationOTPParams struct {
	// Email is the destination recipient email address (required).
	Email string `json:"email"`

	// Type specifies the OTP workflow type ("email-verification", "sign-in", "forget-password", "change-email").
	Type OTPType `json:"type"`

	// Extra holds dynamic metadata passed through event interceptors (optional).
	Extra map[string]any `json:"extra,omitempty"`
}

SendVerificationOTPParams defines parameters required to dispatch an OTP to a user's email.

func (*SendVerificationOTPParams) Get

func (p *SendVerificationOTPParams) Get(key string) (any, bool)

func (*SendVerificationOTPParams) Set

func (p *SendVerificationOTPParams) Set(key string, val any)

type SendVerificationOTPResult

type SendVerificationOTPResult struct {
	// Success indicates if the OTP was successfully generated and dispatched.
	Success bool `json:"success"`

	// ExpiresAt indicates when the dispatched OTP code will expire.
	ExpiresAt time.Time `json:"expires_at"`
}

SendVerificationOTPResult contains the delivery status and expiry of the dispatched OTP.

type SignInEmailOTPParams

type SignInEmailOTPParams struct {
	// Email is the user's email address.
	Email string `json:"email"`

	// OTP is the one-time code submitted by the user.
	OTP string `json:"otp"`

	// Name is the optional display name assigned if a new user is created.
	Name string `json:"name,omitempty"`

	// Extra holds optional dynamic metadata.
	Extra map[string]any `json:"extra,omitempty"`
}

SignInEmailOTPParams defines parameters for passwordless sign-in and auto-registration via OTP.

func (*SignInEmailOTPParams) Get

func (p *SignInEmailOTPParams) Get(key string) (any, bool)

func (*SignInEmailOTPParams) Set

func (p *SignInEmailOTPParams) Set(key string, val any)

type SignInEmailOTPResult

type SignInEmailOTPResult struct {
	// User is the authenticated or newly provisioned user entity.
	User *entity.User `json:"user"`

	// SessionToken is the raw session token.
	SessionToken string `json:"session_token"`

	// Session is the persisted active session entity.
	Session *entity.Session `json:"session"`

	// IsNewUser indicates if this sign-in operation provisioned a new user account.
	IsNewUser bool `json:"is_new_user"`
}

SignInEmailOTPResult contains the authenticated user profile, session, and registration indicator.

type SignInSuccessPayload

type SignInSuccessPayload struct {
	User      *entity.User    `json:"user"`
	Session   *entity.Session `json:"session"`
	IsNewUser bool            `json:"is_new_user"`
	Extra     map[string]any  `json:"extra,omitempty"`
}

SignInSuccessPayload reports authentication or auto-registration details upon OTP sign-in.

type StoreOTPMode

type StoreOTPMode string

StoreOTPMode defines how the OTP code is persisted in storage.

const (
	// StoreOTPPlain stores the OTP code in plain text.
	StoreOTPPlain StoreOTPMode = "plain"

	// StoreOTPHashed stores the OTP code using constant-time SHA-256 hash.
	StoreOTPHashed StoreOTPMode = "hashed"

	// StoreOTPEncrypted stores the OTP code using AES-256-GCM symmetric encryption.
	StoreOTPEncrypted StoreOTPMode = "encrypted"
)

type VerificationRecord

type VerificationRecord struct {
	// ID is the unique database record identifier.
	ID string `json:"id"`

	// Identifier is the composite lookup key (e.g. "email-verification-otp-user@example.com").
	Identifier string `json:"identifier"`

	// Value stores the code and attempt counter formatted as "<stored_otp>:<attempts>".
	Value string `json:"value"`

	// ExpiresAt specifies the exact timestamp after which this verification value is invalid.
	ExpiresAt time.Time `json:"expires_at"`

	// CreatedAt records when the verification record was initialized.
	CreatedAt time.Time `json:"created_at"`

	// UpdatedAt records when the verification record was last modified.
	UpdatedAt time.Time `json:"updated_at"`
}

VerificationRecord represents the persistent storage entity for an OTP verification value.

type VerifyBeforePayload

type VerifyBeforePayload struct {
	Email string         `json:"email"`
	Type  OTPType        `json:"type"`
	Extra map[string]any `json:"extra,omitempty"`
}

VerifyBeforePayload contains parameters before executing OTP verification.

type VerifyEmailOTPParams

type VerifyEmailOTPParams struct {
	// Email is the address being verified.
	Email string `json:"email"`

	// OTP is the verification code submitted by the user.
	OTP string `json:"otp"`

	// Extra holds optional dynamic metadata.
	Extra map[string]any `json:"extra,omitempty"`
}

VerifyEmailOTPParams defines parameters to verify email ownership via OTP.

func (*VerifyEmailOTPParams) Get

func (p *VerifyEmailOTPParams) Get(key string) (any, bool)

func (*VerifyEmailOTPParams) Set

func (p *VerifyEmailOTPParams) Set(key string, val any)

type VerifyEmailOTPResult

type VerifyEmailOTPResult struct {
	// Success indicates successful email verification.
	Success bool `json:"success"`

	// User is the updated user entity with EmailVerified set to true.
	User *entity.User `json:"user"`

	// SessionToken is the raw token string if AutoSignInAfterVerification is enabled.
	SessionToken string `json:"session_token,omitempty"`

	// Session is the active session entity if AutoSignInAfterVerification is enabled.
	Session *entity.Session `json:"session,omitempty"`
}

VerifyEmailOTPResult contains the updated user profile and optional auto-created session.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL