session

package
v0.0.11 Latest Latest
Warning

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

Go to latest
Published: Sep 18, 2026 License: Apache-2.0, MIT Imports: 21 Imported by: 0

Documentation

Overview

Package session manages sign-in sessions: issue (cookie-backed opaque tokens), validate (sliding expiry), list, and revoke. The sign-in endpoint lives here; credential checking is delegated to a Verifier (the password feature) via consumer-side wiring.

Index

Constants

View Source
const CookieName = "tango_session"

CookieName is the browser session cookie.

Variables

View Source
var ErrInvalidCredentials = errors.New("session: invalid credentials")

ErrInvalidCredentials mirrors the password feature error so the handler maps both to 401 without importing it.

View Source
var (
	// ErrNotFound is returned when no session matches, including
	// expired and revoked ones (they must be indistinguishable).
	ErrNotFound = errors.New("session: not found")
)

Errors surfaced by stores and the service.

Functions

func ClearCookie

func ClearCookie(w http.ResponseWriter, secure bool)

ClearCookie expires the session cookie.

func RequestIP

func RequestIP(r *http.Request) string

RequestIP extracts the client IP for session metadata; proxy headers are trusted only via the transport chain, so the socket peer is the source of truth here.

func WriteCookie

func WriteCookie(w http.ResponseWriter, token string, expires time.Time, secure bool)

WriteCookie stores the session token: HttpOnly (no script access), SameSite=Lax (CSRF-safe for top-level navigation), Secure except in plain development mode, path-scoped to the whole site.

func WritePendingCookie

func WritePendingCookie(w http.ResponseWriter, token string, secure bool)

WritePendingCookie sets the short-lived pending-auth cookie: the bridge token for MFA verification, never a session token.

Types

type AuthTokenID

type AuthTokenID = typeid.TypeID[authTokenPrefix]

Typed IDs for the session tables: UUIDv7 suffix, snake_case prefix matching the singular table name.

type Meta

type Meta struct {
	UserAgent  string
	DeviceName string
	IPAddress  string
}

Meta carries request context captured at sign-in.

type PostgresStore

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

PostgresStore persists sessions in public.sessions; the token hash is the lookup key, the TypeID string is the public identifier.

func NewPostgresStore

func NewPostgresStore(exec datastore.Executor) *PostgresStore

NewPostgresStore builds the production session store.

func (*PostgresStore) Create

func (s *PostgresStore) Create(ctx context.Context, se *Session) error

func (*PostgresStore) ListActiveForUser

func (s *PostgresStore) ListActiveForUser(ctx context.Context, userID user.UserID) ([]Session, error)

ListActiveForUser returns live sessions, newest first.

func (*PostgresStore) RevokeAllForUser

func (s *PostgresStore) RevokeAllForUser(ctx context.Context, userID user.UserID, exceptID string) error

RevokeAllForUser revokes every live session, optionally sparing one (the caller's current session).

func (*PostgresStore) RevokeByTokenHash

func (s *PostgresStore) RevokeByTokenHash(ctx context.Context, tokenHash string) error

RevokeByTokenHash revokes the session matching a token hash.

func (*PostgresStore) RevokeForUser

func (s *PostgresStore) RevokeForUser(ctx context.Context, userID user.UserID, sessionID string) error

RevokeForUser revokes one session owned by the user; unknown or foreign session IDs surface ErrNotFound.

func (*PostgresStore) Touch

func (s *PostgresStore) Touch(ctx context.Context, id string, expiresAt time.Time) error

Touch extends a live session's expiry (sliding window) and marks the refresh time.

func (*PostgresStore) ValidByTokenHash

func (s *PostgresStore) ValidByTokenHash(ctx context.Context, tokenHash string) (Session, user.User, error)

ValidByTokenHash resolves one live session with its user; expired and revoked rows are invisible (indistinguishable from missing).

type RefreshTokenID

type RefreshTokenID = typeid.TypeID[refreshTokenPrefix]

Typed IDs for the session tables: UUIDv7 suffix, snake_case prefix matching the singular table name.

type Service

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

Service owns the sign-in flow: verify credentials, issue opaque tokens, resolve them back to principals, and manage revocation.

func NewService

func NewService(store Store, verifier Verifier, users user.Store, recorder identity.Recorder, opts ...ServiceOption) *Service

NewService builds the session feature.

func (*Service) APIRoutes

func (s *Service) APIRoutes(r chi.Router, _ identity.RouteGroups)

APIRoutes mounts the auth endpoints inside the shared /api group: public sign-in, then cookie-guarded session management.

func (*Service) IssueForUser

func (s *Service) IssueForUser(ctx context.Context, userID user.UserID, provider string, meta Meta) (string, error)

IssueForUser mints a session for an already-authenticated identity — the alternative sign-in providers (passkeys, device login, one-time access) call this after verifying their own ceremony. A confirmed second factor fails these flows closed: the pending bridge belongs to the password sign-in path only, so no provider can bypass MFA.

func (*Service) ListForUser

func (s *Service) ListForUser(ctx context.Context, userID user.UserID) ([]Session, error)

ListForUser returns the user's live sessions, newest first.

func (*Service) Name

func (s *Service) Name() string

Name names the feature for logs.

func (*Service) Resolve

func (s *Service) Resolve(ctx context.Context, token string) (user.User, Session, error)

Resolve maps a cookie token back to its principal and live session, sliding the expiry forward when past half-life.

func (*Service) ResolveSession

func (s *Service) ResolveSession(ctx context.Context, token string) (kernel.Principal, error)

ResolveSession implements kernel.Authenticator: cookie token in, transport principal out.

func (*Service) RevokeAllForUser

func (s *Service) RevokeAllForUser(ctx context.Context, userID user.UserID, keepID string) error

RevokeAllForUser revokes every session, optionally sparing one (the current session during a password change).

func (*Service) RevokeCurrent

func (s *Service) RevokeCurrent(ctx context.Context, token string) error

RevokeCurrent signs out the caller's session by token.

func (*Service) RevokeForUser

func (s *Service) RevokeForUser(ctx context.Context, userID user.UserID, sessionID string) error

RevokeForUser revokes one session owned by the user; unknown or foreign session IDs surface ErrNotFound.

func (*Service) SignIn

func (s *Service) SignIn(ctx context.Context, identityText, secret string, meta Meta) (string, user.User, Session, error)

SignIn verifies the credentials and issues a full session; callers that compose with the second factor use SignInWithPending.

func (*Service) SignInWithPending

func (s *Service) SignInWithPending(ctx context.Context, identityText, secret string, meta Meta) (SignInWithPendingResult, error)

SignInWithPending verifies the credentials and lands in a full session — or in the pending-auth bridge when a confirmed second factor requires it. The pending token goes to the pending cookie, never to the session cookie.

type ServiceOption

type ServiceOption func(*Service)

ServiceOption configures the session service.

func WithClock

func WithClock(now func() time.Time) ServiceOption

WithClock overrides the service clock (tests).

func WithCookieSecure

func WithCookieSecure(secure bool) ServiceOption

WithCookieSecure toggles the Secure cookie flag.

func WithLifetime

func WithLifetime(d time.Duration) ServiceOption

WithLifetime sets the session lifetime and the sliding-refresh threshold (half the lifetime).

func WithMFAPort

func WithMFAPort(port identity.MFAPendingIssuer) ServiceOption

WithMFAPort wires the second-factor port; a confirmed enrollment makes password sign-in (and every other non-MFA provider) land in a pending authentication instead of a full session.

func WithRecorder

func WithRecorder(recorder identity.Recorder) ServiceOption

WithRecorder overrides the audit recorder (tests).

type Session

type Session struct {
	ID        string
	UserID    user.UserID
	Provider  string
	TokenHash string

	UserAgent   *string
	DeviceName  *string
	IPAddress   *string
	CreatedAt   time.Time
	ExpiresAt   time.Time
	RefreshedAt *time.Time
	RevokedAt   *time.Time
}

Session is one active sign-in. The ID is public (listed, revoked by users); the token never leaves the service — only its SHA-256 hash is stored, and only the cookie carries it.

type SessionID

type SessionID = typeid.TypeID[sessionPrefix]

Typed IDs for the session tables: UUIDv7 suffix, snake_case prefix matching the singular table name.

type SignInWithPendingResult

type SignInWithPendingResult struct {
	Pending bool
	Token   string // pending token when Pending, else the session token
	User    user.User
	Session Session
}

SignIn verifies the identity + secret pair and issues a session token. The token goes to the client once (cookie); only its hash is stored. SignInWithPendingResult carries either a full session or the pending-auth bridge that only MFA verification can upgrade.

type Store

type Store interface {
	Create(ctx context.Context, s *Session) error
	ValidByTokenHash(ctx context.Context, tokenHash string) (Session, user.User, error)
	Touch(ctx context.Context, id string, expiresAt time.Time) error
	RevokeByTokenHash(ctx context.Context, tokenHash string) error
	RevokeForUser(ctx context.Context, userID user.UserID, sessionID string) error
	RevokeAllForUser(ctx context.Context, userID user.UserID, exceptID string) error
	ListActiveForUser(ctx context.Context, userID user.UserID) ([]Session, error)
}

Store persists sessions. Validity (revoked/expired) is enforced by every read.

type Verifier

type Verifier interface {
	VerifyIdentity(ctx context.Context, identityText, secret string) (user.User, error)
}

Verifier checks an identity + secret pair. Implemented by the password feature; passkeys plug in here later.

Jump to

Keyboard shortcuts

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