Documentation
¶
Overview ¶
Package tokens implements a random, secure and time-limited token generator that helps implement common features like session IDs (multi-use, long-lived), password resets and verify email (single-use, short-lived) scenarios.
Index ¶
- Constants
- func MustMatchType(t string) func(*Token) error
- func MustMatchTypeAndRefID(t string, refID uuid.UUID) func(*Token) error
- func RegisterMigrations(mig *migrate.Migrator) error
- type Token
- type TokenArgs
- type Tokens
- func (t *Tokens) Cleanup(ctx context.Context) error
- func (t *Tokens) Delete(ctx context.Context, token string) error
- func (t *Tokens) DeleteByTypeRef(ctx context.Context, tokenType string, tokenRefID uuid.UUID) error
- func (t *Tokens) New(ctx context.Context, args TokenArgs) (string, json.RawMessage, error)
- func (t *Tokens) ResetIdle(ctx context.Context, token string) error
- func (t *Tokens) Rotate(ctx context.Context, oldToken string, args TokenArgs) (string, error)
- func (t *Tokens) UpdateData(ctx context.Context, token string, data json.RawMessage) error
- func (t *Tokens) Verify(ctx context.Context, token string, vfn func(*Token) error) (*Token, error)
Constants ¶
const DefaultRawTokenSize = 32
DefaultRawTokenSize is the size used if not otherwise specified.
const ErrInvalid = errors.ConstError("invalid token")
ErrInvalid is the error returned if an invalid (expired or unknown) token is passed to Verify.
Variables ¶
This section is empty.
Functions ¶
func MustMatchType ¶
MustMatchType returns a token validation function that fails if the type of the loaded token is not t.
func MustMatchTypeAndRefID ¶
MustMatchTypeAndRefID returns a token validation function that fails if the type of the loaded token is not t or if its ref_id is not refID.
func RegisterMigrations ¶
RegisterMigrations registers the tokens package's migrations with the provided migrator. The migrations are registered under the group "karbur/tokens".
Types ¶
type Token ¶
type Token struct {
Token string `db:"token"`
Type string `db:"type"`
SingleUse bool `db:"single_use"`
RefID uuid.UUID `db:"ref_id"`
Expiry time.Time `db:"expiry"`
Idle sql.Null[time.Time] `db:"idle"`
Data json.RawMessage `db:"data"`
}
Token represents a token loaded via Verify.
type TokenArgs ¶
type TokenArgs struct {
// Type is an application-defined type of token.
Type string
// RefID is an application-defined identifier linked to this token.
RefID uuid.UUID
// SingleUse indicates if the token is unique and single-use (consumed and
// invalid after first use, a single valid one exists for the Type and RefID)
// or not.
SingleUse bool
// AbsoluteExpiry is the duration that the token is valid. It is precise to
// the second.
AbsoluteExpiry time.Duration
// IdleExpiry is the duration that the token is valid in between calls to
// Verify (or between New and Verify). The idle time is reset each time a
// token is successfully verified. It is ignored if SingleUse is true. It is
// precise to the second. A zero value means no idle expiry.
IdleExpiry time.Duration
// Data is arbitrary, application-specific data to store alongside the token.
// It gets marshaled and stored as JSON.
Data any
}
TokenArgs configures the token to create.
type Tokens ¶
type Tokens struct {
// Conn is a database connection (which is satisfied by pgdb.Pool, it doesn't
// need to be a dedicated connection, it just needs to satisfy the
// interface).
Conn pgdb.Connection
// RawTokenSize is the number of bytes of the randomly-generated token before
// base64-encoding. DefaultRawTokenSize is used if it is <= 0.
RawTokenSize int
}
Tokens manages creation, validation and cleanup of secure, random tokens. It is safe to use concurrently as long as the pgdb.Connection implementation also is. Fields must not be modified after initialization.
func (*Tokens) Cleanup ¶
Cleanup deletes all expired tokens from the table. It uses the existing DB transaction if there is one. For databases with the pg_cron extension, it is also possible to schedule a recurring database job that calls the "tokens_cleanup" procedure instead of doing the cleanup via this function.
func (*Tokens) Delete ¶
Delete deletes the specified token, regardless of its expiry. It uses the existing DB transaction if there is one.
func (*Tokens) DeleteByTypeRef ¶
DeleteByTypeRef deletes all tokens with the specified tokenType and tokenRefID. This is meant for scenarios like when all session IDs should be invalidated for a given user, or canceling a password reset operation. It uses the existing DB transaction if there is one.
func (*Tokens) New ¶
New generates a new random, secure token configured according to args. It uses the existing DB transaction if there is one. The token is base64-url-encoded so it is safe to use in URLs and cookies if needed. The JSON-marshaled token data is also returned.
For single-use tokens, if a token already exists for the same Type and RefID, it is replaced by the new token, invalidating the previous one.
func (*Tokens) ResetIdle ¶
ResetIdle resets the idle expiry of the specified token, regardless of its current validity. If no initial idle expiration was set or if the token is single-use, it is a no-op. It uses the existing DB transaction if there is one.
func (*Tokens) Rotate ¶
Rotate generates a new token and updates the token entry of oldToken with that new token. The new token inherits the type from the old token, only the RefID, absolute expiration and idle expiration are reset using args, and the data is reset only if the provided args.Data field is non-nil. Callers can use json.RawMessage("null") to force data to reset to JSON null. It is a no-op if oldToken is a single-use token. It uses the existing DB transaction if there is one.
func (*Tokens) UpdateData ¶
UpdateData updates the data associated with the token, regardless of its expiry. It uses the existing DB transaction if there is one.
func (*Tokens) Verify ¶
Verify loads and verifies if the provided token is valid. If the token is single-use, it is deleted after load as it is not valid anymore. It uses the existing DB transaction if there is one. If the token has an idle timeout and is not expired yet, it is reset.
The vfn argument is extra validation to apply on the token before considering it valid. The MustMatchType and MustMatchTypeAndRefID functions in this package generate common validation functions for use in those scenarios:
- If the token is multi-use, such as a session ID, MustMatchType should be used to verify it has the expected type (so that a token generated for a different scenario cannot be valid for a session check)
- If the token is single-use, such as a password reset, MustMatchTypeAndRefID should be used to verify it has the expected type and ref_id (which should be known, as the user typically enters the account's email address in addition to the reset token).