Help us shape the future of Go! Take the 2026 Go Developer Survey

phone

package
v1.9.0 Latest Latest
Warning

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

Go to latest
Published: Nov 2, 2026 License: MIT Imports: 27 Imported by: 0

README

phone

Sign in with a phone number and a one-time code, no password anywhere.

This is the flow most consumer apps outside the West default to, because a phone number is the identity people actually have. The plugin reuses the SMS bridge and code generation that mfa already ships, so you're not configuring two SMS paths.

By default an unrecognised number creates an account. Turn AutoCreate off if you want signup to be a separate, gated step.

Use it when

  • Your users are mobile-first and a phone number is more reliable than an email address
  • You're launching in a market where SMS OTP is the expected sign-in
  • You want signup and sign-in to be the same two-screen flow

Skip it when

  • SIM swap is in your threat model. A phone number is a weak root of identity for anything financial
  • SMS costs matter at your volume. Every sign-in is a paid message
  • Your users are on desktop and will not have the phone to hand

Wiring

authsome.WithPlugin(phone.New(phone.Config{
    CodeTTL: 5 * time.Minute,
}))

The engine's SMS bridge is used when SMSSender is nil, so wire Twilio or your provider once at the engine level and both this plugin and mfa pick it up.

Config

Field Type Default What it does
SMSSender bridge.SMSSender engine bridge Where codes are sent from
CodeTTL time.Duration 5m How long an OTP stays valid
AutoCreate *bool true Create a user when the number is not known. false errors instead

Settings

Key Default
phone.code_ttl_seconds 300
phone.auto_create true

Endpoints

Registered under /v1/phone.

Method Path Purpose
POST /start Send an OTP to the number
POST /verify Exchange the OTP for a session

Lifecycle hooks

None. It registers as an AuthMethodContributor so phone shows up as a linked method on the user record.

mfa shares the SMS plumbing. If you want the same low-friction flow over email instead, use magiclink.

Documentation

Overview

contract.go: Wires the phone plugin into the forge-dashboard contract surface via plugin.ContractContributor. The manifest + handlers live in plugins/phone/contract/.

Package phone provides phone number + OTP authentication as a primary auth method. It reuses the SMS bridge, Twilio adapter, and MFA SMS code generation infrastructure.

Index

Constants

This section is empty.

Variables

View Source
var (
	// SettingCodeTTLSeconds controls the lifetime of OTP codes in seconds.
	SettingCodeTTLSeconds = settings.Define("phone.code_ttl_seconds", 300,
		settings.WithDisplayName("OTP Code TTL (seconds)"),
		settings.WithDescription("Lifetime of OTP verification codes in seconds"),
		settings.WithCategory("Phone Auth"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithInputType(formconfig.FieldNumber),
		settings.WithUIValidation(formconfig.Validation{Required: true, Min: intPtr(60), Max: intPtr(900)}),
		settings.WithHelpText("How long OTP codes remain valid. Default: 300 (5 minutes)"),
		settings.WithOrder(10),
	)

	// SettingAutoCreate controls whether to create new users for unknown phone numbers.
	SettingAutoCreate = settings.Define("phone.auto_create", true,
		settings.WithDisplayName("Auto-Create Users"),
		settings.WithDescription("Automatically create new users when an unregistered phone number is used"),
		settings.WithCategory("Phone Auth"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithEnforceable(),
		settings.WithHelpText("When disabled, only existing users can authenticate via phone"),
		settings.WithOrder(20),
	)
)

Functions

This section is empty.

Types

type Config

type Config struct {
	// SMSSender sends SMS messages. If nil, the engine's SMS bridge is used.
	SMSSender bridge.SMSSender

	// CodeTTL is the lifetime of OTP codes (default: 5 minutes).
	CodeTTL time.Duration

	// AutoCreate controls whether to create new users when the phone number
	// is not found. If false, unregistered phone numbers receive an error.
	// Default: true.
	AutoCreate *bool
}

Config configures the phone auth plugin.

type Plugin

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

Plugin is the phone authentication plugin.

func New added in v1.9.0

func New(cfg ...Config) *Plugin

New creates a new phone auth plugin.

func (*Plugin) DeclareSettings added in v1.9.0

func (p *Plugin) DeclareSettings(m *settings.Manager) error

DeclareSettings implements plugin.SettingsProvider.

func (*Plugin) ListUserAuthMethods added in v1.9.0

func (p *Plugin) ListUserAuthMethods(ctx context.Context, userID id.UserID) ([]*plugin.AuthMethod, error)

ListUserAuthMethods reports phone as a linked auth method if the user has a verified phone number.

func (*Plugin) Name added in v1.9.0

func (p *Plugin) Name() string

Name returns the plugin name.

func (*Plugin) OnInit added in v1.9.0

func (p *Plugin) OnInit(_ context.Context, engine plugin.Engine) error

OnInit captures dependencies from the engine.

func (*Plugin) RegisterContract added in v1.9.0

func (p *Plugin) RegisterContract(
	d *dispatcher.Dispatcher,
	reg contract.Registry,
	wreg contract.WardenRegistry,
	engine plugin.Engine,
) error

func (*Plugin) RegisterRoutes

func (p *Plugin) RegisterRoutes(router forge.Router) error

RegisterRoutes registers phone auth HTTP endpoints on a forge.Router.

func (*Plugin) SetAppID added in v1.9.0

func (p *Plugin) SetAppID(appID string)

SetAppID sets the app ID for the plugin (used in testing).

func (*Plugin) SetCeremonyStore added in v1.9.0

func (p *Plugin) SetCeremonyStore(s ceremony.Store)

SetCeremonyStore injects the short-lived challenge store, which OnInit otherwise resolves from the engine (used in testing).

func (*Plugin) SetStore added in v1.9.0

func (p *Plugin) SetStore(s store.Store)

SetStore allows direct store injection for testing.

type StartRequest added in v1.9.0

type StartRequest struct {
	Phone string `json:"phone"`
	AppID string `json:"app_id,omitempty"`
}

StartRequest is the request body for starting phone auth.

type StartResponse added in v1.9.0

type StartResponse struct {
	Status    string `json:"status"`
	ExpiresIn int    `json:"expires_in"` // seconds
}

StartResponse is returned when an OTP is sent.

type VerifyRequest

type VerifyRequest struct {
	Phone string `json:"phone"`
	Code  string `json:"code"`
	AppID string `json:"app_id,omitempty"`
}

VerifyRequest is the request body for verifying phone OTP.

type VerifyResponse added in v1.9.0

type VerifyResponse struct {
	User         *user.User `json:"user"`
	SessionToken string     `json:"session_token"`
	RefreshToken string     `json:"refresh_token"`
	ExpiresAt    time.Time  `json:"expires_at"`
	NewUser      bool       `json:"new_user"`
}

VerifyResponse is returned when phone auth succeeds.

Directories

Path Synopsis
Package contract wires the phone plugin's deep-link settings page into the forge-dashboard contract registry.
Package contract wires the phone plugin's deep-link settings page into the forge-dashboard contract registry.

Jump to

Keyboard shortcuts

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