Documentation ¶
Overview ¶
Package authn provides passwordless, email-based user authentication.
The flow is the following:
- A user wants to login. He/she provides his/her email.
- A one-time entry code is emailed to him/her by Authenticator.SendEntryCode().
- User copies the entry code from the email, which can be verified by Authenticator.VerifyEntryCode().
- If the entry code was valid, a Token is presented whose value can be used later to authenticate the user.
- Authenticity of a user can be verified by Authenticator.VerifyToken().
- The user can be logged out by calling Authenticator.InvalidateToken().
The Authenticator automatically manages user identities. When an entry code verification passes, a user identity (User) is created if one does not yet exist for the email. This user identity (UserID) is attached to and returned with all tokens. A user may have multiple emails, and emails can be changed (Authenticator.SetUserEmails()) without affecting the user's identity.
A user may have multiple valid tokens (multiple sessions). Authenticator.InvalidateToken() only invalidates the given token. Authenticator.Tokens() may be used to query all valid sessions of a user by a token value, or Authenticator.UserTokens() by user ID.
Authenticator uses MongoDB as the persistent store, accessed via the official mongo-go driver.
Index ¶
- Constants
- Variables
- type Authenticator
- func (a *Authenticator[UserData]) GetUser(ctx context.Context, userID primitive.ObjectID) (user *User[UserData], err error)
- func (a *Authenticator[_]) InvalidateToken(ctx context.Context, tokenValue string) (err error)
- func (a *Authenticator[_]) SendEntryCode(ctx context.Context, email string, client *Client, data map[string]any) (err error)
- func (a *Authenticator[_]) SetUserEmails(ctx context.Context, userID primitive.ObjectID, loweredEmails []string) (err error)
- func (a *Authenticator[_]) Tokens(ctx context.Context, tokenValue string) (tokens []*Token, err error)
- func (a *Authenticator[_]) UserTokens(ctx context.Context, userID primitive.ObjectID) (tokens []*Token, err error)
- func (a *Authenticator[UserData]) VerifyEntryCode(ctx context.Context, code string, client *Client, validators ...Validator) (token *Token, err error)
- func (a *Authenticator[_]) VerifyToken(ctx context.Context, tokenValue string, client *Client, ...) (token *Token, err error)
- type Client
- type Config
- type EmailParams
- type EmailSenderFunc
- type Token
- type User
- type Validator
Constants ¶
const ( // DefaultAuthnDBName is the default for Config.AuthnDBName. DefaultAuthnDBName = "authn" // DefaultTokensCollectionName is the default for Config.TokensCollectionName. DefaultTokensCollectionName = "tokens" // DefaultUsersCollectionName is the default for Config.UsersCollectionName. DefaultUsersCollectionName = "users" // DefaultEntryCodeBytes is the default for Config.EntryCodeBytes. DefaultEntryCodeBytes = 8 // DefaultEntryCodeExpiration is the default for Config.EntryCodeExpiration. DefaultEntryCodeExpiration = 20 * time.Minute // DefaultTokenValueBytes is the default for Config.TokenValueBytes. DefaultTokenValueBytes = 24 // DefaultTokenExpiration is the default for Config.TokenExpiration. DefaultTokenExpiration = 6 * 31 * 24 * time.Hour // ~6 months )
const DefaultEmailTemplate = `` /* 259-byte string literal not displayed */
DefaultEmailTemplate is the default for Config.EmailTemplate.
Variables ¶
var ( // ErrAlreadyVerified indicates an attempt to verify an already // verified entry code. ErrAlreadyVerified = errors.New("already verified") // ErrUnknown indicates that the entry code or token value is unknown. ErrUnknown = errors.New("unknown") // ErrExpired indicates that the entry code or token has expired. ErrExpired = errors.New("expired") )
var ErrInvalidEmail = errors.New("invalid email")
ErrInvalidEmail indicates that the provided email address is invalid.
Functions ¶
This section is empty.
Types ¶
type Authenticator ¶
type Authenticator[UserData any] struct { // contains filtered or unexported fields }
Authenticator is the implementation of a passwordless authenticator. It's safe for concurrent use by multiple goroutines.
func NewAuthenticator ¶
func NewAuthenticator[UserData any]( mongoClient *mongo.Client, sendEmail EmailSenderFunc, cfg Config, ) *Authenticator[UserData]
NewAuthenticator creates a new Authenticator. This function panics if mongoClient or sendEmail are nil, or if Config.EmailTemplate is provided but is invalid.
func (*Authenticator[UserData]) GetUser ¶
func (a *Authenticator[UserData]) GetUser(ctx context.Context, userID primitive.ObjectID) (user *User[UserData], err error)
GetUser returns the user document for the given ID.
func (*Authenticator[_]) InvalidateToken ¶
func (a *Authenticator[_]) InvalidateToken(ctx context.Context, tokenValue string) (err error)
InvalidateToken invalidates the given token. Should be called when a user wants to log out (only the given session).
If the token value is unknown, ErrUnknown is returned. If the token has expired (or has already been invalidated), ErrExpired is returned.
func (*Authenticator[_]) SendEntryCode ¶
func (a *Authenticator[_]) SendEntryCode(ctx context.Context, email string, client *Client, data map[string]any) (err error)
SendEntryCode sends a one-time entry code to the given email address. Should be called when a user wants to login.
If client is provided, it will be saved as Token.EntryClient, At field filled with current timestamp. If client is nil, EntryClient will not be set.
If email is invalid, ErrInvalidEmail is returned.
data is set as EmailParams.Data, and will be available in the email template. The default email template does not use it, so it may be nil if you use the default email template.
func (*Authenticator[_]) SetUserEmails ¶
func (a *Authenticator[_]) SetUserEmails(ctx context.Context, userID primitive.ObjectID, loweredEmails []string) (err error)
SetUserEmails sets the assigned (lowercased) emails of a user.
Emails must be unique across all users. Attempting to set an email that is already associated to another user will result in an error.
func (*Authenticator[_]) Tokens ¶
func (a *Authenticator[_]) Tokens(ctx context.Context, tokenValue string) (tokens []*Token, err error)
Tokens returns all valid tokens associated with the owner of the given token.
If the token value is unknown, ErrUnknown is returned. If the token has expired (or has already been invalidated), ErrExpired is returned.
func (*Authenticator[_]) UserTokens ¶
func (a *Authenticator[_]) UserTokens(ctx context.Context, userID primitive.ObjectID) (tokens []*Token, err error)
UserTokens returns all valid tokens for the given user. No error is reported if the given user does not exists (tokens will be empty of course).
func (*Authenticator[UserData]) VerifyEntryCode ¶
func (a *Authenticator[UserData]) VerifyEntryCode(ctx context.Context, code string, client *Client, validators ...Validator) (token *Token, err error)
VerifyEntryCode verifies the given entry code. Should be called to verify user's email upon login.
If client is provided, it will be saved as Token.EntryClient, At field filled with current timestamp. If client is nil, EntryClient will not be updated.
If the entry code is unknown, ErrUnknown is returned. If the entry code has expired, ErrExpired is returned.
An entry code can only be verified once. If the entry code is known but has been verified before, ErrAlreadyVerified is returned.
If there are validators passed, they are called before the token is accepted and updated, in the order they are provided, which may veto the decision. If a validation error occurs, an error wrapping that is returned early. If a user exists associated with the token's email at the time of the verification, Token.UserID is set to the existing user's ID. If no such user exists, no user is created when calling the validators.
If validation passes and no user exists associated with the token's email address, a new user is created automatically, and Token.UserID is set to this new user's ID. Changes to the user's email later on will not affect Token.UserID.
func (*Authenticator[_]) VerifyToken ¶
func (a *Authenticator[_]) VerifyToken(ctx context.Context, tokenValue string, client *Client, validators ...Validator) (token *Token, err error)
VerifyToken verifies the given token value. Should be called to verify the authenticity of a logged in user.
If client is provided, it will be saved as Token.Client, At field filled with current timestamp, and Token.Used will also be incremented. If client is nil, Client will not be updated.
If the token value is unknown, ErrUnknown is returned. If the token has expired, ErrExpired is returned.
If there are validators passed, they are called before the token is accepted and updated, in the order they are provided, which may veto the decision. If a validation error occurs, an error wrapping that is returned early.
type Client ¶
type Client struct { // User agent of the client. UserAgent string `bson:"agent,omitempty"` // IP address of the client. IP string `bson:"ip,omitempty"` // At tells when the token was accessed. At time.Time `bson:"at"` // Data may hold arbitrary data. Data map[string]any `bson:"data,omitempty"` }
Client holds some information about the client.
type Config ¶
type Config struct { // AuthnDBName is the name of the database used by the Authenticator. AuthnDBName string // TokensCollectionName is the name of the database collection used by the // Authenticator to store tokens. TokensCollectionName string // UsersCollectionName is the name of the database collection used by the // Authenticator to store users. UsersCollectionName string // EntryCodeBytes tells how many bytes to use for entry codes. // The actual entry code is a hex string, will be twice as many hex digits. EntryCodeBytes int // EntryCodeExpiration tells how long an unverified entry code remains valid. EntryCodeExpiration time.Duration // TokenValueBytes tells how many bytes to use for token values. // The actual token string is base64, will be roughly 4/3 times longer. TokenValueBytes int // TokenExpiration tells how long a token remains valid. TokenExpiration time.Duration // EmailTemplate is the template text of the emails to be sent out // with entry codes. EmailTemplate string // SiteName is used in the entry code emails. // Has no default, should be provided if the default email template is used. SiteName string // SenderName is used in the entry code emails. // Has no default, should be provided if the default email template is used. SenderName string }
Config holds Authenticator configuration. A zero value is a valid configuration, see constants for default values.
type EmailParams ¶
type EmailParams struct { Email string SiteName string EntryCode string EntryCodeExpiration time.Duration SenderName string // Data may hold custom data. // Its value comes from Authenticator.SendEntryCode(). Data map[string]any }
EmailParams is passed as data when executing the email template.
type EmailSenderFunc ¶
EmailSenderFunc is the type of the function used to send out emails.
type Token ¶
type Token struct { // ID of the token. ID primitive.ObjectID `bson:"_id,omitempty"` // Case-sensitive email of the owner of the token. Email string `bson:"email"` // Lowercased email of the owner of the token. // Used for lookups. LoweredEmail string `bson:"lemail"` // Token creation timestamp. Created time.Time `bson:"c"` // One-time entry code for the token. EntryCode string `bson:"ecode"` // EntryClient is the client information of the entry code verification. EntryClient *Client `bson:"eclient,omitempty"` // Verified tells if the token's entry code has been verified. Verified bool `bson:"verified"` // UserID is the ID of the owner of the token. UserID primitive.ObjectID `bson:"userID"` // Client information of the last access. Client *Client `bson:"client,omitempty"` // Expires tells when this entry code or token expires. Expires time.Time `bson:"exp"` // Reusable token value for authentication. Value string `bson:"value"` // Used tells how many times this token was used. // A token is used when it is verified, and only // if Client information is provided. Used int `bson:"used,omitempty"` }
Token represents a token which authenticates users.
type User ¶
type User[UserData any] struct { // ID of the user. ID primitive.ObjectID `bson:"_id"` // Lowercased emails of the user. LoweredEmails []string `bson:"lemails"` // User creation timestamp. Created time.Time `bson:"c"` // Data may hold arbitrary data. Data UserData `bson:"data,omitempty"` }
User represents a user that owns tokens. A user may have multiple emails, and emails may be changed later, the User (identified by its ID) will remain the same.
type Validator ¶
Validator is a function which can check a token before it is accepted and updated in Authenticator.VerifyEntryCode() and Authenticator.VerifyToken(). The validator receives the persisted, un-updated token and the new client passed to the above functions.
Validators may be used to perform extensive checks on the client, e.g. check and restrict IP addresses or disallow changed user agents.