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

auth

package
v0.3.2 Latest Latest
Warning

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

Go to latest
Published: Oct 25, 2026 License: AGPL-3.0 Imports: 23 Imported by: 0

Documentation

Overview

Package auth holds the security handlers of the API surfaces (session cookie, API token, collect key), the session issuer and the cookies the sign-in flows set.

Index

Constants

View Source
const (
	ClaimUserID = "uid"
	ClaimEpoch  = "epoch"
	// ClaimProviderLogin is the Unix time, in seconds, of the Sign-in provider login the session
	// began with (ADR 0048). It is the stateless proof that stands in for the password of a User
	// who has none; only a provider login sets it, and a reissue keeps the original time.
	ClaimProviderLogin = "provider_login"
	// ClaimPasskeyCheck is the Unix time, in seconds, of the last fresh Passkey check of the
	// session: a Passkey login or a step-up check (ADR 0052). Like ClaimProviderLogin it is a
	// stateless proof for a User with no password, and a reissue keeps the original time.
	ClaimPasskeyCheck = "passkey_check"
	// ClaimMethod is how the session was obtained (a Method). A token without a known method
	// is refused: the User signs in again (ADR 0052).
	ClaimMethod = "method"
)

The session token's claims beyond go-pkgz's own (ADR 0020). Both ride the token's user attributes as decimal strings: ClaimUserID is the User's id, the lookup key of every request; ClaimEpoch is the User's session_epoch at issuance.

View Source
const PasskeyCeremonyCookie = "PASSKEY"

PasskeyCeremonyCookie is the name of the cookie that carries a Passkey ceremony's challenge between `begin` and `finish` (ADR 0052).

View Source
const PasskeyCheckPath = "/site/me/passkeys/check"

PasskeyCheckPath is the prefix of the Passkey check operations that use the cookie.

View Source
const PasskeyLoginPath = "/site/auth/passkey"

PasskeyLoginPath is the prefix of the login operations that use the cookie.

View Source
const PasskeyRegistrationPath = "/site/me/passkeys/registration"

PasskeyRegistrationPath is the prefix of the registration operations that use the cookie; the browser sends it to nothing else.

View Source
const RegistrationCookie = "REG"

RegistrationCookie is the name of the cookie that makes a browser the Registration browser (ADR 0030): registration sets it, and the verification link trusts the password from the form only when it comes back with it.

View Source
const SessionCookie = "JWT"

SessionCookie is the name of the cookie that carries the session token; the /site contract's cookie security scheme reads it.

View Source
const SigninFlowCookie = "SIGNIN"

SigninFlowCookie is the name of the cookie that carries a provider sign-in's flow between `start` and `callback` (ADR 0048).

Variables

View Source
var ErrUnauthorized = errors.New("unauthorized")

ErrUnauthorized is returned by security handlers when a request is not authorized.

Functions

func CollectScoped

func CollectScoped(ctx context.Context) *ent.Scoped

CollectScoped returns the Workspace-scoped client of the resolved collect key. It is nil on an unauthenticated context; the security handler runs before every collect operation, so handlers can rely on it.

func HasScope

func HasScope(auth *TokenAuth, scope externalapi.ApiTokenScope) bool

func RegistrationHash

func RegistrationHash(value string) string

RegistrationHash is the hash of a registration cookie's value, empty for no cookie.

func SameRegistrationBrowser

func SameRegistrationBrowser(tokenHash, cookieValue string) bool

SameRegistrationBrowser reports whether a request's registration cookie value is the one whose hash a verification token carries. A token minted without a hash (a link asked for from a browser that holds no cookie) matches no browser.

func StampUser

func StampUser(claims *gptoken.Claims, u *ent.User)

StampUser writes the User's id and session epoch into the claims' user: the claims every session carries (Issue; the test harness mints its tokens with it).

func TokenScoped

func TokenScoped(ctx context.Context) *ent.Scoped

TokenScoped returns the Workspace-scoped client of the authenticated api token. It is nil on an unauthenticated context, so call it only after the handler's HasScope check (which fails for a nil TokenAuth). Handlers pass it to the domain modules; they never build a *ent.Scoped from an id themselves.

func VerifyClaims

func VerifyClaims(email, browserHash string) map[string]string

VerifyClaims builds the claims of a verification token: the address it is sent to and the hash of the registration cookie it trusts (empty for none).

func VerifyClaimsBrowser

func VerifyClaimsBrowser(claims map[string]string) string

VerifyClaimsBrowser is the hash of the Registration browser a verification token was minted for, empty when none.

func VerifyClaimsEmail

func VerifyClaimsEmail(claims map[string]string) string

VerifyClaimsEmail is the address a verification token was sent to.

func WithCollectAuth

func WithCollectAuth(ctx context.Context, auth *CollectAuth) context.Context

func WithHostWorkspace

func WithHostWorkspace(ctx context.Context, workspaceID int64) context.Context

WithHostWorkspace marks a request that arrived on a Workspace's own Tracking host (ADR 0059): the collect key must then be that Workspace's, so the host is no ingestion path for another key.

func WithSiteAuth

func WithSiteAuth(ctx context.Context, auth *SiteAuth) context.Context

func WithTokenAuth

func WithTokenAuth(ctx context.Context, auth *TokenAuth) context.Context

Types

type CollectAuth

type CollectAuth struct {
	WorkspaceID int64
	// Scoped is the client confined to WorkspaceID. This file is the collect
	// API's construction point of it (ADR 0017); read it with CollectScoped.
	Scoped *ent.Scoped
}

CollectAuth holds the workspace resolved from the per-workspace collect key.

func GetCollectAuth

func GetCollectAuth(ctx context.Context) *CollectAuth

type CollectSecurityHandler

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

CollectSecurityHandler implements collectapi.SecurityHandler: resolves the per-workspace collect write-key (x-collect-key) to its workspace.

func NewCollectSecurityHandler

func NewCollectSecurityHandler(client *ent.Client) *CollectSecurityHandler

func (*CollectSecurityHandler) HandleApiKeyAuth

HandleApiKeyAuth authenticates the collect key and applies the rate limits that need it (ADR 0025): a client address that spent its failed-authentication budget is refused before the key is looked at, a wrong key counts against it, and a success charges the Workspace's /collect budget. Rejections surface as *ratelimit.LimitedError.

type ExternalSecurityHandler

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

ExternalSecurityHandler implements externalapi.SecurityHandler (Bearer token auth).

func NewExternalSecurityHandler

func NewExternalSecurityHandler(client *ent.Client, bus *events.Bus) *ExternalSecurityHandler

NewExternalSecurityHandler builds the Bearer-token handler. The bus opens the transactions of audited writes made under the token (ADR 0022).

func (*ExternalSecurityHandler) HandleBearerAuth

HandleBearerAuth authenticates the Bearer token and applies the rate limits that need it (ADR 0025): a client address that spent its failed-authentication budget is refused before the token is looked at, a failure counts against it, and a success charges the Workspace's shared /api and /mcp budget. Rejections surface as *ratelimit.LimitedError.

type Method

type Method string

Method is how a session was obtained (ADR 0052). The Two-factor requirement reads it: a session is Strong when the person proved more than a password or a provider account.

const (
	// MethodPassword is a login by email and password with no Second factor step.
	MethodPassword Method = "password"
	// MethodProvider is a login with a Sign-in provider (ADR 0048) with no Second factor step.
	MethodProvider Method = "provider"
	// MethodEmail is the session a verification link starts: it proves the mailbox only.
	MethodEmail Method = "email"
	// MethodSecondFactor is a login that passed a Second factor or Recovery code step.
	MethodSecondFactor Method = "second_factor"
	// MethodPasskey is a login with a Passkey, which verifies the person and replaces both steps.
	MethodPasskey Method = "passkey"
)

func (Method) Strong

func (m Method) Strong() bool

Strong reports whether a session of this method meets a Workspace's Two-factor requirement without the User having a Second factor. It is the one reading of "is this session strong".

type Session

type Session struct {
	Method          Method
	ProviderLoginAt time.Time
	PasskeyCheckAt  time.Time
}

Session is what a session token records about how it began: its Method, the time of the Sign-in provider login it began with (zero otherwise) and the time of its last Passkey check (zero when it has none). The two times are the proofs that stand in for a password.

func (Session) WithoutPasskeys

func (s Session) WithoutPasskeys() Session

WithoutPasskeys is the session after the User's last Passkey is gone: a Passkey session falls back to the weakest method it began with, and its Passkey check no longer stands in for a password.

func (Session) WithoutSecondFactor

func (s Session) WithoutSecondFactor() Session

WithoutSecondFactor is the session after its Second factor is gone: a session that was strong only because it passed a Second factor step falls back to the weakest method it began with.

type Sessions

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

Sessions issues and clears the site session (ADR 0020): a JWT signed with the instance secret, carried in an HttpOnly, SameSite=Lax cookie that lives as long as the token (SPHERICON_SESSION_TTL, no refresh). It is the one place a session is minted; the login operations hand its cookie to the client.

func NewSessions

func NewSessions(jwtSecret string, ttl time.Duration, secure bool, now func() time.Time, verifyWith ...string) *Sessions

NewSessions builds the issuer. secure sets the cookie's Secure attribute (the instance is served over HTTPS); now is the clock of the token's expiry, nil meaning time.Now. A session is signed with jwtSecret and verified against it and then against each of verifyWith, the secondary key of a JWT secret rotation.

func (*Sessions) Cleared

func (s *Sessions) Cleared() *http.Cookie

Cleared is the cookie that ends the session on the client: empty and expired.

func (*Sessions) ClearedSigninFlow

func (s *Sessions) ClearedSigninFlow() *http.Cookie

ClearedSigninFlow is the cookie that removes the flow from the browser.

func (*Sessions) Holder

func (s *Sessions) Holder(raw string) (id, epoch int64, ok bool)

Holder reads a raw session token the request carries without requiring one (a public operation that keeps the acting session when there is one): it checks the signature and expiry and returns the User id and epoch the token was issued under. The caller compares the epoch with the User's; ok is false for a token that does not verify.

func (*Sessions) Issue

func (s *Sessions) Issue(u *ent.User, sess Session) (*http.Cookie, error)

Issue signs a session for u, stamped with its id, current session epoch and how it began, and returns the cookie that carries it. The provider login time rides the token, so a step that asks a passwordless User for proof can tell how recent the login was without a store.

func (*Sessions) NewRegistrationBrowser

func (s *Sessions) NewRegistrationBrowser(ttl time.Duration) (cookie *http.Cookie, hash string)

NewRegistrationBrowser draws the random value that marks one browser, returning the cookie that carries it (httpOnly, SameSite=Strict, scoped to the auth prefix, living for ttl) and the hash that goes into the verification token. The value itself never leaves the browser.

func (*Sessions) PasskeyCeremony

func (s *Sessions) PasskeyCeremony(path, value string, ttl time.Duration) *http.Cookie

PasskeyCeremony wraps the sealed ceremony in its cookie: httpOnly, SameSite=Strict (a ceremony begins and finishes in one page), living for ttl and sent only to path.

func (*Sessions) SessionOf

func (s *Sessions) SessionOf(raw string) (Session, bool)

SessionOf is how a raw session token began; ok is false for a token that does not verify, is expired or records no known method.

func (*Sessions) SigninFlow

func (s *Sessions) SigninFlow(value string, ttl time.Duration) *http.Cookie

SigninFlow wraps the sealed flow in its cookie: httpOnly, living for ttl, and SameSite=Lax (not Strict like the Registration browser's): the provider sends the browser back with a cross-site top-level redirect, which Strict would withhold the cookie from.

type SiteAuth

type SiteAuth struct {
	// Session is how the session began: its Method and, for a Sign-in provider login (ADR 0048),
	// the time of that login.
	Session

	UserID int64
	Email  string
}

SiteAuth holds the authenticated dashboard user resolved from the JWT cookie.

func GetSiteAuth

func GetSiteAuth(ctx context.Context) *SiteAuth

type SiteSecurityHandler

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

SiteSecurityHandler implements siteapi.SecurityHandler: validates the JWT cookie issued by go-pkgz/auth and resolves the dashboard user from it.

go-pkgz's Parse ignores an expired exp (its own middleware would refresh), so the expiry is enforced here against the injected clock; there is no refresh (ADR 0020). The User is looked up by the id the token carries, and the token's epoch must equal the User's session_epoch: a bump ends every earlier session.

func NewSiteSecurityHandler

func NewSiteSecurityHandler(jwtSecret string, client *ent.Client, now func() time.Time, verifyWith ...string) *SiteSecurityHandler

NewSiteSecurityHandler builds the cookie handler. now is the clock the token's expiry is checked against; nil means time.Now. A token verifies against jwtSecret and then against each of verifyWith, the secondary key of a JWT secret rotation.

func (*SiteSecurityHandler) HandleApiKeyAuth

func (*SiteSecurityHandler) Verify

func (h *SiteSecurityHandler) Verify(ctx context.Context, raw string) (*ent.User, error)

Verify checks a raw session token (signature, expiry, User id and epoch) and returns the User it belongs to, or ErrUnauthorized.

type TokenAuth

type TokenAuth struct {
	TokenID     int64
	WorkspaceID int64
	Name        string
	Scopes      []string
	// Scoped is the client confined to WorkspaceID. This file is the external
	// API's construction point of it (ADR 0017); read it with TokenScoped.
	Scoped *ent.Scoped
}

TokenAuth holds the authenticated api-token context for external API requests.

func GetTokenAuth

func GetTokenAuth(ctx context.Context) *TokenAuth

Jump to

Keyboard shortcuts

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