Help us shape the future of Go! Take the 2026 Go Developer Survey

token

package
v0.5.3 Latest Latest
Warning

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

Go to latest
Published: Nov 3, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package token manages model-owned personal and renewable access credentials. Persistence adapters own atomic refresh/reuse behavior. Guards resolve current models and use auth.AccessScopes as a credential ceiling, never as permissions.

Index

Constants

View Source
const MaxFamilyTickets = 64
View Source
const MaxLifetime = 365 * 24 * time.Hour
View Source
const MaxPrefixBytes = 16

MaxPrefixBytes bounds Config.Prefix.

View Source
const MaxPruneFamilies = 1024
View Source
const MaxRotations = 4096
View Source
const MaxTicketLifetime = 5 * time.Minute

MaxTicketLifetime and MaxFamilyTickets bound single-use handshake tickets.

View Source
const MaxTokens = 1024

Variables

This section is empty.

Functions

This section is empty.

Types

type Address

type Address struct {
	Namespace keyspace.Namespace
	Guard     auth.GuardName
	Provider  auth.ProviderName
	Model     string
}

Address binds persisted tokens to an application, environment, model/provider and guard. It is an infrastructure boundary, not an application identity API.

func (Address) Key

func (a Address) Key() (string, error)

Key validates the address and returns its opaque persistence scope.

func (Address) SubjectKey

func (a Address) SubjectKey(identity model.Identity) (string, error)

SubjectKey returns the subject's opaque key within the validated address.

func (Address) Validate

func (a Address) Validate() error

Validate checks the namespace, guard, provider and model address components.

type Backend

type Backend interface {
	// Create atomically creates a token under the subject's capacity policy.
	Create(context.Context, Address, Creation) (Record, error)
	// Lookup resolves a live token by digest, optionally recording activity.
	Lookup(context.Context, Address, Digest, bool) (value.Optional[Record], error)
	// Refresh atomically consumes a refresh generation and creates its successor; reuse
	// revokes the family and returns omission.
	Refresh(context.Context, Address, Digest, Digest, Digest) (value.Optional[Record], error)
	// Revoke removes the token family identified by its credential digest and reports
	// whether it existed.
	Revoke(context.Context, Address, Digest) (bool, error)
	// RevokeID removes the subject's identified token family and reports whether it
	// existed.
	RevokeID(context.Context, Address, model.Identity, model.ID[Record]) (bool, error)
	// RevokeAll removes all of the subject's token families, serializing with creation.
	RevokeAll(context.Context, Address, model.Identity) (uint64, error)
	// List returns a bounded page of the subject's token records.
	List(context.Context, Address, model.Identity, int) ([]Record, error)
	// Prune removes a bounded batch of expired token families.
	Prune(context.Context, Address, int) (uint64, error)
}

Backend is an authoritative hash-only store. Every mutation owns one transaction and runs without automatic retries. Stable subject locks serialize creation, revocation and refresh. Sample server time after the relevant lock is acquired. Lookup only accepts the family's current access generation. Refresh atomically consumes the current generation and creates its successor, preserving the family lifetime and grant. A consumed refresh hash revokes the entire family, committing that revocation before returning an omitted result. Never roll it back merely because the public refresh operation is rejected. At the rotation limit, revoke rather than accumulate unbounded history. Retain consumed generations until the family expires or is revoked. Concurrent use may yield one success followed by family revocation; no replay grace period is implied. An unknown commit outcome returns an error, never a partial record or new secret.

type CheckedBackend

type CheckedBackend interface {
	// CreateChecked runs the proof check before credential locks and creates the token in
	// the same transaction.
	CreateChecked(context.Context, Address, Creation, func(context.Context, *database.Tx) error) (Record, error)
}

CheckedBackend verifies a proof's current model in the SAME transaction as creation, before acquiring credential subject locks. The check must lock the model and may run once only. A failed check creates nothing; never retry it.

type CompletionBackend

type CompletionBackend interface {
	CheckedBackend
	// ConsumePendingIn locks, revalidates and provisionally deletes the exact pending-MFA
	// credential in the caller's transaction.
	ConsumePendingIn(context.Context, *database.Tx, Address, Record) error
	// CreateBefore performs checked creation and rechecks the pending deadline before
	// commit, rolling back joined writes on expiry.
	CreateBefore(context.Context, Address, Creation, temporal.DateTime, func(context.Context, *database.Tx) error) (Record, error)
}

CompletionBackend can replace a pending-MFA credential in checked creation. ConsumePendingIn joins the supplied transaction, locks and revalidates the exact pending record/hash/identity/expiry, and deletes it provisionally. It rejects full credentials. CreateBefore runs the check and creates through the ordinary creation path, then rechecks the pending deadline before commit. Both methods preserve rollback, never retry, and return no secret on failure.

type Config

type Config struct {
	Namespace            keyspace.Namespace
	Personal             Lifetime
	Renewable            Lifetime
	Challenge            Lifetime
	MaxPerSubject        int
	MaxPendingPerSubject int
	Limit                auth.LimitPolicy
	MaxRotations         int
	AccessGrace          time.Duration
	Prefix               string
	MaxConcurrent        int
	Timeout              time.Duration
	TicketLifetime       time.Duration
	MaxTicketsPerFamily  int
}

Config bounds the store and fixes new tokens' lifetimes. MaxPerSubject caps a subject's live personal/renewable families per guard; Limit selects rejection with auth.CredentialLimit (default) or evicting the oldest. Pending-MFA challenge tokens only count toward MaxPendingPerSubject and always replace the oldest pending one. Expired families never count. AccessGrace keeps a refreshed family's previous access token valid for this long after the refresh (never beyond its own expiry), so requests already in flight with it do not fail; zero disables it. Prefix is prepended to newly issued access/refresh secrets for secret scanning (for example "myapp_"); secrets with or without a prefix are accepted, since only the random part is hashed. TicketLifetime bounds a single-use handshake ticket from IssueTicket (between one second and MaxTicketLifetime; zero disables tickets), and MaxTicketsPerFamily caps one family's unexpired tickets, dropping the oldest.

func DefaultConfig

func DefaultConfig(namespace keyspace.Namespace) Config

DefaultConfig returns default token lifetimes, capacity, rotation, access-grace, ticket and operation bounds.

func (Config) Validate

func (c Config) Validate() error

Validate checks namespace, mode lifetimes, prefix, capacity, rotations, grace, ticket and operation bounds.

type Creation

type Creation struct {
	ID             model.ID[Record]
	Subject        model.Identity
	Name           string
	Scopes         []auth.AccessScopeName
	Mode           Mode
	Assurance      auth.Assurance
	AccessHash     Digest
	RefreshHash    value.Optional[Digest]
	Lifetime       Lifetime
	RotationLimit  uint32
	Maximum        int
	PendingMaximum int
	Limit          auth.LimitPolicy
	Device         auth.Device
}

Creation is a trusted, bounded issuance request. Backend.Create enforces Maximum live personal/renewable families and PendingMaximum live challenge families, each counting only its own kind, while holding the stable subject lock. Limit selects rejection or eviction for full families; challenges always evict their oldest. Expired families never count.

func (Creation) At

func (c Creation) At(address Address, now time.Time) (Record, error)

At validates creation and builds a record with copied scopes and UTC microsecond timestamps.

func (Creation) Validate

func (c Creation) Validate(address Address) error

Validate checks subject capacity, limit policy, device and token metadata.

type Digest

type Digest struct {
	// contains filtered or unexported fields
}

Digest is a token-store hash. It is distinct from a session digest and cannot substitute for an opaque credential supplied at a transport boundary.

func HashSecret

func HashSecret(value secret.String) (Digest, error)

HashSecret accepts the canonical 256-bit random encoding, optionally preceded by a Config.Prefix-style prefix. Only the random part is hashed, so changing the configured prefix never invalidates existing tokens.

func ParseDigest

func ParseDigest(text string) (Digest, error)

ParseDigest restores a token digest from its hexadecimal representation.

func (Digest) Equal

func (d Digest) Equal(other Digest) bool

Equal compares token digests.

func (Digest) Format

func (Digest) Format(s fmt.State, _ rune)

Format writes the label "token digest" without exposing the value.

func (Digest) Hex

func (d Digest) Hex() string

Hex returns the digest's hexadecimal persistence representation.

func (Digest) IsZero

func (d Digest) IsZero() bool

IsZero reports whether the digest is uninitialized.

type GraceBackend

type GraceBackend interface {
	// LookupWithin accepts the current access generation or its immediate predecessor
	// within grace and its own access expiry.
	LookupWithin(context.Context, Address, Digest, time.Duration) (value.Optional[Record], error)
}

GraceBackend additionally accepts the generation immediately before the family's current one, with Record.SupersededAt set, while the backend's clock is within grace of the successor's issue time and before its own access expiry. It never accepts older generations, and refresh-token reuse still revokes the family.

type ID

type ID[M any] struct {
	// contains filtered or unexported fields
}

ID is a model-owned token family identifier, never an authentication secret. It remains stable across refresh. Explicit subject authorization is still needed.

func ParseID

func ParseID[M any](text string) (ID[M], error)

ParseID parses a token identifier while retaining model ownership.

func (ID[M]) IsZero

func (id ID[M]) IsZero() bool

IsZero reports whether the identifier is uninitialized.

func (ID[M]) MarshalText

func (id ID[M]) MarshalText() ([]byte, error)

MarshalText encodes the token identifier in text form.

func (ID[M]) String

func (id ID[M]) String() string

String returns the token identifier's text representation.

type Info

type Info[M, K any] struct {
	// contains filtered or unexported fields
}

Info exposes immutable metadata and a concrete stored model reference. It contains neither raw credentials nor hashes and does not authorize its caller.

func (Info[M, K]) AccessExpiresAt

func (i Info[M, K]) AccessExpiresAt() temporal.DateTime

AccessExpiresAt returns the access credential's expiry.

func (Info[M, K]) Assurance

func (i Info[M, K]) Assurance() auth.Assurance

Assurance returns the token's authentication assurance.

func (Info[M, K]) CreatedAt

func (i Info[M, K]) CreatedAt() temporal.DateTime

CreatedAt returns the token's creation time.

func (Info[M, K]) Device

func (i Info[M, K]) Device() auth.Device

Device is the client metadata captured when the token family was issued.

func (Info[M, K]) ExpiresAt

func (i Info[M, K]) ExpiresAt() temporal.DateTime

ExpiresAt returns the token's absolute expiry.

func (Info[M, K]) Format

func (Info[M, K]) Format(s fmt.State, _ rune)

Format writes the label "token metadata" without exposing the value.

func (Info[M, K]) Generation

func (i Info[M, K]) Generation() uint32

Generation returns the token family's rotation generation.

func (Info[M, K]) ID

func (i Info[M, K]) ID() ID[M]

ID returns the model-owned token identifier.

func (Info[M, K]) IssuedAt

func (i Info[M, K]) IssuedAt() temporal.DateTime

IssuedAt returns this generation's issue time.

func (Info[M, K]) LastSeenAt

func (i Info[M, K]) LastSeenAt() temporal.DateTime

LastSeenAt returns the token's recorded activity time.

func (Info[M, K]) Mode

func (i Info[M, K]) Mode() Mode

Mode returns the token's personal, renewable or challenge mode.

func (Info[M, K]) Name

func (i Info[M, K]) Name() string

Name returns the token's assigned display name.

func (Info[M, K]) RefreshExpiresAt

func (i Info[M, K]) RefreshExpiresAt() value.Optional[temporal.DateTime]

RefreshExpiresAt returns the optional refresh credential deadline.

func (Info[M, K]) Scopes

func (i Info[M, K]) Scopes() auth.AccessScopes[M]

Scopes returns the token's immutable model-owned capability grants.

func (Info[M, K]) Subject

func (i Info[M, K]) Subject() model.Reference[M, K]

Subject returns the token's typed model reference.

type IssueOptions

type IssueOptions[M any] struct {
	Name    string
	Scopes  auth.AccessScopes[M]
	Refresh bool
}

IssueOptions is chosen by trusted credential verification, not mass-assigned from a login DTO. Requested scopes must fit this binding's declared ceiling. A scoped proof can only issue a subset of its own grants.

type Issued

type Issued[M, K any] struct {
	// contains filtered or unexported fields
}

Issued retains raw credentials for explicit transport delivery only. Routine formatting and JSON do not disclose them. Nonrenewable tokens omit refresh.

func (Issued[M, K]) AccessSecret

func (i Issued[M, K]) AccessSecret() secret.String

AccessSecret explicitly exposes the issued access secret wrapper for delivery.

func (Issued[M, K]) Format

func (Issued[M, K]) Format(s fmt.State, _ rune)

Format writes the label "issued token" without exposing the value.

func (Issued[M, K]) Info

func (i Issued[M, K]) Info() Info[M, K]

Info returns the issued token's safe metadata.

func (Issued[M, K]) RefreshSecret

func (i Issued[M, K]) RefreshSecret() value.Optional[secret.String]

RefreshSecret returns the optional refresh secret wrapper for explicit delivery.

type Lifetime

type Lifetime struct{ Access, RefreshIdle, Absolute time.Duration }

Lifetime preserves a family's absolute expiry across every refresh. Access activity never extends refresh idle expiry. Only successful refresh does so.

func (Lifetime) Validate

func (l Lifetime) Validate(mode Mode) error

Validate checks access, refresh and absolute durations against the selected token mode.

type Lifetimes

type Lifetimes struct{ Personal, Renewable, Challenge Lifetime }

Lifetimes are one guard's issuance lifetimes by mode.

type Mode

type Mode uint8

Mode distinguishes nonrenewable personal credentials, renewable pairs and short-lived MFA challenges. A challenge never carries ordinary access scopes.

const (
	Personal Mode = iota + 1
	Renewable
	Challenge
)

type Option

type Option func(*options) error

Option configures one token binding at construction.

func WithLifetimes

func WithLifetimes(lifetimes Lifetimes) Option

WithLifetimes shortens this guard's issuance lifetimes, for example admin tokens that expire sooner than user tokens issued from the same store. A zero Lifetime keeps the store's for that mode. The store's lifetimes are the ceiling: no duration may exceed the store's for its mode, so the configured store bounds every guard. Each lifetime must be valid for its mode, and the renewable access lifetime must still cover Config.AccessGrace. A family keeps the lifetime it was issued with across refreshes.

type Record

type Record struct {
	ID               model.ID[Record]
	Address          Address
	Subject          model.Identity
	Name             string
	Scopes           []auth.AccessScopeName
	Mode             Mode
	Assurance        auth.Assurance
	AccessHash       Digest
	RefreshHash      value.Optional[Digest]
	Lifetime         Lifetime
	RotationLimit    uint32
	Generation       uint32
	CreatedAt        temporal.DateTime
	IssuedAt         temporal.DateTime
	LastSeenAt       temporal.DateTime
	AccessExpiresAt  temporal.DateTime
	RefreshExpiresAt value.Optional[temporal.DateTime]
	ExpiresAt        temporal.DateTime
	Device           auth.Device
	// SupersededAt is set only when Lookup returns the generation immediately
	// before the family's current one: the successor's issue time. Its access
	// secret authenticates only within Config.AccessGrace of that time.
	SupersededAt value.Optional[temporal.DateTime] `json:",omitzero"`
}

Record is the explicit adapter boundary for the current token generation. It contains no raw secret. Adapters transfer ownership of the bounded scope slice; typed application code receives immutable Info instead of this record.

func (Record) Format

func (Record) Format(s fmt.State, _ rune)

Format writes the label "token record" without exposing the value.

func (Record) Live

func (r Record) Live(now time.Time) bool

Live reports whether access or refresh remains live at now.

func (Record) LiveAccess

func (r Record) LiveAccess(now time.Time) bool

LiveAccess reports whether now is strictly before access and absolute expiry.

func (Record) LiveAccessWithin

func (r Record) LiveAccessWithin(now time.Time, grace time.Duration) bool

LiveAccessWithin reports access validity, allowing a superseded generation only until grace after its successor was issued (never past its own expiry).

func (Record) LiveRefresh

func (r Record) LiveRefresh(now time.Time) bool

LiveRefresh reports whether this renewable token is before both refresh and absolute expiry.

func (Record) Refreshed

func (r Record) Refreshed(now time.Time, access, refresh Digest) (Record, bool, error)

Refreshed computes the successor of a current live generation. The adapter must first lock and re-read the family, detect consumed-token reuse, and ensure this record is current. False means no successor; it must never revive a dead family.

func (Record) Validate

func (r Record) Validate(address Address) error

Validate checks address ownership, token metadata, device and generation lifetime consistency.

type RefreshRevocationBackend

type RefreshRevocationBackend interface {
	// RevokeRefresh removes the family matching a current or consumed refresh digest and
	// returns its subject, omitting unknown families.
	RevokeRefresh(context.Context, Address, Digest) (value.Optional[model.Identity], error)
}

RefreshRevocationBackend revokes the family that issued a refresh digest, whether it is the current or previous generation's or a consumed one, under the subject lock shared with issuance. It returns the removed family's subject, omitted when no family matches. Expired families are removed too.

type SelectiveBackend

type SelectiveBackend interface {
	// RevokeOthers removes every token family of the subject except keep, returning the
	// count.
	RevokeOthers(context.Context, Address, model.Identity, model.ID[Record]) (uint64, error)
}

SelectiveBackend revokes every family of a subject in this address except keep, under the subject lock shared with issuance, and returns the count.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store borrows a backend and shares bounded operation capacity across bindings. The application owns the backend pool's lifecycle.

func NewStore

func NewStore(backend Backend, config Config) (*Store, error)

NewStore validates and borrows backend and creates bounded token-operation admission.

type Ticket

type Ticket[M, K any] struct {
	// contains filtered or unexported fields
}

Ticket is a single-use handshake credential for the token family that authenticated its issuing request, for transports that cannot send an Authorization header, such as a browser WebSocket. Secret() is an explicit transport boundary; routine formatting and JSON never disclose it.

func (Ticket[M, K]) ExpiresAt

func (t Ticket[M, K]) ExpiresAt() temporal.DateTime

ExpiresAt returns the handshake ticket's deadline.

func (Ticket[M, K]) Family

func (t Ticket[M, K]) Family() ID[M]

Family returns the model-owned token family identifier.

func (Ticket[M, K]) Format

func (Ticket[M, K]) Format(s fmt.State, _ rune)

Format writes the label "token ticket" without exposing the value.

func (Ticket[M, K]) Secret

func (t Ticket[M, K]) Secret() secret.String

Secret explicitly exposes the single-use handshake ticket wrapper for delivery.

type TicketBackend

type TicketBackend interface {
	// IssueTicket stores a bounded single-use ticket for a live family and returns its
	// expiry, omitting families that are no longer live.
	IssueTicket(context.Context, Address, model.Identity, model.ID[Record], Digest, time.Duration, int) (value.Optional[temporal.DateTime], error)
	// RedeemTicket atomically deletes and reads a ticket, omitting unknown or expired
	// tickets.
	RedeemTicket(context.Context, Address, Digest) (value.Optional[TicketRecord], error)
	// LookupFamily returns a live family's current generation for revalidation.
	LookupFamily(context.Context, Address, model.Identity, model.ID[Record]) (value.Optional[Record], error)
	// PruneTickets removes at most limit expired tickets in the address.
	PruneTickets(context.Context, Address, int) (uint64, error)
}

TicketBackend stores single-use handshake tickets as hashes. IssueTicket stores one for the subject's live family, first dropping that family's expired tickets and the oldest beyond max-1, and reports omitted expiry when the family is no longer live. RedeemTicket deletes the ticket in the statement that reads it, so a ticket redeems at most once; an unknown or expired ticket is omitted. Revoking, refreshing past or expiring a family never revives a ticket, and deleting a family deletes its tickets. LookupFamily reads a family's current generation for a re-check and is omitted unless the family is live. PruneTickets deletes at most limit expired tickets of the address.

type TicketRecord

type TicketRecord struct {
	Family    model.ID[Record]
	Subject   model.Identity
	ExpiresAt temporal.DateTime
}

TicketRecord is a redeemed ticket: the family and subject it was issued for.

type Tokens

type Tokens[M model.Identifiable, K any] struct {
	// contains filtered or unexported fields
}

Tokens binds one stored model/provider/guard/source and declared scope ceiling. Its guard is constructed once; persisted tokens cannot be rebound by extracting an unscoped strategy. Construction performs no I/O. It issues with the store's lifetimes unless WithLifetimes shortens them for this guard.

func New

func New[M model.Identifiable, K any](store *Store, name auth.GuardName, provider auth.Provider[M, K], source auth.CredentialName, allowed auth.AccessScopes[M], opts ...Option) (*Tokens[M, K], error)

New binds a token store, provider, guard and credential source with an immutable allowed-scope ceiling and validated options.

func (*Tokens[M, K]) CompleteMFA

func (t *Tokens[M, K]) CompleteMFA(ctx context.Context, raw secret.String, factor auth.SecondFactor[M, K], options IssueOptions[M]) (Issued[M, K], error)

CompleteMFA replaces a live pending credential from THIS guard with one fresh authenticated credential. The lookup is only a locator; checked creation revalidates and consumes it with the current model and factor in one transaction. Full, expired, revoked, wrong-guard or already-consumed credentials are rejected. No reusable authenticated proof escapes, and failures return no new secrets. Options belong to the server's login policy, not unvalidated client claims.

func (*Tokens[M, K]) Current

func (t *Tokens[M, K]) Current(ctx context.Context) (Info[M, K], error)

Current returns the metadata of the token that authenticated this guard in the current auth scope, including its effective scopes (for example to mark "this device" in List results by comparing IDs). It reuses the scope's verification; no second lookup runs.

func (*Tokens[M, K]) CurrentProof

func (t *Tokens[M, K]) CurrentProof(ctx context.Context) (auth.Proof[M, K], error)

CurrentProof returns a proof for the current request's model that retains the presented token's scope grants. Issuing a token from it can only narrow those grants, unlike auth.NewProof, which is unrestricted.

func (*Tokens[M, K]) Guard

func (t *Tokens[M, K]) Guard() auth.Guard[M]

Guard returns the token authentication guard, or an undefined guard for a nil binding.

func (*Tokens[M, K]) Issue

func (t *Tokens[M, K]) Issue(ctx context.Context, proof auth.Proof[M, K], options IssueOptions[M]) (Issued[M, K], error)

Issue persists a trusted verified proof. It does not verify a password or a submitted identity. Requested scopes cannot exceed this binding's declaration or the input proof's grant. Pending MFA has fixed expiry and cannot refresh.

func (*Tokens[M, K]) IssueTicket

func (t *Tokens[M, K]) IssueTicket(ctx context.Context) (Ticket[M, K], error)

IssueTicket mints a single-use ticket for the token family that authenticated this guard in the current request. Only its hash is stored. It expires after Config.TicketLifetime, the first redemption consumes it, and revoking the family deletes it. Mount it behind this binding's guard and deliver it through a no-store credential response. Only a TicketEndpoint.URL delivery boundary may put the single-use ticket in a URL; never put access or refresh tokens there. Redact ticket query parameters from ingress logs and use HTTPS.

func (*Tokens[M, K]) List

func (t *Tokens[M, K]) List(ctx context.Context, reference model.Reference[M, K]) ([]Info[M, K], error)

List returns bounded current-family metadata. An expired access credential can still have a live refresh grant; callers can inspect both expiry values.

func (*Tokens[M, K]) Logout

func (t *Tokens[M, K]) Logout(ctx context.Context, access secret.String) (bool, error)

Logout revokes the family of a presented access token and reports EventLogout when it removed one.

func (*Tokens[M, K]) LogoutRefresh

func (t *Tokens[M, K]) LogoutRefresh(ctx context.Context, refresh secret.String) (bool, error)

LogoutRefresh revokes the family of a presented refresh secret, for a logout authenticated by the refresh credential itself, such as a browser's HttpOnly refresh cookie: an expired access token does not prevent it. A current, previous or consumed refresh secret of the family revokes it, as replaying one would; an unknown one revokes nothing and is not an error, so logout stays idempotent. A malformed secret is Unauthenticated, as with Revoke. It reports EventLogout when it removed a family.

func (*Tokens[M, K]) Prune

func (t *Tokens[M, K]) Prune(ctx context.Context, limit int) (uint64, error)

Prune removes at most limit expired families with their bounded generation history. Stable subject rows remain for issuance/revocation serialization.

func (*Tokens[M, K]) PruneExpired

func (t *Tokens[M, K]) PruneExpired(ctx context.Context, batch, maxBatches int) (uint64, error)

PruneExpired prunes expired token families of this guard in batches of batch families, stopping after a short batch or maxBatches, and reports the families removed. Schedule it from the application calendar or the configured housekeeping schedule; the auth package does not depend on the scheduler.

func (*Tokens[M, K]) RedeemTicket

func (t *Tokens[M, K]) RedeemTicket(ctx context.Context, raw secret.String) (value.Optional[auth.BoundCredential], error)

RedeemTicket consumes a ticket issued by this binding. A redeemed ticket becomes a bound credential for TicketSource, which each later auth scope re-verifies against the live token family, its subject's current eligibility and the binding's scope ceiling. An unknown, expired or already redeemed ticket is omitted; the caller treats that as unauthenticated.

func (*Tokens[M, K]) Refresh

func (t *Tokens[M, K]) Refresh(ctx context.Context, raw secret.String) (Issued[M, K], error)

Refresh consumes one renewable secret atomically. Reuse of a consumed secret revokes the entire family, including a concurrent successful successor. Clients must serialize refresh and reauthenticate after an uncertain/failed outcome; the framework never retries or returns partial credentials.

func (*Tokens[M, K]) Revocation

func (t *Tokens[M, K]) Revocation() (auth.Revocation[M, K], error)

Revocation contributes this bound guard to auth.NewRevocations. Construct the group once and pass its Invalidate method to password-reset/domain callbacks.

func (*Tokens[M, K]) Revoke

func (t *Tokens[M, K]) Revoke(ctx context.Context, access secret.String) (bool, error)

Revoke hashes the access credential and removes its family, reporting whether it existed.

func (*Tokens[M, K]) RevokeAll

func (t *Tokens[M, K]) RevokeAll(ctx context.Context, reference model.Reference[M, K]) (uint64, error)

RevokeAll removes the reference's token families after checking provider ownership.

func (*Tokens[M, K]) RevokeAllIn

func (t *Tokens[M, K]) RevokeAllIn(ctx context.Context, tx *database.Tx, reference model.Reference[M, K]) (uint64, error)

RevokeAllIn joins a caller's transaction. Its count is provisional and the caller must propagate errors. It never acquires a model lock; credential-change orchestration must lock that model first. Unsupported adapters fail closed.

func (*Tokens[M, K]) RevokeCurrent

func (t *Tokens[M, K]) RevokeCurrent(ctx context.Context) (bool, error)

RevokeCurrent revokes the family of the token that authenticated this request (API logout on an authenticated route). It reports EventLogout when it removed the family.

func (*Tokens[M, K]) RevokeID

func (t *Tokens[M, K]) RevokeID(ctx context.Context, reference model.Reference[M, K], id ID[M]) (bool, error)

RevokeID removes one family belonging to this exact model subject and guard. Authorize the caller before supplying a subject; the ID is not authority.

func (*Tokens[M, K]) RevokeOthers

func (t *Tokens[M, K]) RevokeOthers(ctx context.Context) (uint64, error)

RevokeOthers revokes every other token family of the current subject in this guard while keeping the current one ("log out other devices"). It runs under the subject lock shared with issuance and reports EventOtherDevicesLoggedOut.

func (*Tokens[M, K]) TicketSource

func (t *Tokens[M, K]) TicketSource() auth.CredentialName

TicketSource is the credential source that redeemed tickets authenticate: this binding's guard source.

func (*Tokens[M, K]) Touch

func (t *Tokens[M, K]) Touch(ctx context.Context, access secret.String) (bool, error)

Touch records explicit access activity without extending access, refresh or absolute expiry. Ordinary guard verification remains read-only.

func (*Tokens[M, K]) Validate

func (t *Tokens[M, K]) Validate() error

Validate checks the token store and guard binding.

func (*Tokens[M, K]) ValidateTickets added in v0.4.0

func (t *Tokens[M, K]) ValidateTickets() error

ValidateTickets checks the binding and whether its configured store supports enabled tickets, without acquiring resources or issuing a credential.

func (*Tokens[M, K]) WithObserver

func (t *Tokens[M, K]) WithObserver(observer auth.Observer) (*Tokens[M, K], error)

WithObserver returns a binding sharing this guard that reports EventLogin for full issuance (including MFA completion), EventLogout for Logout, LogoutRefresh and RevokeCurrent, and EventOtherDevicesLoggedOut for RevokeOthers.

type TransactionalBackend

type TransactionalBackend interface {
	// RevokeAllIn removes the subject's token families within the caller-owned
	// transaction, without committing or retrying.
	RevokeAllIn(context.Context, *database.Tx, Address, model.Identity) (uint64, error)
}

TransactionalBackend joins a caller-owned transaction. It must verify pool ownership, restore schema state, preserve rollback, and never commit or retry.

Directories

Path Synopsis
Package postgres implements authoritative token persistence through explicit Foundry migrations and generated model queries.
Package postgres implements authoritative token persistence through explicit Foundry migrations and generated model queries.

Jump to

Keyboard shortcuts

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