Documentation
¶
Overview ¶
Package session owns opaque, absolutely expiring browser sessions and their stream lifetimes.
Index ¶
- Constants
- func CheckOrigin(r *http.Request, origin string) error
- func NewHandler(backend Backend, cfg HandlerConfig) (http.Handler, error)
- func ParseCSRFToken(r *http.Request) (string, error)
- func ValidateReferences(refs []string) error
- func ValidateToken(token string) error
- type Backend
- type Config
- type ErrorWriter
- type Grant
- type HandlerConfig
- type Issued
- type LoginAttempt
- type LoginCredentials
- type LoginRequest
- type LoginVerifier
- type LoginVerifierFunc
- type LogoutRequest
- type Manager
- func (m *Manager) Acquire(ctx context.Context, ref string) (context.Context, func(), error)
- func (m *Manager) Authenticate(r *http.Request) (auth.Principal, error)
- func (m *Manager) BeginLogin(ctx context.Context, token string) (LoginAttempt, error)
- func (m *Manager) CSRFToken(ctx context.Context, ref string) (string, error)
- func (m *Manager) Cleanup(ctx context.Context) (int64, error)
- func (m *Manager) Close(ctx context.Context) error
- func (m *Manager) CompleteLogin(ctx context.Context, attempt LoginAttempt, p auth.Principal) (Issued, error)
- func (m *Manager) Create(ctx context.Context, p auth.Principal) (Issued, error)
- func (m *Manager) Logout(ctx context.Context, ref string) error
- func (m *Manager) Nudge()
- func (m *Manager) Reference(token string) (string, error)
- func (m *Manager) Resolve(ctx context.Context, ref string) (State, error)
- func (m *Manager) RevokeSubject(ctx context.Context, kind auth.Kind, subject string) (int64, error)
- func (m *Manager) Rotate(ctx context.Context, ref string, p auth.Principal) (Issued, error)
- type Record
- type Response
- type State
- type Store
Constants ¶
const ( Lifetime = time.Hour LookupBudget = 500 * time.Millisecond MutationBudget = 2 * time.Second // LoginBudget bounds a POST /auth/login, password verification included; a remote Backend's login call must // fit inside it. LoginBudget = 5 * time.Second Retention = 10 * time.Minute WatchLimit = 1024 )
const MaxLookupBatch = 256
MaxLookupBatch bounds the authority query and result payload.
Variables ¶
This section is empty.
Functions ¶
func CheckOrigin ¶
CheckOrigin requires an unsafe request to carry exactly one Origin header equal to origin, the rule the handler applies to login and logout, so a gateway can apply it to its other unsafe cookie routes. Safe methods pass.
func NewHandler ¶
func NewHandler(backend Backend, cfg HandlerConfig) (http.Handler, error)
NewHandler mounts POST /auth/login, GET /auth/session, and POST /auth/logout only. Login and logout require exactly one Origin header equal to Origin, and login requires JSON; status never emits Set-Cookie. The handler validates every browser-facing input, including the cookie shape and logout CSRF header, before it calls the backend, so a backend may be the local Manager or a remote session authority.
func ParseCSRFToken ¶
ParseCSRFToken returns the synchronizer token an unsafe request presents in exactly one X-CSRF-Token header. Safe methods return an empty token without error.
func ValidateReferences ¶
ValidateReferences validates a batch before persistence access.
func ValidateToken ¶
ValidateToken checks the exact canonical 32-byte base64url session grammar.
Types ¶
type Backend ¶
type Backend interface {
Login(context.Context, LoginRequest) (Grant, error)
Status(ctx context.Context, token string) (Grant, error)
Logout(context.Context, LogoutRequest) error
}
Backend owns session state behind the browser handler. Implementations return typed failures. The handler passes backend errors, causes included, to HandlerConfig.Errors, which must normalize them so causes never reach the browser.
func NewLocalBackend ¶
func NewLocalBackend(manager *Manager, verifier LoginVerifier) (Backend, error)
NewLocalBackend serves sessions from manager, verifying passwords with the application-owned verifier.
type Config ¶
type Config struct {
Store Store
Clock util.Clock
Random io.Reader
Pepper string
CSRF *security.SignedCSRF
// ReportError receives worker failures that have no caller: lease renewal and retained-generation cleanup.
ReportError func(context.Context, error)
}
Config injects persistence, time, cryptographic randomness and independent secrets.
type ErrorWriter ¶
type ErrorWriter func(http.ResponseWriter, *http.Request, error)
ErrorWriter is the outer transport's normalizer/problem response boundary.
type HandlerConfig ¶
type HandlerConfig struct {
// Origin is the exact HTTPS origin accepted for login and logout.
Origin string
// Errors writes every failure through the outer transport's problem boundary.
Errors ErrorWriter
// Clock derives cookie Max-Age from each grant's absolute expiry.
Clock util.Clock
}
HandlerConfig configures the browser session endpoints.
type Issued ¶
Issued contains the one-time plaintext credential, which callers must not log or persist.
type LoginAttempt ¶
type LoginAttempt struct {
// contains filtered or unexported fields
}
LoginAttempt is the presented cookie's state captured by BeginLogin before password verification. The zero value is invalid; an attempt is bound to the Manager that began it.
type LoginCredentials ¶
type LoginCredentials struct {
Username string `json:"username"`
Password string `json:"password"`
}
LoginCredentials is the bounded password login input. Credentials must never be logged.
type LoginRequest ¶
type LoginRequest struct {
Credentials LoginCredentials
Presented string
Source netip.Addr
}
LoginRequest is a validated login plus the session cookie the browser presented, which is empty for a fresh login. Source is the address of the connection that sent the login, for per-source admission; it is the zero Addr when the connection has no IP address, such as a Unix socket. Forwarding headers are never trusted for it.
type LoginVerifier ¶
type LoginVerifier interface {
VerifyLogin(context.Context, LoginCredentials) (auth.Principal, error)
}
LoginVerifier supplies application-owned verification without embedding membership in sessions.
type LoginVerifierFunc ¶
func (LoginVerifierFunc) VerifyLogin ¶
func (f LoginVerifierFunc) VerifyLogin(ctx context.Context, in LoginCredentials) (auth.Principal, error)
type LogoutRequest ¶
LogoutRequest is a validated session cookie plus the synchronizer CSRF token presented with it.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager owns its watch/cleanup workers. Composition must Close it before closing its store.
func NewManager ¶
NewManager starts explicitly owned stream lease and cleanup workers.
func (*Manager) Acquire ¶
Acquire performs a final authoritative lookup under the local revocation gate and leases the stream to it. The returned lifetime ends on expiry, logout, rotation, an unrenewed lease or manager shutdown; context.Cause reports a lease cause. Release is idempotent and must be called by the stream owner.
func (*Manager) Authenticate ¶
Authenticate resolves a cookie and enforces synchronizer CSRF on every unsafe cookie request.
func (*Manager) BeginLogin ¶
BeginLogin classifies the presented cookie before the application verifies the password, so that a logout committed during verification still wins. An empty token begins a fresh login. Malformed tokens, corrupt records and store failures are rejected before any password work.
func (*Manager) CSRFToken ¶
CSRFToken issues a token bound to a currently authoritative session generation.
func (*Manager) Cleanup ¶
Cleanup performs exactly one indexed batch of at most 256 retained generations.
func (*Manager) Close ¶
Close ends local stream lifetimes synchronously and waits for all owned workers.
func (*Manager) CompleteLogin ¶
func (m *Manager) CompleteLogin(ctx context.Context, attempt LoginAttempt, p auth.Principal) (Issued, error)
CompleteLogin issues a credential for a principal the application verified after BeginLogin. A fresh attempt creates a family. An active attempt atomically replaces its generation, preserving an unexpired family's absolute expiry, and fails if the family was revoked or superseded meanwhile. A terminal attempt revokes the stale cookie's whole family before issuing a fresh family, so a browser stuck with a stale HttpOnly cookie recovers through strong authentication while every generation of the old family stays fenced.
func (*Manager) Create ¶
Create establishes a fresh one-hour family, without binding it to an application resource.
func (*Manager) Logout ¶
Logout revokes the family even when ref belongs to a rotated-away generation.
func (*Manager) Nudge ¶
func (m *Manager) Nudge()
Nudge requests early revalidation through the owned renewal worker. Requests coalesce.
func (*Manager) Reference ¶
Reference checks the session token grammar and returns its protected reference. It performs no lookup.
func (*Manager) Resolve ¶
Resolve validates a protected reference against authoritative storage and returns its current generation, family, absolute expiry and authentication time. It applies the same rules as Authenticate without CSRF, so only trusted callers that already hold a protected reference, such as an identity service checking a delegation, may use it.
func (*Manager) RevokeSubject ¶
RevokeSubject revokes every session of one subject, for example after a password reset, and ends their streams. It returns how many session families it revoked; zero means the subject had none left to revoke.
type Record ¶
type Record struct {
Reference string
Family string
Generation uint64
Principal auth.Principal
ExpiresAt time.Time
RetainUntil time.Time
// AuthenticatedAt is when the family's user last proved a credential: set by create and relogin, kept by
// rotation. Zero means unknown, which callers must treat as not recent.
AuthenticatedAt time.Time
Active bool
Revoked bool
}
Record is protected persistence material. Never persist plaintext session credentials.
type Response ¶
type Response struct {
Status string `json:"status"`
Identity auth.Principal `json:"identity"`
ExpiresAt time.Time `json:"expiresAt"`
CSRFToken string `json:"csrfToken"`
}
Response is the browser session contract. Identity omits credential references and tokens.
type State ¶
type State struct {
Principal auth.Principal
Family string
Generation uint64
ExpiresAt time.Time
// AuthenticatedAt is when the user last proved a credential for this family; zero means unknown.
AuthenticatedAt time.Time
}
State is the authoritative record behind a protected session reference.
type Store ¶
type Store interface {
Create(context.Context, Record) error
Lookup(context.Context, string) (Record, error)
LookupBatch(context.Context, []string) (map[string]Record, error)
Rotate(context.Context, string, Record) error
Relogin(context.Context, string, Record) error
Revoke(context.Context, string) (string, error)
RevokeSubject(context.Context, auth.Kind, string) (int64, error)
Cleanup(context.Context, time.Time, int) (int64, error)
}
Store operations are transactional and cancellation-aware. Lookup must read authoritative writer state. Rotate atomically compares the active generation and inserts its successor without extending expiry or changing the authentication time. Relogin compares the active, non-revoked generation and replaces its identity after password verification. It preserves unexpired absolute expiry; an expired active generation may start a new one-hour lifetime. It records the new authentication time. Lookup reports a missing protected reference with NOT_FOUND, never an unavailable-store error. LookupBatch accepts at most MaxLookupBatch distinct references; omitted entries are authoritative absence, not a store outage. RevokeSubject atomically revokes matching families and returns the newly transitioned family count. Revoke resolves even an inactive generation and revokes its entire family.