ott

package
v0.27.2 Latest Latest
Warning

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

Go to latest
Published: Aug 31, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Index

Constants

View Source
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"
)
View Source
const (
	SessionContextKey contextKey = "ott_session"
	UserContextKey    contextKey = "ott_user"
)
View Source
const PluginID = "one-time-token"

PluginID is the unique string identifier for the One-Time Token plugin ("one-time-token").

Variables

View Source
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

func DefaultGenerateToken(length int) (string, error)

DefaultGenerateToken generates a cryptographically secure random token string of the requested byte length.

func DefaultTokenHasher

func DefaultTokenHasher(token string) (string, error)

DefaultTokenHasher computes a SHA-256 hash of the input token encoded in unpadded Base64Url string format.

func ToOTTIdentifier

func ToOTTIdentifier(storedToken string) string

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

type HasherFunc func(token string) (string, error)

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

func WithDisableClientRequest(disable bool) Option

WithDisableClientRequest configures whether to reject client-initiated token generation requests.

func WithDisableSetSessionCookie

func WithDisableSetSessionCookie(disable bool) Option

WithDisableSetSessionCookie configures whether to disable setting session cookies upon verification.

func WithExpiresIn

func WithExpiresIn(d time.Duration) Option

WithExpiresIn sets the expiration duration for issued one-time tokens.

func WithSetOttHeaderOnNewSession

func WithSetOttHeaderOnNewSession(enable bool) Option

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

func (p *Plugin) Authenticate() func(next http.Handler) http.Handler

Authenticate returns a net/http middleware handler to extract and verify One-Time Tokens from request headers or query parameters.

func (*Plugin) Config

func (p *Plugin) Config() Config

Config returns a copy of the active plugin configuration.

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) ID

func (p *Plugin) ID() string

ID returns the unique string identifier for the plugin ("one-time-token").

func (*Plugin) Init

func (p *Plugin) Init(ctx *plugin.Context) error

Init initializes the plugin with the shared execution context.

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

type TokenGeneratorFunc func(length int) (string, error)

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.

Jump to

Keyboard shortcuts

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