Documentation
¶
Index ¶
- Constants
- Variables
- func DefaultGenerateToken(length int) (string, error)
- func DefaultTokenHasher(token string) (string, error)
- func ToOTTIdentifier(storedToken string) string
- type Config
- type GenerateTokenParams
- type GenerateTokenResponse
- type HasherFunc
- type OTTGeneratedPayload
- type OTTVerifiedPayload
- type Option
- func WithCustomGenerator(fn TokenGeneratorFunc) Option
- func WithCustomHasher(fn HasherFunc) Option
- func WithDisableClientRequest(disable bool) Option
- func WithDisableSetSessionCookie(disable bool) Option
- func WithExpiresIn(d time.Duration) Option
- func WithSetOttHeaderOnNewSession(enable bool) Option
- func WithStoreTokenMode(mode StoreTokenMode) Option
- type Plugin
- func (p *Plugin) AttachHeader(w http.ResponseWriter, sessionToken string) error
- func (p *Plugin) Authenticate() func(next http.Handler) http.Handler
- func (p *Plugin) Config() Config
- func (p *Plugin) GenerateToken(ctx context.Context, params GenerateTokenParams) (*GenerateTokenResponse, error)
- func (p *Plugin) ID() string
- func (p *Plugin) Init(ctx *plugin.Context) error
- func (p *Plugin) VerifyToken(ctx context.Context, params VerifyTokenParams) (*VerifyTokenResponse, error)
- type Repository
- type StoreTokenMode
- type TokenGeneratorFunc
- type VerificationRecord
- type VerifyTokenParams
- type VerifyTokenResponse
Constants ¶
const ( // EventOTTGenerated is published when a new One-Time Token is successfully issued. EventOTTGenerated = "ott:generated" // EventOTTVerified is published when an OTT is successfully validated and consumed. EventOTTVerified = "ott:verified" )
const ( SessionContextKey contextKey = "ott_session" UserContextKey contextKey = "ott_user" )
const PluginID = "one-time-token"
PluginID is the unique string identifier for the One-Time Token plugin ("one-time-token").
Variables ¶
var ( // ErrInvalidToken is returned when the provided OTT verification token is incorrect or not found. ErrInvalidToken = errors.New("ott: invalid verification token") // ErrTokenExpired is returned when attempting to verify an OTT token that has passed its validity lifetime. ErrTokenExpired = errors.New("ott: verification token has expired") // ErrSessionNotFound is returned when the session token referenced by the OTT token does not exist. ErrSessionNotFound = errors.New("ott: session not found") // ErrSessionExpired is returned when the underlying session associated with the OTT token has expired. ErrSessionExpired = errors.New("ott: session has expired") // ErrUserNotFound is returned when the user associated with the session cannot be found. ErrUserNotFound = errors.New("ott: user not found") // ErrClientRequestDisabled is returned when a client attempts to generate an OTT token while DisableClientRequest is true. ErrClientRequestDisabled = errors.New("ott: client token generation request is disabled") // ErrInvalidParameter is returned when a required input parameter is missing or empty. ErrInvalidParameter = errors.New("ott: required parameter is missing or invalid") )
Sentinel errors for the One-Time Token (OTT) plugin.
Functions ¶
func DefaultGenerateToken ¶
DefaultGenerateToken generates a cryptographically secure random token string of the requested byte length.
func DefaultTokenHasher ¶
DefaultTokenHasher computes a SHA-256 hash of the input token encoded in unpadded Base64Url string format.
func ToOTTIdentifier ¶
ToOTTIdentifier formats a stored token value into a namespace lookup key ("one-time-token:<token>").
Types ¶
type Config ¶
type Config struct {
// ExpiresIn specifies the validity duration for generated OTT tokens (default: 3 minutes).
ExpiresIn time.Duration
// DisableClientRequest when true rejects token generation requests originating directly from client-side HTTP callers.
DisableClientRequest bool
// DisableSetSessionCookie when true prevents setting the session HTTP cookie upon token verification.
DisableSetSessionCookie bool
// SetOttHeaderOnNewSession when true enables automatically attaching the set-ott header on new session creation.
SetOttHeaderOnNewSession bool
// StoreTokenMode defines token persistence security ("plain" or "hashed", default: "plain").
StoreTokenMode StoreTokenMode
// CustomHasher overrides the default SHA-256 base64url token hasher.
CustomHasher HasherFunc
// CustomGenerator overrides the default crypto/rand random token generator.
CustomGenerator TokenGeneratorFunc
}
Config structures all operational settings for the One-Time Token (OTT) plugin.
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns recommended default settings for the OTT plugin.
type GenerateTokenParams ¶
type GenerateTokenParams struct {
// SessionToken is the raw token string of the active session to bind to the OTT.
SessionToken string `json:"session_token"`
// IsClientReq indicates whether the token generation request originated directly from a client HTTP request.
IsClientReq bool `json:"is_client_req"`
}
GenerateTokenParams defines input parameters when requesting the issuance of a new One-Time Token.
type GenerateTokenResponse ¶
type GenerateTokenResponse struct {
// Token is the issued single-use token.
Token string `json:"token"`
}
GenerateTokenResponse contains the generated raw One-Time Token string returned to the caller.
type HasherFunc ¶
HasherFunc defines a custom function signature for hashing OTT tokens.
type OTTGeneratedPayload ¶
type OTTGeneratedPayload struct {
// SessionToken is the target session token bound to the generated OTT.
SessionToken string `json:"session_token"`
// Token is the issued OTT token string (or hashed token representation).
Token string `json:"token"`
// ExpiresAt is the timestamp when the generated token expires.
ExpiresAt time.Time `json:"expires_at"`
}
OTTGeneratedPayload defines the EventBus event payload dispatched when an OTT is created.
type OTTVerifiedPayload ¶
type OTTVerifiedPayload struct {
// SessionID is the unique identifier of the active session retrieved upon verification.
SessionID string `json:"session_id"`
// UserID is the unique identifier of the user owning the session.
UserID string `json:"user_id"`
// Token is the raw OTT token string that was verified.
Token string `json:"token"`
}
OTTVerifiedPayload defines the EventBus event payload dispatched when an OTT is consumed.
type Option ¶
type Option func(*Config)
Option configures functional options for the OTT plugin.
func WithCustomGenerator ¶
func WithCustomGenerator(fn TokenGeneratorFunc) Option
WithCustomGenerator sets a custom token string generator function.
func WithCustomHasher ¶
func WithCustomHasher(fn HasherFunc) Option
WithCustomHasher sets a custom token hashing function.
func WithDisableClientRequest ¶
WithDisableClientRequest configures whether to reject client-initiated token generation requests.
func WithDisableSetSessionCookie ¶
WithDisableSetSessionCookie configures whether to disable setting session cookies upon verification.
func WithExpiresIn ¶
WithExpiresIn sets the expiration duration for issued one-time tokens.
func WithSetOttHeaderOnNewSession ¶
WithSetOttHeaderOnNewSession enables or disables automatic set-ott header emission on new session creation.
func WithStoreTokenMode ¶
func WithStoreTokenMode(mode StoreTokenMode) Option
WithStoreTokenMode configures token storage security mode ("plain" or "hashed").
type Plugin ¶
type Plugin struct {
// contains filtered or unexported fields
}
Plugin implements the One-Time Token authentication plugin for go-modular-auth.
func New ¶
func New(repo Repository, opts ...Option) *Plugin
New instantiates a new One-Time Token plugin configured with the given repository and options.
func (*Plugin) AttachHeader ¶ added in v0.20.0
func (p *Plugin) AttachHeader(w http.ResponseWriter, sessionToken string) error
AttachHeader generates an OTT for the given session and sets the set-ott HTTP header on the response writer.
func (*Plugin) Authenticate ¶ added in v0.20.0
Authenticate returns a net/http middleware handler to extract and verify One-Time Tokens from request headers or query parameters.
func (*Plugin) GenerateToken ¶
func (p *Plugin) GenerateToken(ctx context.Context, params GenerateTokenParams) (*GenerateTokenResponse, error)
GenerateToken generates and persists a new single-use token bound to an active user session token.
func (*Plugin) VerifyToken ¶
func (p *Plugin) VerifyToken(ctx context.Context, params VerifyTokenParams) (*VerifyTokenResponse, error)
VerifyToken validates and atomically consumes a One-Time Token, returning the active session and user entities.
type Repository ¶
type Repository interface {
// CreateVerificationValue stores a new OTT verification record in storage.
//
// Function:
// Called when generating a single-use One-Time Token (OTT).
//
// Storage:
// Cache (Redis / In-Memory TTL) - Short-lived single-use OTT token.
//
// Arguments:
// - ctx: Request cancellation context.
// - record: VerificationRecord entity containing token identifier, session token value payload, and expiration timestamp.
//
// Returns:
// - error: Nil on success, or database error.
//
// Example SQL:
// INSERT INTO verification_tokens (id, identifier, value, expires_at, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6);
//
// Example Cache (Redis):
// err := rdb.Set(ctx, "ott:" + record.Identifier, bytes, ttl).Err()
CreateVerificationValue(ctx context.Context, record *VerificationRecord) error
// ConsumeVerificationValue atomically retrieves and deletes an OTT verification record by identifier.
// This operation MUST be atomic to protect against replay attacks and race conditions.
//
// Function:
// Called during OTT exchange endpoint to authenticate and consume the one-time token.
//
// Storage:
// Cache (Redis GETDEL / Memory) - Atomic read-and-delete single-use token consumption.
//
// Arguments:
// - ctx: Request cancellation context.
// - identifier: Composite token identifier key.
//
// Returns:
// - *VerificationRecord: Consumed record if found and not expired.
// - error: ErrInvalidToken if missing, or ErrTokenExpired if passed validity duration.
//
// Example SQL:
// DELETE FROM verification_tokens WHERE identifier = $1 AND expires_at > $2 RETURNING id, identifier, value, expires_at, created_at, updated_at;
//
// Example Cache (Redis):
// val, err := rdb.GetDel(ctx, "ott:" + identifier).Bytes()
ConsumeVerificationValue(ctx context.Context, identifier string) (*VerificationRecord, error)
// GetSessionByToken retrieves an active session entity matching the specified session token string.
//
// Function:
// Used after consuming an OTT token to verify and load the target session entity.
//
// Storage:
// Both (Cache-Aside Strategy) - Fast session retrieval by raw token string.
//
// Arguments:
// - ctx: Request cancellation context.
// - token: Raw session token string stored in OTT value.
//
// Returns:
// - *entity.Session: Active session entity if found.
// - error: ErrSessionNotFound if missing, or ErrSessionExpired if expired.
//
// Example SQL:
// SELECT id, user_id, token, expires_at, created_at, updated_at FROM sessions WHERE token = $1 LIMIT 1;
//
// Example Cache (Redis):
// val, err := rdb.Get(ctx, "session:" + token).Bytes()
GetSessionByToken(ctx context.Context, token string) (*entity.Session, error)
// GetUserByID retrieves a user entity matching the specified user identifier.
//
// Function:
// Used to verify user existence and populate user context after consuming an OTT.
//
// Storage:
// Database (GORM / SQL) - User primary key lookup.
//
// Arguments:
// - ctx: Request cancellation context.
// - userID: Target user primary key ID.
//
// Returns:
// - *entity.User: Matching user entity if found.
// - error: ErrUserNotFound if missing.
//
// Example SQL:
// SELECT id, email, name, email_verified, created_at, updated_at FROM users WHERE id = $1 LIMIT 1;
GetUserByID(ctx context.Context, userID string) (*entity.User, error)
}
Repository defines the persistent storage interface contract required by the OTT plugin. Implement this interface on your custom database adapter (e.g. PostgreSQL, MySQL, SQLite, MongoDB, GORM).
Implementation Example (GORM / database/sql): ¶
type GormOttRepository struct {
db *gorm.DB
}
func (r *GormOttRepository) ConsumeVerificationValue(ctx context.Context, identifier string) (*ott.VerificationRecord, error) {
var rec ott.VerificationRecord
err := r.db.WithContext(ctx).Where("identifier = ? AND expires_at > ?", identifier, time.Now()).First(&rec).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ott.ErrInvalidToken
}
return nil, err
}
_ = r.db.WithContext(ctx).Delete(&rec).Error
return &rec, nil
}
Storage and Caching Recommendation (Atomic Redis Ephemeral Storage): ¶
One-Time Tokens (OTT) are high-concurrency, short-lived tokens. Utilizing Redis `GETDEL` provides single-use atomic consumption that inherently prevents race conditions and replay attacks:
type RedisOttRepository struct {
redis *redis.Client
}
func (r *RedisOttRepository) CreateVerificationValue(ctx context.Context, record *ott.VerificationRecord) error {
bytes, _ := json.Marshal(record)
ttl := time.Until(record.ExpiresAt)
return r.redis.Set(ctx, "ott:"+record.Identifier, bytes, ttl).Err()
}
func (r *RedisOttRepository) ConsumeVerificationValue(ctx context.Context, identifier string) (*ott.VerificationRecord, error) {
key := "ott:" + identifier
val, err := r.redis.GetDel(ctx, key).Bytes()
if err != nil {
return nil, ott.ErrInvalidToken
}
var rec ott.VerificationRecord
_ = json.Unmarshal(val, &rec)
return &rec, nil
}
type StoreTokenMode ¶
type StoreTokenMode string
StoreTokenMode defines how one-time tokens are persisted in storage ("plain" or "hashed").
const ( // StoreTokenPlain persists the token in raw plain text format. StoreTokenPlain StoreTokenMode = "plain" // StoreTokenHashed persists the token as a one-way SHA-256 base64url hash. StoreTokenHashed StoreTokenMode = "hashed" )
type TokenGeneratorFunc ¶
TokenGeneratorFunc defines a custom function signature for generating random OTT token strings.
type VerificationRecord ¶
type VerificationRecord struct {
// ID is the unique database record identifier.
ID string `json:"id"`
// Identifier is the lookup key (e.g. "one-time-token:<stored_token>").
Identifier string `json:"identifier"`
// Value stores the target session token string.
Value string `json:"value"`
// ExpiresAt specifies the exact timestamp after which this token record is invalid.
ExpiresAt time.Time `json:"expires_at"`
// CreatedAt records when the token record was created.
CreatedAt time.Time `json:"created_at"`
// UpdatedAt records when the token record was last updated.
UpdatedAt time.Time `json:"updated_at"`
}
VerificationRecord represents the persistent storage entity for an OTT verification token.
type VerifyTokenParams ¶
type VerifyTokenParams struct {
// Token is the raw One-Time Token string to verify and consume.
Token string `json:"token"`
}
VerifyTokenParams defines input parameters when verifying and consuming an OTT.
type VerifyTokenResponse ¶
type VerifyTokenResponse struct {
// Session is the retrieved active session associated with the consumed token.
Session *entity.Session `json:"session"`
// User is the account entity owning the active session.
User *entity.User `json:"user"`
}
VerifyTokenResponse contains the validated active Session and associated User entities.