Documentation
¶
Overview ¶
Package sms provides phone-based OTP (One-Time Password) verification and authentication.
This plugin enables:
- Phone number verification via SMS OTP codes
- Phone+password registration and login
- Multi-factor authentication (MFA) via SMS
- Password reset via phone verification
OTP Flow:
- User requests OTP via SendOTP endpoint (requires authentication)
- Plugin generates 6-digit code (configurable length)
- Code sent via configured SMS provider (Twilio, AWS SNS, etc.)
- Code stored in database with expiry (default: 10 minutes)
- User submits code via VerifyOTP endpoint
- Plugin validates code and marks phone as verified
Phone+Password Flow:
- User registers with phone+password via /register endpoint
- Password hashed with bcrypt and stored
- User can login with phone+password via /login endpoint
- Session created on successful authentication
Route Structure:
- POST /sms/send - Send SMS OTP code (protected)
- POST /sms/verify - Verify SMS OTP code (public)
- POST /sms/login - Login with phone+password (public)
- POST /sms/register - Register with phone+password (public)
Provider Integration: Implement the Provider interface to use your SMS service:
type MyTwilioProvider struct { accountSID, authToken, from string }
func (p *MyTwilioProvider) SendOTP(to, code string) error {
// Send SMS with OTP code via Twilio API
}
Index ¶
- Constants
- func GetMigrations(dialect plugins.Dialect) ([]plugins.Migration, error)
- func ValidatePhoneNumber(phone string) error
- type Config
- type Handlers
- func (h *Handlers) LoginWithPhoneHandler(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) RegisterWithPhoneHandler(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) SendOTPHandler(w http.ResponseWriter, r *http.Request)
- func (h *Handlers) VerifyOTPHandler(w http.ResponseWriter, r *http.Request)
- type Plugin
- func (p *Plugin) CreateUserWithPhoneAndPassword(ctx context.Context, name, phone, password string) (*smstypes.User, error)
- func (p *Plugin) Dependencies() []plugins.Dependency
- func (p *Plugin) Description() string
- func (p *Plugin) EnrichUser(ctx context.Context, user *core.EnrichedUser) error
- func (p *Plugin) GetMigrations() []plugins.Migration
- func (p *Plugin) GetUserByPhone(ctx context.Context, phone string) (*auth.User, error)
- func (p *Plugin) Init(_ context.Context, a plugins.Aegis) error
- func (p *Plugin) MarkPhoneVerified(ctx context.Context, phone string) error
- func (p *Plugin) MountRoutes(r router.Router, prefix string)
- func (p *Plugin) Name() string
- func (p *Plugin) ProvidesAuthMethods() []string
- func (p *Plugin) RequiresTables() []string
- func (p *Plugin) SendOTP(ctx context.Context, phoneNumber, purpose string) error
- func (p *Plugin) UpdateUserPhone(ctx context.Context, userID, phone string, verified bool) error
- func (p *Plugin) VerifyOTP(ctx context.Context, phoneNumber, code string) (bool, error)
- func (p *Plugin) Version() string
- type Provider
Constants ¶
const ( // Request schemas SchemaLoginWithPhoneRequest = "LoginWithPhoneRequest" SchemaRegisterWithPhoneRequest = "RegisterWithPhoneRequest" SchemaSendOTPRequest = "SendOTPRequest" SchemaVerifyOTPRequest = "VerifyOTPRequest" )
Schema names for OpenAPI specification generation. These constants define the OpenAPI schema names for SMS request/response types. They are used in route metadata to generate accurate API documentation with typed request/response examples.
Variables ¶
This section is empty.
Functions ¶
func GetMigrations ¶
GetMigrations returns all database migrations for the SMS plugin.
This function loads migrations from embedded SQL files and returns them in version order.
Version Numbering:
- Version 001+: Migrations from migrations/<dialect>/<version>_<description>.<up|down>.sql
Migration File Format:
- Up migration: 001_initial.up.sql
- Down migration: 001_initial.down.sql
Parameters:
- dialect: Database dialect (postgres, mysql, sqlite)
Returns:
- []plugins.Migration: Sorted list of migrations (oldest first)
- error: If migration files cannot be read or parsed
func ValidatePhoneNumber ¶
ValidatePhoneNumber validates a phone number format using libphonenumber.
This function performs comprehensive phone number validation:
- Basic format check (regex)
- International format validation (E.164)
- Country code verification
- Number length validation
Parameters:
- phone: Phone number to validate (can include country code prefix)
Returns:
- error: If phone number is empty or invalid
Example:
err := ValidatePhoneNumber("+14155551234") // Valid US number
err := ValidatePhoneNumber("+442071838750") // Valid UK number
Types ¶
type Config ¶
type Config struct {
Provider Provider // SMS sending provider (required for production)
OTPExpiry time.Duration // OTP expiry duration (default: 10 minutes)
OTPLength int // OTP code length (default: 6)
}
Config holds SMS plugin configuration.
Example:
cfg := &sms.Config{
Provider: myTwilioProvider,
OTPExpiry: 15 * time.Minute,
OTPLength: 8,
}
type Handlers ¶
type Handlers struct {
// contains filtered or unexported fields
}
Handlers encapsulates SMS plugin HTTP handlers.
func NewHandlers ¶
NewHandlers creates SMS plugin handlers.
Parameters:
- plugin: Initialized SMS plugin
Returns:
- *Handlers: Handler instance ready for route registration
func (*Handlers) LoginWithPhoneHandler ¶
func (h *Handlers) LoginWithPhoneHandler(w http.ResponseWriter, r *http.Request)
LoginWithPhoneHandler handles phone+password login.
Authentication Flow:
- Validate phone number format (E.164)
- Retrieve user by phone number
- Verify password with bcrypt
- Create session
- Set session cookie
Endpoint:
- Method: POST
- Path: /sms/login
- Auth: Public
Request Body:
{
"phoneNumber": "+14155551234",
"password": "SecurePassword123!"
}
Response (200 OK):
{
"success": true,
"message": "Login successful",
"data": {
"user": {"id": "user_123", "phone": "+14155551234", ...}
}
}
func (*Handlers) RegisterWithPhoneHandler ¶
func (h *Handlers) RegisterWithPhoneHandler(w http.ResponseWriter, r *http.Request)
RegisterWithPhoneHandler handles phone+password registration.
Registration Flow:
- Validate phone number format (E.164)
- Check if phone already exists
- Create user with hashed password
- Create session (auto-login)
- Set session cookie
Endpoint:
- Method: POST
- Path: /sms/register
- Auth: Public
Request Body:
{
"name": "John Doe",
"phoneNumber": "+14155551234",
"password": "SecurePassword123!",
"avatar": "https://example.com/avatar.jpg" // Optional
}
Response (201 Created):
{
"success": true,
"message": "Registration successful",
"data": {
"user": {"id": "user_123", "phone": "+14155551234", "phoneVerified": false}
}
}
func (*Handlers) SendOTPHandler ¶
func (h *Handlers) SendOTPHandler(w http.ResponseWriter, r *http.Request)
SendOTPHandler handles sending OTP via SMS.
This endpoint is protected to prevent spam/abuse and SMS cost attacks. Only authenticated users can request OTP codes.
Endpoint:
- Method: POST
- Path: /sms/send
- Auth: Required (session)
Request Body:
{
"phoneNumber": "+14155551234",
"userId": "user_123",
"purpose": "phone_verification" // or "password_reset", "login_mfa"
}
Response (200 OK):
{
"success": true,
"message": "OTP sent successfully"
}
func (*Handlers) VerifyOTPHandler ¶
func (h *Handlers) VerifyOTPHandler(w http.ResponseWriter, r *http.Request)
VerifyOTPHandler handles verifying SMS OTP codes.
This endpoint is public to allow users to verify their phone numbers without requiring prior authentication.
Endpoint:
- Method: POST
- Path: /sms/verify
- Auth: Public
Request Body:
{
"phoneNumber": "+14155551234",
"code": "123456",
"purpose": "phone_verification"
}
Response (200 OK):
{
"success": true,
"message": "OTP verified successfully"
}
Response (400 Bad Request):
{
"success": false,
"error": "Invalid or expired OTP"
}
type Plugin ¶
type Plugin struct {
// contains filtered or unexported fields
}
Plugin provides phone-based OTP verification and authentication.
This plugin integrates with SMS service providers to send OTP codes and supports phone+password authentication as an alternative auth method.
Features:
- Configurable OTP length and expiry
- Pluggable SMS providers (Twilio, AWS SNS, Vonage, etc.)
- International phone number validation (E.164 format)
- Password hashing with bcrypt
- Session management integration
func New ¶
New creates a new SMS plugin instance.
Parameters:
- cfg: Plugin configuration (can be nil for defaults)
- store: Custom SMSStore implementation (can be nil, will use DefaultSMSStore)
- dialect: Database dialect (optional, defaults to PostgreSQL)
Returns:
- *Plugin: Configured plugin ready for initialization
Example:
plugin := sms.New(&sms.Config{
Provider: myTwilioProvider,
OTPExpiry: 15 * time.Minute,
OTPLength: 8,
}, nil, plugins.DialectPostgres)
func (*Plugin) CreateUserWithPhoneAndPassword ¶
func (p *Plugin) CreateUserWithPhoneAndPassword(ctx context.Context, name, phone, password string) (*smstypes.User, error)
CreateUserWithPhoneAndPassword creates a new user with name, password account, and phone number.
func (*Plugin) Dependencies ¶
func (p *Plugin) Dependencies() []plugins.Dependency
Dependencies returns external package dependencies
func (*Plugin) Description ¶
Description returns a human-readable description for logging.
func (*Plugin) EnrichUser ¶ added in v1.2.1
EnrichUser implements plugins.UserEnricher to add phone verification status.
This method is called automatically by the authentication system after user lookup. It adds the user's phone verification status to the EnrichedUser, making it available in API responses without requiring separate queries.
Fields Added:
- "phoneVerified" (bool): Whether the user's phone has been verified
Parameters:
- ctx: Request context
- user: EnrichedUser to populate with phone verification data
Returns:
- error: If lookup fails
func (*Plugin) GetMigrations ¶
GetMigrations returns the plugin migrations
func (*Plugin) GetUserByPhone ¶
GetUserByPhone retrieves a user by phone number
func (*Plugin) MarkPhoneVerified ¶ added in v1.2.2
MarkPhoneVerified marks a user's phone as verified in the database.
This is called automatically by VerifyOTPHandler after successful OTP verification. It can also be called programmatically to mark a phone number as verified.
Parameters:
- ctx: Request context
- phone: Phone number to mark as verified (E.164 format)
Returns:
- error: If user lookup or update fails
Example:
err := plugin.MarkPhoneVerified(ctx, "+14155551234")
func (*Plugin) MountRoutes ¶
MountRoutes registers HTTP routes for the SMS plugin
func (*Plugin) ProvidesAuthMethods ¶
ProvidesAuthMethods returns authentication methods provided
func (*Plugin) RequiresTables ¶
RequiresTables returns the core tables this plugin reads from.
func (*Plugin) UpdateUserPhone ¶ added in v1.2.2
UpdateUserPhone updates a user's phone number and verification status programmatically.
Parameters:
- ctx: Request context
- userID: User ID to update
- phone: New phone number (E.164 format)
- verified: Whether the phone is verified
Returns:
- error: If update fails
Example:
err := plugin.UpdateUserPhone(ctx, "user_123", "+14155551234", false)
type Provider ¶
type Provider interface {
// SendOTP sends a one-time password to the specified phone number.
//
// Parameters:
// - to: Recipient phone number in E.164 format (e.g., "+14155551234")
// - code: OTP code to send (e.g., "123456")
//
// Returns:
// - error: If SMS sending fails
SendOTP(to, code string) error
// VerifyOTP verifies an OTP code for a phone number.
//
// Note: Most implementations delegate verification to the plugin's OTP storage.
// This method is available for provider-specific validation if needed.
//
// Parameters:
// - to: Phone number to verify
// - code: OTP code to verify
//
// Returns:
// - bool: true if OTP is valid
// - error: If verification fails
VerifyOTP(to, code string) (bool, error)
}
Provider is the interface that SMS service providers must implement.
Users should create their own implementation based on their SMS service (Twilio, AWS SNS, Vonage, MessageBird, Plivo, etc.).
Abstraction Benefits:
- Swap SMS providers without changing plugin code
- Test with mock providers
- Use different providers for different regions
Example Implementation (Twilio):
type TwilioProvider struct {
accountSID string
authToken string
from string // Twilio phone number
}
func (p *TwilioProvider) SendOTP(to, code string) error {
msgData := url.Values{}
msgData.Set("To", to)
msgData.Set("From", p.from)
msgData.Set("Body", fmt.Sprintf("Your verification code is: %s", code))
urlStr := fmt.Sprintf("https://api.twilio.com/2010-04-01/Accounts/%s/Messages.json", p.accountSID)
req, _ := http.NewRequest("POST", urlStr, strings.NewReader(msgData.Encode()))
req.SetBasicAuth(p.accountSID, p.authToken)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
client := &http.Client{}
resp, err := client.Do(req)
return err
}
func (p *TwilioProvider) VerifyOTP(to, code string) (bool, error) {
return true, nil // Verification handled by plugin
}
Example Implementation (AWS SNS):
type SNSProvider struct {
client *sns.Client
}
func (p *SNSProvider) SendOTP(to, code string) error {
_, err := p.client.Publish(context.TODO(), &sns.PublishInput{
PhoneNumber: aws.String(to),
Message: aws.String(fmt.Sprintf("Your OTP: %s", code)),
})
return err
}
Directories
¶
| Path | Synopsis |
|---|---|
|
Package defaultstore implements the SQL-backed default store for the sms plugin.
|
Package defaultstore implements the SQL-backed default store for the sms plugin. |
|
internal
|
|
|
Package types defines the domain models and request/response types used by the sms plugin.
|
Package types defines the domain models and request/response types used by the sms plugin. |