Documentation
¶
Overview ¶
Package phonenumber provides SMS OTP (One-Time Password) and phone number-based authentication for go-modular-auth, supporting passwordless sign-in, phone number verification and updates, phone + password login, SMS password resets, and attempt budgeting.
Index ¶
- Constants
- Variables
- func DefaultNumericOTPGenerator(length int) (string, error)
- func SplitAtLastColon(input string) (string, string)
- func ToOTPIdentifier(phoneNumber string) string
- func ToPasswordResetOTPIdentifier(phoneNumber string) string
- type AESGCMCipher
- type CallbackOnVerificationFunc
- type CheckVerificationOTPParams
- type CheckVerificationOTPResult
- type Cipher
- type Config
- type CreateVerificationOTPParams
- type CreateVerificationOTPResult
- type DefaultSHA256Hasher
- type GenerateOTPFunc
- type GetVerificationOTPParams
- type GetVerificationOTPResult
- type Hasher
- type OTPFailedPayload
- type OTPSentPayload
- type OTPType
- type OTPVerifiedPayload
- type OnPasswordResetFunc
- type OnVerificationData
- type Option
- func WithAllowedAttempts(attempts int) Option
- func WithCallbackOnVerification(fn CallbackOnVerificationFunc) Option
- func WithCustomCipher(cipher Cipher) Option
- func WithCustomHasher(hasher Hasher) Option
- func WithDisableSignUp(disable bool) Option
- func WithExpiresIn(d time.Duration) Option
- func WithGenerateOTP(fn GenerateOTPFunc) Option
- func WithMaxPasswordLength(maxLen int) Option
- func WithMinPasswordLength(minLen int) Option
- func WithOTPLength(length int) Option
- func WithOnPasswordReset(fn OnPasswordResetFunc) Option
- func WithPasswordPolicy(minLen, maxLen int, revokeOnReset bool) Option
- func WithPhoneNumberValidator(fn PhoneNumberValidatorFunc) Option
- func WithRequireVerification(require bool) Option
- func WithResendStrategy(strategy ResendStrategy) Option
- func WithRevokeSessionsOnPasswordReset(revoke bool) Option
- func WithSendOTP(fn SendOTPFunc) Option
- func WithSendPasswordResetOTP(fn SendOTPFunc) Option
- func WithSignUpOnVerification(cfg SignUpOnVerificationConfig) Option
- func WithStoreOTP(mode StoreOTPMode, secretKey ...string) Option
- func WithVerifyOTP(fn VerifyOTPFunc) Option
- type PasswordResetPayload
- type PhoneNumberValidatorFunc
- type Plugin
- func (p *Plugin) CheckVerificationOTP(ctx context.Context, params CheckVerificationOTPParams) (*CheckVerificationOTPResult, error)
- func (p *Plugin) Config() Config
- func (p *Plugin) CreateVerificationOTP(ctx context.Context, params CreateVerificationOTPParams) (*CreateVerificationOTPResult, error)
- func (p *Plugin) GetVerificationOTP(ctx context.Context, params GetVerificationOTPParams) (*GetVerificationOTPResult, error)
- func (p *Plugin) ID() string
- func (p *Plugin) Init(ctx *plugin.Context) error
- func (p *Plugin) Repository() Repository
- func (p *Plugin) RequestPasswordReset(ctx context.Context, params RequestPasswordResetParams) (*RequestPasswordResetResult, error)
- func (p *Plugin) ResetPassword(ctx context.Context, params ResetPasswordParams) (*ResetPasswordResult, error)
- func (p *Plugin) SendOTP(ctx context.Context, params SendOTPParams) (*SendOTPResult, error)
- func (p *Plugin) SignIn(ctx context.Context, params SignInParams) (*SignInResult, error)
- func (p *Plugin) UnlinkPhoneNumber(ctx context.Context, userID string) (*entity.User, error)
- func (p *Plugin) Verify(ctx context.Context, params VerifyParams) (*VerifyResult, error)
- type Repository
- type RequestPasswordResetParams
- type RequestPasswordResetResult
- type ResendStrategy
- type ResetPasswordParams
- type ResetPasswordResult
- type SendOTPData
- type SendOTPFunc
- type SendOTPParams
- type SendOTPResult
- type SignInParams
- type SignInResult
- type SignInSuccessPayload
- type SignUpOnVerificationConfig
- type StoreOTPMode
- type VerificationRecord
- type VerifyOTPData
- type VerifyOTPFunc
- type VerifyParams
- type VerifyResult
Constants ¶
const ( // EventPhoneNumberOTPSendBefore is emitted right before dispatching an OTP to the recipient phone number. // Payload: *OTPSentPayload EventPhoneNumberOTPSendBefore = "phonenumber:otp:send:before" // EventPhoneNumberOTPSent is emitted immediately after an OTP has been successfully dispatched. // Payload: *OTPSentPayload EventPhoneNumberOTPSent = "phonenumber:otp:send:after" // EventPhoneNumberOTPVerifyBefore is emitted right before verifying a submitted OTP code. // Payload: *OTPSentPayload EventPhoneNumberOTPVerifyBefore = "phonenumber:otp:verify:before" // EventPhoneNumberOTPVerified is emitted after an OTP code has been successfully verified. // Payload: *OTPVerifiedPayload EventPhoneNumberOTPVerified = "phonenumber:otp:verify:after" // EventPhoneNumberOTPFailed is emitted when an incorrect OTP is submitted. // Payload: *OTPFailedPayload EventPhoneNumberOTPFailed = "phonenumber:otp:verify:failed" // EventPhoneNumberOTPAttemptsExceeded is emitted when all allowed attempts on an OTP have been exhausted. // Payload: *OTPFailedPayload EventPhoneNumberOTPAttemptsExceeded = "phonenumber:otp:attempts:exceeded" // EventPhoneNumberOTPExpired is emitted when verification is attempted on an expired OTP. // Payload: *OTPFailedPayload EventPhoneNumberOTPExpired = "phonenumber:otp:expired" // EventPhoneNumberSignInSuccess is emitted when a user successfully authenticates via phone OTP. // Payload: *SignInSuccessPayload EventPhoneNumberSignInSuccess = "phonenumber:signin:success" // EventPhoneNumberSignUpSuccess is emitted when a new user is provisioned upon phone OTP verification. // Payload: *SignInSuccessPayload EventPhoneNumberSignUpSuccess = "phonenumber:signup:success" // EventPhoneNumberSignInPasswordSuccess is emitted when a user authenticates using phone + password. // Payload: *SignInSuccessPayload EventPhoneNumberSignInPasswordSuccess = "phonenumber:signin_password:success" // EventPhoneNumberUpdated is emitted when a user's phone number is updated after verification. // Payload: *entity.User EventPhoneNumberUpdated = "phonenumber:updated" // EventPhoneNumberUnlinked is emitted when a user's phone number is removed. // Payload: *entity.User EventPhoneNumberUnlinked = "phonenumber:unlinked" // EventPhoneNumberPasswordResetRequested is emitted when a password reset OTP is requested for a phone number. // Payload: *OTPSentPayload EventPhoneNumberPasswordResetRequested = "phonenumber:password_reset:requested" // EventPhoneNumberPasswordResetSuccess is emitted when a user resets their password using a phone OTP. // Payload: *PasswordResetPayload EventPhoneNumberPasswordResetSuccess = "phonenumber:password_reset:success" )
Event bus topic string constants emitted during Phone Number lifecycle operations.
const ( ExtraKeyPhoneNumber = "phone_number" ExtraKeyPhoneNumberVerified = "phone_number_verified" ExtraKeyOTPCode = "otp_code" ExtraKeyOTPType = "otp_type" ExtraKeyRememberMe = "remember_me" ExtraKeyUpdatePhone = "update_phone_number" ExtraKeyDisableSession = "disable_session" ExtraKeyDeviceID = "device_id" ExtraKeyIPAddress = "ip_address" ExtraKeyUserAgent = "user_agent" )
Standard Extra metadata keys that can be set or consumed in Phone Number parameters and Event payloads.
const PluginID = "phone-number"
PluginID is the unique string identifier for the Phone Number plugin ("phone-number").
Variables ¶
var ( // ErrInvalidPhoneNumber is returned when a phone number format validation fails or is empty. ErrInvalidPhoneNumber = errors.New("phonenumber: invalid phone number") // ErrPhoneNumberAlreadyExists is returned when attempting to assign a phone number that already belongs to another user. ErrPhoneNumberAlreadyExists = errors.New("phonenumber: phone number already in use by another user") // ErrPhoneNumberNotRegistered is returned when attempting to sign in with a phone number that does not exist in storage and auto-signup is disabled. ErrPhoneNumberNotRegistered = errors.New("phonenumber: phone number is not registered") // ErrInvalidPhoneNumberOrPassword is returned when phone + password login credentials do not match. ErrInvalidPhoneNumberOrPassword = errors.New("phonenumber: invalid phone number or password") // ErrPhoneNumberNotVerified is returned when attempting password sign-in with an unverified phone number while RequireVerification is enabled. ErrPhoneNumberNotVerified = errors.New("phonenumber: phone number is not verified") // ErrPhoneNumberCannotBeUpdated is returned when direct phone mutation is disallowed without prior verification. ErrPhoneNumberCannotBeUpdated = errors.New("phonenumber: phone number cannot be updated directly without verification") // ErrSendOTPNotImplemented is returned when attempting to dispatch an OTP without a configured SendOTP callback. ErrSendOTPNotImplemented = errors.New("phonenumber: send OTP callback is not configured") // ErrOTPNotFound is returned when no active OTP record exists for the given phone number identifier. ErrOTPNotFound = errors.New("phonenumber: OTP not found or already consumed") // ErrOTPExpired is returned when attempting to verify an OTP that has passed its expiration duration. ErrOTPExpired = errors.New("phonenumber: OTP expired") // ErrInvalidOTP is returned when the provided OTP code does not match the stored code or hash. ErrInvalidOTP = errors.New("phonenumber: invalid OTP") // ErrTooManyAttempts is returned when the maximum number of failed verification attempts on an active OTP has been exhausted. ErrTooManyAttempts = errors.New("phonenumber: maximum verification attempt limit reached") // ErrPasswordTooShort is returned when a new password does not satisfy the minimum length requirement. ErrPasswordTooShort = errors.New("phonenumber: password is too short") // ErrPasswordTooLong is returned when a new password exceeds the maximum allowed length. ErrPasswordTooLong = errors.New("phonenumber: password is too long") // ErrUserNotFound is returned when no user matches the queried ID or phone number. ErrUserNotFound = errors.New("phonenumber: user not found") // ErrCredentialAccountNotFound is returned when credential provider credentials are missing for the user. ErrCredentialAccountNotFound = errors.New("phonenumber: credential account not found") // ErrCannotRetrieveHashed is returned when attempting to inspect a plain text OTP while hashed storage mode is active. ErrCannotRetrieveHashed = errors.New("phonenumber: OTP is hashed, cannot retrieve plain text") // ErrInvalidStoredFormat is returned when a stored OTP record does not conform to the expected format. ErrInvalidStoredFormat = errors.New("phonenumber: invalid stored OTP record format") )
Sentinel errors for the Phone Number plugin.
Functions ¶
func DefaultNumericOTPGenerator ¶
DefaultNumericOTPGenerator generates a cryptographically secure random numeric string of length N (default: 6).
func SplitAtLastColon ¶
SplitAtLastColon splits a stored value string into the stored OTP payload and the attempt counter ("<stored_otp>:<attempts>").
func ToOTPIdentifier ¶
ToOTPIdentifier formats the standard storage identifier for phone verification or sign-in OTP. Format: "phone-verification-otp-<normalized_phone>"
func ToPasswordResetOTPIdentifier ¶
ToPasswordResetOTPIdentifier formats the storage identifier for SMS password reset OTP. Format: "phone-password-reset-otp-<normalized_phone>"
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.
type CallbackOnVerificationFunc ¶
type CallbackOnVerificationFunc func(ctx context.Context, data OnVerificationData) error
CallbackOnVerificationFunc defines a hook executed after a phone number is successfully verified.
type CheckVerificationOTPParams ¶
type CheckVerificationOTPParams struct {
// PhoneNumber is the target phone number.
PhoneNumber string `json:"phone_number"`
// Type specifies the OTP workflow type.
Type OTPType `json:"type"`
// OTP is the code to check.
OTP string `json:"otp"`
plugin.ExtraContainer
}
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 currently valid.
type Cipher ¶
type Cipher interface {
// Encrypt encrypts a plain text OTP into a secure string representation.
Encrypt(plaintext string) (string, error)
// Decrypt decrypts an encrypted string back into the original plain text OTP.
Decrypt(ciphertext string) (string, error)
}
Cipher defines the contract for symmetric reversible encryption of OTP codes.
type Config ¶
type Config struct {
// SendOTP is the required SMS delivery callback.
SendOTP SendOTPFunc
// VerifyOTP is an optional delegated verification callback.
VerifyOTP VerifyOTPFunc
// SendPasswordResetOTP is an optional dedicated callback for password reset SMS OTPs.
SendPasswordResetOTP SendOTPFunc
// PhoneNumberValidator is an optional callback to validate phone number format.
PhoneNumberValidator PhoneNumberValidatorFunc
// CallbackOnVerification is an optional callback executed upon successful phone verification.
CallbackOnVerification CallbackOnVerificationFunc
// OnPasswordReset is an optional callback executed upon password reset confirmation.
OnPasswordReset OnPasswordResetFunc
// SignUpOnVerification configures temporary credentials for auto-created users.
SignUpOnVerification *SignUpOnVerificationConfig
// GenerateOTP is an optional custom OTP generator function.
GenerateOTP GenerateOTPFunc
// 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
// RequireVerification enforces that the phone number must already be verified before allowing password-based sign-in.
RequireVerification bool
// DisableSignUp prevents creating a new user if an account does not exist during phone verification.
DisableSignUp 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
// 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
// ResendStrategy specifies behavior on resend requests ("rotate" or "reuse", default: "rotate").
ResendStrategy ResendStrategy
// CustomHasher is an optional custom Hasher implementation for "hashed" mode.
CustomHasher Hasher
// CustomCipher is an optional custom Cipher implementation for "encrypted" mode.
CustomCipher Cipher
}
Config structures all configuration options for the Phone Number plugin.
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns recommended production default settings for the Phone Number plugin.
type CreateVerificationOTPParams ¶
type CreateVerificationOTPParams struct {
// PhoneNumber is the target phone number.
PhoneNumber string `json:"phone_number"`
// Type specifies the OTP workflow type.
Type OTPType `json:"type"`
plugin.ExtraContainer
}
CreateVerificationOTPParams defines parameters for server-side OTP generation without SMS dispatch.
type CreateVerificationOTPResult ¶
type CreateVerificationOTPResult struct {
// Success indicates if the OTP record was created.
Success bool `json:"success"`
// ExpiresAt indicates when the created OTP will expire.
ExpiresAt time.Time `json:"expires_at"`
}
CreateVerificationOTPResult contains the status and expiry of the created OTP.
type DefaultSHA256Hasher ¶
type DefaultSHA256Hasher struct{}
DefaultSHA256Hasher implements Hasher using SHA-256 encoded in Base64 Raw URL.
func (DefaultSHA256Hasher) Hash ¶
func (h DefaultSHA256Hasher) Hash(code string) (string, error)
Hash computes the SHA-256 hash of the plain OTP code.
func (DefaultSHA256Hasher) Verify ¶
func (h DefaultSHA256Hasher) Verify(code, hashedCode string) bool
Verify compares the plain text OTP against the stored hash using constant-time evaluation.
type GenerateOTPFunc ¶
GenerateOTPFunc allows overriding the default random numeric code generation routine.
type GetVerificationOTPParams ¶
type GetVerificationOTPParams struct {
// PhoneNumber is the target phone number.
PhoneNumber string `json:"phone_number"`
// Type specifies the OTP workflow type.
Type OTPType `json:"type"`
plugin.ExtraContainer
}
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(code string) (string, error)
// Verify compares a plain text OTP against the stored hash in constant time.
Verify(code, hashedCode string) bool
}
Hasher defines the contract for hashing and verifying OTP codes in constant time.
type OTPFailedPayload ¶
type OTPFailedPayload struct {
PhoneNumber string `json:"phone_number"`
Type OTPType `json:"type"`
AttemptsUsed int `json:"attempts_used"`
AttemptsRemaining int `json:"attempts_remaining"`
Reason string `json:"reason"`
plugin.ExtraContainer
}
OTPFailedPayload contains details when OTP verification fails or reaches limits.
type OTPSentPayload ¶
type OTPSentPayload struct {
PhoneNumber string `json:"phone_number"`
Type OTPType `json:"type"`
ExpiresAt time.Time `json:"expires_at"`
plugin.ExtraContainer
}
OTPSentPayload contains recipient and expiration details for dispatched OTPs.
type OTPType ¶
type OTPType string
OTPType defines the valid types of OTP operations supported by the Phone Number plugin.
type OTPVerifiedPayload ¶
type OTPVerifiedPayload struct {
UserID string `json:"user_id"`
PhoneNumber string `json:"phone_number"`
Type OTPType `json:"type"`
Timestamp time.Time `json:"timestamp"`
plugin.ExtraContainer
}
OTPVerifiedPayload reports details of a successful OTP code verification.
type OnPasswordResetFunc ¶
OnPasswordResetFunc defines a hook executed after a password reset is confirmed.
type OnVerificationData ¶
type OnVerificationData struct {
// PhoneNumber is the verified phone number.
PhoneNumber string `json:"phone_number"`
// User is the updated or newly created user entity.
User *entity.User `json:"user"`
// Extra holds dynamic metadata passed through event interceptors.
Extra map[string]any `json:"extra,omitempty"`
}
OnVerificationData contains the context delivered to the post-verification callback.
type Option ¶
type Option func(*Config)
Option represents a configuration modifier function.
func WithAllowedAttempts ¶
WithAllowedAttempts sets the maximum number of failed verification tries allowed on an active OTP before locking out.
func WithCallbackOnVerification ¶
func WithCallbackOnVerification(fn CallbackOnVerificationFunc) Option
WithCallbackOnVerification registers a hook to be called after a phone number is verified.
func WithCustomCipher ¶
WithCustomCipher registers a custom Cipher for StoreOTPEncrypted mode.
func WithCustomHasher ¶
WithCustomHasher registers a custom Hasher for StoreOTPHashed mode.
func WithDisableSignUp ¶
WithDisableSignUp disables automatic user provisioning when verifying an unregistered phone number.
func WithExpiresIn ¶
WithExpiresIn configures the lifespan of generated OTPs.
func WithGenerateOTP ¶
func WithGenerateOTP(fn GenerateOTPFunc) Option
WithGenerateOTP registers a custom OTP code generation routine.
func WithMaxPasswordLength ¶
WithMaxPasswordLength configures the maximum allowed password length.
func WithMinPasswordLength ¶
WithMinPasswordLength configures the minimum required password length.
func WithOTPLength ¶
WithOTPLength configures the number of digits in generated numeric OTPs.
func WithOnPasswordReset ¶
func WithOnPasswordReset(fn OnPasswordResetFunc) Option
WithOnPasswordReset sets a callback hook executed upon successful password reset.
func WithPasswordPolicy ¶
WithPasswordPolicy sets password length constraints and session revocation behavior upon password reset.
func WithPhoneNumberValidator ¶
func WithPhoneNumberValidator(fn PhoneNumberValidatorFunc) Option
WithPhoneNumberValidator sets a custom validator function for phone number formats.
func WithRequireVerification ¶
WithRequireVerification enforces that phone numbers must be verified before allowing phone + password sign-in.
func WithResendStrategy ¶
func WithResendStrategy(strategy ResendStrategy) Option
WithResendStrategy configures the behavior when a user requests an OTP while an active one exists.
func WithRevokeSessionsOnPasswordReset ¶
WithRevokeSessionsOnPasswordReset configures whether to revoke all active sessions on password reset.
func WithSendOTP ¶
func WithSendOTP(fn SendOTPFunc) Option
WithSendOTP configures the SMS delivery callback function.
func WithSendPasswordResetOTP ¶
func WithSendPasswordResetOTP(fn SendOTPFunc) Option
WithSendPasswordResetOTP configures a dedicated SMS delivery callback for password resets.
func WithSignUpOnVerification ¶
func WithSignUpOnVerification(cfg SignUpOnVerificationConfig) Option
WithSignUpOnVerification configures temporary naming and email resolution for auto-registered users.
func WithStoreOTP ¶
func WithStoreOTP(mode StoreOTPMode, secretKey ...string) Option
WithStoreOTP configures the persistence strategy and optional secret key for encrypted mode.
func WithVerifyOTP ¶
func WithVerifyOTP(fn VerifyOTPFunc) Option
WithVerifyOTP configures an optional delegated verification callback (e.g. Twilio Verify API).
type PasswordResetPayload ¶
type PasswordResetPayload struct {
UserID string `json:"user_id"`
PhoneNumber string `json:"phone_number"`
Timestamp time.Time `json:"timestamp"`
plugin.ExtraContainer
}
PasswordResetPayload reports details when a password is reset via SMS OTP.
type PhoneNumberValidatorFunc ¶
PhoneNumberValidatorFunc defines custom phone number validation (e.g. E.164 format verification).
type Plugin ¶
type Plugin struct {
// contains filtered or unexported fields
}
Plugin implements the Phone Number (SMS OTP) authentication plugin.
func New ¶
func New(repo Repository, opts ...Option) *Plugin
New instantiates a new Phone Number plugin configured with the given repository and options.
func (*Plugin) CheckVerificationOTP ¶
func (p *Plugin) CheckVerificationOTP(ctx context.Context, params CheckVerificationOTPParams) (*CheckVerificationOTPResult, error)
CheckVerificationOTP validates an OTP code against storage without consuming it or modifying attempt counters.
func (*Plugin) Config ¶
Config returns the active configuration settings of the Phone Number plugin.
func (*Plugin) CreateVerificationOTP ¶
func (p *Plugin) CreateVerificationOTP(ctx context.Context, params CreateVerificationOTPParams) (*CreateVerificationOTPResult, error)
CreateVerificationOTP generates and persists an OTP record in storage without sending SMS.
func (*Plugin) GetVerificationOTP ¶
func (p *Plugin) GetVerificationOTP(ctx context.Context, params GetVerificationOTPParams) (*GetVerificationOTPResult, error)
GetVerificationOTP retrieves the active plain text OTP code (fails if stored in hashed mode).
func (*Plugin) ID ¶
ID returns the unique string identifier for the Phone Number plugin ("phone-number").
func (*Plugin) Repository ¶
func (p *Plugin) Repository() Repository
Repository returns the underlying storage repository instance.
func (*Plugin) RequestPasswordReset ¶
func (p *Plugin) RequestPasswordReset(ctx context.Context, params RequestPasswordResetParams) (*RequestPasswordResetResult, error)
RequestPasswordReset dispatches a numeric OTP via SMS to enable password resetting.
func (*Plugin) ResetPassword ¶
func (p *Plugin) ResetPassword(ctx context.Context, params ResetPasswordParams) (*ResetPasswordResult, error)
ResetPassword verifies the reset OTP and securely updates the user's password.
func (*Plugin) SendOTP ¶
func (p *Plugin) SendOTP(ctx context.Context, params SendOTPParams) (*SendOTPResult, error)
SendOTP generates and dispatches a numeric OTP via SMS to the specified recipient phone number.
func (*Plugin) SignIn ¶
func (p *Plugin) SignIn(ctx context.Context, params SignInParams) (*SignInResult, error)
SignIn authenticates a user using their phone number and password.
func (*Plugin) UnlinkPhoneNumber ¶
UnlinkPhoneNumber removes the phone number from the user's profile and resets its verified status.
func (*Plugin) Verify ¶
func (p *Plugin) Verify(ctx context.Context, params VerifyParams) (*VerifyResult, error)
Verify validates a submitted OTP code and performs user login, auto-registration, or phone number update.
type Repository ¶
type Repository interface {
// FindVerificationValue retrieves an active verification record matching the given identifier.
//
// Function:
// Queries storage for an active SMS OTP verification record.
//
// Storage:
// Cache (Redis / In-Memory TTL) - Ephemeral short-lived SMS OTP token data.
//
// Arguments:
// - ctx: Request cancellation context.
// - identifier: The composite OTP key (e.g. "phone-verification-otp-+1234567890").
//
// 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;
//
// Example Cache (Redis):
// val, err := rdb.Get(ctx, "phoneotp:" + identifier).Bytes()
FindVerificationValue(ctx context.Context, identifier string) (*VerificationRecord, error)
// CreateVerificationValue creates or replaces a verification record in storage.
//
// Function:
// Persists a newly generated SMS OTP verification record.
//
// Storage:
// Cache (Redis / In-Memory TTL) - Short-lived key-value with TTL equal to token validity.
//
// 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);
//
// Example Cache (Redis):
// err := rdb.Set(ctx, "phoneotp:" + record.Identifier, bytes, ttl).Err()
CreateVerificationValue(ctx context.Context, record *VerificationRecord) error
// UpdateVerificationValue updates the value and expiry of an existing verification record.
//
// Function:
// Updates attempts counter or regenerates SMS OTP code.
//
// Storage:
// Cache (Redis / In-Memory TTL) - Key update with adjusted TTL.
//
// 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;
//
// Example Cache (Redis):
// err := rdb.Set(ctx, "phoneotp:" + identifier, bytes, ttl).Err()
UpdateVerificationValue(ctx context.Context, identifier, value string, expiresAt time.Time) error
// DeleteVerificationValue removes a verification record from storage by identifier.
//
// Function:
// Explicit removal of an SMS OTP record upon invalidation.
//
// Storage:
// Cache (Redis / In-Memory TTL) - Key eviction from memory/Redis.
//
// 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;
//
// Example Cache (Redis):
// err := rdb.Del(ctx, "phoneotp:" + identifier).Err()
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.
//
// Function:
// Single-use SMS OTP verification and atomic consumption.
//
// Storage:
// Cache (Redis GETDEL / Memory) - Atomic read-and-delete operation.
//
// 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;
//
// Example Cache (Redis):
// val, err := rdb.GetDel(ctx, "phoneotp:" + identifier).Bytes()
ConsumeVerificationValue(ctx context.Context, identifier string) (*VerificationRecord, error)
// GetUserByID retrieves a user entity matching the provided unique identifier.
//
// Function:
// Used to load user details by primary key ID.
//
// Storage:
// Database (GORM / SQL) - User primary key lookup.
//
// Arguments:
// - ctx: Request cancellation context.
// - userID: The user's primary key ID.
//
// Returns:
// - *entity.User: The matching user entity if found.
// - error: ErrUserNotFound if missing, or database error.
//
// Example SQL:
// SELECT id, name, email, phone_number, phone_number_verified, created_at, updated_at FROM users WHERE id = $1 LIMIT 1;
GetUserByID(ctx context.Context, userID string) (*entity.User, error)
// GetUserByPhoneNumber retrieves a user entity matching the provided phone number.
//
// Function:
// Used during SMS OTP sign-in or signup to locate user account.
//
// Storage:
// Database (GORM / SQL) - Phone number index query.
//
// Arguments:
// - ctx: Request cancellation context.
// - phoneNumber: The normalized phone number to query.
//
// Returns:
// - *entity.User: The matching user entity if found.
// - error: ErrUserNotFound if missing, or database error.
//
// Example SQL:
// SELECT id, name, email, phone_number, phone_number_verified, created_at, updated_at FROM users WHERE phone_number = $1 LIMIT 1;
GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*entity.User, error)
// CreateUser persists a newly registered user in storage.
//
// Function:
// Called when a new user registers via phone number OTP.
//
// Storage:
// Database (GORM / SQL) - Relational insert of new User entity.
//
// Arguments:
// - ctx: Request cancellation context.
// - params: Parameters containing name, email, phone number, and metadata.
//
// Returns:
// - *entity.User: The created user entity.
// - error: Nil on success, or database error.
//
// Example SQL:
// INSERT INTO users (id, name, email, phone_number, phone_number_verified, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6, $7);
CreateUser(ctx context.Context, params *dto.CreateUserParams) (*entity.User, error)
// UpdateUser updates modified fields of an existing user profile (e.g. PhoneNumber, PhoneNumberVerified).
//
// Function:
// Called when updating user attributes or setting PhoneNumberVerified to true.
//
// Storage:
// Database (GORM / SQL) - Relational update.
//
// Arguments:
// - ctx: Request cancellation context.
// - user: The modified user entity.
//
// Returns:
// - error: Nil on success, or database error.
//
// Example SQL:
// UPDATE users SET phone_number = $1, phone_number_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.
//
// Function:
// Used to locate provider credentials for a user.
//
// Storage:
// Database (GORM / SQL) - Account credentials record lookup.
//
// 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: ErrCredentialAccountNotFound 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.
//
// Function:
// Persists account credential linking record.
//
// Storage:
// Database (GORM / SQL) - Account entity creation.
//
// 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.
//
// Function:
// Called during password reset or credential update.
//
// Storage:
// Database (GORM / SQL) - Account password hash update.
//
// 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
// CreateSession persists a new active user session in storage.
//
// Function:
// Creates a new active session upon successful SMS OTP verification.
//
// Storage:
// Database (GORM / SQL) - Active session creation.
//
// 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).
//
// Function:
// Bulk invalidation of user sessions.
//
// Storage:
// Database (GORM / SQL) - Bulk session removal.
//
// 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 Phone Number plugin. Implement this interface on your custom database adapter (e.g. PostgreSQL, MySQL, SQLite, MongoDB, GORM).
Implementation Example (GORM / database/sql): ¶
type GormPhoneNumberRepository struct {
db *gorm.DB
}
func (r *GormPhoneNumberRepository) FindVerificationValue(ctx context.Context, identifier string) (*phonenumber.VerificationRecord, error) {
var rec phonenumber.VerificationRecord
if err := r.db.WithContext(ctx).Where("identifier = ?", identifier).First(&rec).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, err
}
return &rec, nil
}
Storage and Caching Recommendation (Redis TTL Storage): ¶
SMS OTP codes (`VerificationRecord`) are short-lived, single-use credentials. Storing them in Redis with automatic key expiration (TTL) guarantees auto-cleanup without periodic DB purges:
type RedisPhoneNumberRepository struct {
redis *redis.Client
}
func (r *RedisPhoneNumberRepository) CreateVerificationValue(ctx context.Context, record *phonenumber.VerificationRecord) error {
bytes, _ := json.Marshal(record)
ttl := time.Until(record.ExpiresAt)
return r.redis.Set(ctx, "phoneotp:"+record.Identifier, bytes, ttl).Err()
}
func (r *RedisPhoneNumberRepository) ConsumeVerificationValue(ctx context.Context, identifier string) (*phonenumber.VerificationRecord, error) {
key := "phoneotp:" + identifier
val, err := r.redis.GetDel(ctx, key).Bytes() // Atomic single-use retrieval & deletion
if err != nil {
return nil, phonenumber.ErrOTPNotFound
}
var rec phonenumber.VerificationRecord
_ = json.Unmarshal(val, &rec)
return &rec, nil
}
type RequestPasswordResetParams ¶
type RequestPasswordResetParams struct {
// PhoneNumber is the account phone number requesting a password reset.
PhoneNumber string `json:"phone_number"`
plugin.ExtraContainer
}
RequestPasswordResetParams defines parameters to request a password reset OTP via SMS.
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 {
// PhoneNumber is the account phone number.
PhoneNumber string `json:"phone_number"`
// 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"`
plugin.ExtraContainer
}
ResetPasswordParams defines parameters for setting a new password using a verified SMS OTP.
type ResetPasswordResult ¶
type ResetPasswordResult struct {
// Success indicates if the password was successfully reset.
Success bool `json:"success"`
}
ResetPasswordResult reports whether the password was successfully reset.
type SendOTPData ¶
type SendOTPData struct {
// PhoneNumber is the destination recipient phone number.
PhoneNumber string `json:"phone_number"`
// Code is the raw numeric OTP verification code to deliver.
Code string `json:"code"`
// Type indicates the specific operation requiring verification ("phone-verification", "phone-password-reset").
Type OTPType `json:"type"`
// Extra holds dynamic metadata passed through event interceptors.
Extra map[string]any `json:"extra,omitempty"`
}
SendOTPData contains the parameters delivered to the transactional SMS delivery callback.
type SendOTPFunc ¶
type SendOTPFunc func(ctx context.Context, data SendOTPData) error
SendOTPFunc defines the callback function invoked to dispatch an SMS OTP.
type SendOTPParams ¶
type SendOTPParams struct {
// PhoneNumber is the destination recipient phone number (required).
PhoneNumber string `json:"phone_number"`
plugin.ExtraContainer
}
SendOTPParams defines parameters required to dispatch an OTP to a user's phone number.
type SendOTPResult ¶
type SendOTPResult 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"`
}
SendOTPResult contains the delivery status and expiry of the dispatched OTP.
type SignInParams ¶
type SignInParams struct {
// PhoneNumber is the registered user's phone number.
PhoneNumber string `json:"phone_number"`
// Password is the plain text password.
Password string `json:"password"`
// RememberMe extends session lifespan if set to true.
RememberMe *bool `json:"remember_me,omitempty"`
plugin.ExtraContainer
}
SignInParams defines parameters for credential-based phone number + password login.
type SignInResult ¶
type SignInResult struct {
// User is the authenticated 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"`
}
SignInResult contains the authenticated user and active session.
type SignInSuccessPayload ¶
type SignInSuccessPayload struct {
User *entity.User `json:"user"`
Session *entity.Session `json:"session,omitempty"`
IsNewUser bool `json:"is_new_user"`
plugin.ExtraContainer
}
SignInSuccessPayload reports authentication or auto-registration details upon phone login.
type SignUpOnVerificationConfig ¶
type SignUpOnVerificationConfig struct {
// GetTempEmail resolves a fallback email for auto-created users given their phone number.
GetTempEmail func(phoneNumber string) string
// GetTempName resolves a fallback display name for auto-created users given their phone number.
GetTempName func(phoneNumber string) string
}
SignUpOnVerificationConfig configures automatic user creation upon phone verification.
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. "phone-verification-otp-+1234567890").
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 VerifyOTPData ¶
type VerifyOTPData struct {
// PhoneNumber is the destination recipient phone number.
PhoneNumber string `json:"phone_number"`
// Code is the verification code submitted by the user.
Code string `json:"code"`
// Extra holds dynamic metadata passed through event interceptors.
Extra map[string]any `json:"extra,omitempty"`
}
VerifyOTPData contains the parameters passed to the custom verification callback.
type VerifyOTPFunc ¶
type VerifyOTPFunc func(ctx context.Context, data VerifyOTPData) (bool, error)
VerifyOTPFunc defines an optional external OTP verification callback (e.g. Twilio Verify).
type VerifyParams ¶
type VerifyParams struct {
// PhoneNumber is the phone number being verified (required).
PhoneNumber string `json:"phone_number"`
// Code is the verification code submitted by the user (required).
Code string `json:"code"`
// UserID is the ID of the authenticated user (required if UpdatePhoneNumber is true).
UserID string `json:"user_id,omitempty"`
// UpdatePhoneNumber indicates whether to attach the verified phone number to an existing user session.
UpdatePhoneNumber bool `json:"update_phone_number,omitempty"`
// DisableSession prevents creating an active authentication session upon successful verification.
DisableSession bool `json:"disable_session,omitempty"`
plugin.ExtraContainer
}
VerifyParams defines parameters to verify a phone number OTP for login, registration, or profile update.
type VerifyResult ¶
type VerifyResult struct {
// Success indicates successful phone verification.
Success bool `json:"success"`
// User is the authenticated, newly provisioned, or updated user entity.
User *entity.User `json:"user"`
// SessionToken is the raw session token if a session was created.
SessionToken string `json:"session_token,omitempty"`
// Session is the active session entity if created.
Session *entity.Session `json:"session,omitempty"`
}
VerifyResult contains the updated user profile and optional created session.