Documentation
¶
Overview ¶
contract.go: Wires the magiclink plugin into the forge-dashboard contract surface via plugin.ContractContributor.
Package magiclink provides passwordless magic link authentication for AuthSome.
The magic link plugin allows users to sign in by clicking a one-time link sent to their email. It uses the core verification entity (type="magic_link") for token storage, so no additional database tables are required.
Usage:
mailer := &MyMailer{} // implements magiclink.Mailer
eng, _ := authsome.NewEngine(
authsome.WithStore(store),
authsome.WithPlugin(magiclink.New(magiclink.Config{
Mailer: mailer,
})),
)
Index ¶
- Constants
- Variables
- type CodeMailer
- type Config
- type MagicLinkMessage
- type Mailer
- type MailerFunc
- type Plugin
- func (p *Plugin) DeclareSettings(m *settings.Manager) error
- func (p *Plugin) Name() string
- func (p *Plugin) OnInit(_ context.Context, engine plugin.Engine) error
- func (p *Plugin) RegisterContract(d *dispatcher.Dispatcher, reg contract.Registry, wreg contract.WardenRegistry, ...) error
- func (p *Plugin) RegisterRoutes(router forge.Router) error
- func (p *Plugin) SetAppID(appID string)
- func (p *Plugin) SetStore(s store.Store)
- type Purpose
- type SendRequest
- type SendResponse
- type VerifyCodeRequest
- type VerifyRequest
- type VerifyResponse
Constants ¶
const MaxCodeAttempts = 5
MaxCodeAttempts caps the tries against one emailed code. Every try counts, and the code is discarded once they are spent: a six-digit code is a million guesses, and this keeps the odds of one landing at five in a million per email sent. The route limiter is the other defence, but it is absent when rate limiting is off, so the code bounds its own attempts the way an MFA ticket does.
const VerificationTypeMagicLink account.VerificationType = "magic_link"
VerificationTypeMagicLink is the verification type used for magic link tokens.
Variables ¶
var ( // SettingTokenTTLSeconds controls the magic link token lifetime in seconds. SettingTokenTTLSeconds = settings.Define("magiclink.token_ttl_seconds", 600, settings.WithDisplayName("Token TTL (seconds)"), settings.WithDescription("Lifetime of magic link tokens in seconds"), settings.WithCategory("Magic Link"), settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp), settings.WithInputType(formconfig.FieldNumber), settings.WithUIValidation(formconfig.Validation{Required: true, Min: intPtr(60), Max: intPtr(3600)}), settings.WithHelpText("How long magic link tokens remain valid. Default: 600 (10 minutes)"), settings.WithOrder(10), ) // SettingSessionTokenTTLSeconds controls the session token lifetime for magic link sessions. SettingSessionTokenTTLSeconds = settings.Define("magiclink.session_token_ttl_seconds", 3600, settings.WithDisplayName("Session Token TTL (seconds)"), settings.WithDescription("Lifetime of sessions created via magic link in seconds"), settings.WithCategory("Magic Link"), settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp), settings.WithInputType(formconfig.FieldNumber), settings.WithUIValidation(formconfig.Validation{Required: true, Min: intPtr(300), Max: intPtr(86400)}), settings.WithHelpText("How long sessions created via magic link remain valid. Default: 3600 (1 hour)"), settings.WithOrder(20), ) // SettingSessionRefreshTTLSeconds controls the refresh token lifetime for magic link sessions. SettingSessionRefreshTTLSeconds = settings.Define("magiclink.session_refresh_ttl_seconds", 2592000, settings.WithDisplayName("Refresh Token TTL (seconds)"), settings.WithDescription("Lifetime of refresh tokens for magic link sessions in seconds"), settings.WithCategory("Magic Link"), settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp), settings.WithInputType(formconfig.FieldNumber), settings.WithUIValidation(formconfig.Validation{Required: true, Min: intPtr(3600), Max: intPtr(7776000)}), settings.WithHelpText("How long refresh tokens remain valid. Default: 2592000 (30 days)"), settings.WithOrder(30), ) // SettingEmailCode turns on the 6-digit sign-in code that goes out in the // same email as the link. Unset, Config.EmailCode decides. SettingEmailCode = settings.Define("magiclink.email_code", false, settings.WithDisplayName("Email a sign-in code"), settings.WithDescription("Send a 6-digit code alongside the magic link"), settings.WithCategory("Magic Link"), settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp), settings.WithInputType(formconfig.FieldSwitch), settings.WithHelpText("The code and the link share one lifetime, and using either one uses up both. Needs a mailer that implements magiclink.CodeMailer."), settings.WithOrder(40), ) // SettingAllowSignUp lets /v1/magic-link/send create an account for an // unknown address when the request asks for it. Unset, // Config.AllowSignUp decides. SettingAllowSignUp = settings.Define("magiclink.allow_signup", false, settings.WithDisplayName("Passwordless sign-up"), settings.WithDescription("Create an account with no password when someone asks for a link with create_user"), settings.WithCategory("Magic Link"), settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp), settings.WithInputType(formconfig.FieldSwitch), settings.WithHelpText("Sign-up still has to be enabled for the app, and the sign-up hooks (waitlist, captcha) still apply."), settings.WithOrder(50), ) )
Functions ¶
This section is empty.
Types ¶
type CodeMailer ¶ added in v1.12.0
type CodeMailer interface {
Mailer
SendMagicLinkWithCode(ctx context.Context, msg MagicLinkMessage) error
}
CodeMailer is a Mailer that can also deliver the sign-in code and knows why the email is going out. When the configured Mailer implements it, the plugin calls SendMagicLinkWithCode instead of SendMagicLink. The email code is only minted for a mailer that implements it.
type Config ¶
type Config struct {
// Mailer sends magic link emails. Required. Implement CodeMailer as well
// to deliver the email code and the purpose of the email.
Mailer Mailer
// EmailCode also mints a 6-digit code with every link, delivered in the
// same email, which the user can type at /v1/magic-link/verify-code.
// The code and link share one lifetime and using either uses up both.
// It needs a Mailer that implements CodeMailer. The
// magiclink.email_code setting overrides it per app. Default: false.
EmailCode bool
// AllowSignUp lets /v1/magic-link/send create an account with no
// password when the request carries create_user and no account has the
// address. Sign-up must also be enabled for the app. The
// magiclink.allow_signup setting overrides it per app. Default: false.
AllowSignUp bool
// TokenTTL is the lifetime of magic link tokens (default: 10 minutes).
TokenTTL time.Duration
// SessionTokenTTL is the lifetime of sessions created via magic link (default: 1 hour).
SessionTokenTTL time.Duration
// SessionRefreshTTL is the lifetime of refresh tokens for magic link sessions (default: 30 days).
SessionRefreshTTL time.Duration
}
Config configures the magic link plugin.
type MagicLinkMessage ¶ added in v1.12.0
type MagicLinkMessage struct {
// AppID is the app the link signs in to.
AppID string
// Email is the recipient.
Email string
// Token goes in the link. The client posts it to /v1/magic-link/verify.
Token string
// Code is the 6-digit code the user can type instead of clicking. It is
// empty unless the email code is on for the app.
Code string
// Purpose is PurposeSignIn or PurposeSignUp, so the email can greet a
// new account differently.
Purpose Purpose
// ExpiresAt is when the link and the code stop working.
ExpiresAt time.Time
// Name is the user's name, when the account has one.
Name string
}
MagicLinkMessage is everything a CodeMailer needs to write the email.
type MailerFunc ¶ added in v1.9.0
MailerFunc is an adapter to use a plain function as a Mailer.
func (MailerFunc) SendMagicLink ¶ added in v1.9.0
func (f MailerFunc) SendMagicLink(ctx context.Context, email, token string) error
SendMagicLink implements Mailer.
type Plugin ¶
type Plugin struct {
// contains filtered or unexported fields
}
Plugin is the magic link authentication plugin.
func (*Plugin) DeclareSettings ¶ added in v1.9.0
DeclareSettings implements plugin.SettingsProvider.
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 ¶
RegisterRoutes registers magic link HTTP endpoints on a forge.Router.
type SendRequest ¶
type SendRequest struct {
Email string `json:"email"`
AppID string `json:"app_id,omitempty"`
// CreateUser asks for an account to be created, with no password, when
// no account has the address. It needs passwordless sign-up to be on.
CreateUser bool `json:"create_user,omitempty"`
// Name is the new account's name, used only when one is created.
Name string `json:"name,omitempty"`
// CaptchaToken is the captcha answer for a sign-up, when the app
// requires one. The X-Captcha-Token header works too.
CaptchaToken string `json:"captcha_token,omitempty"`
}
SendRequest is the request body for sending a magic link.
type SendResponse ¶
type SendResponse struct {
Status string `json:"status"`
// ExpiresIn is how many seconds the link (and the code) stay valid.
ExpiresIn int `json:"expires_in"`
// CodeLength is the number of digits in the emailed code, or absent
// when no code is sent.
CodeLength int `json:"code_length,omitempty"`
}
SendResponse is returned when a magic link is sent. It is the same whether or not an account has the address.
type VerifyCodeRequest ¶ added in v1.12.0
type VerifyCodeRequest struct {
Email string `json:"email"`
Code string `json:"code"`
AppID string `json:"app_id,omitempty"`
}
VerifyCodeRequest is the request body for signing in with the emailed code.
type VerifyRequest ¶ added in v1.9.0
VerifyRequest is the request body for verifying a magic link.