sessionhttp

package module
v0.0.0-...-789096f Latest Latest
Warning

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

Go to latest
Published: Nov 1, 2026 License: MIT Imports: 15 Imported by: 2

README

Session HTTP integration

An optional Go 1.27 module. Importing identity/http does not import this module or the session implementation. This checkpoint owns session-read and self-signout handlers, not the full session transport, feature/router/OpenAPI composition or release-qualified backend platform.

Quick start and ownership

Select NewPreAuthHandlerWithBrowserDevice(config, deviceConfig) or NewPreAuthFeatureWithBrowserDevice(config, deviceConfig) to include the session-owned browser correlation cookie. Dedicated versioned keys, namespace, bounded lifetime and __Host-identity_device policy are mandatory. Share the same device configuration with the selected OTP Send transport. Existing flow-only constructors do not select or inspect this cookie. A valid device is reused across flows without renewal; invalid/duplicate/expired custody denies before preparation. Absent custody is generated only after IP admission and delivered alongside the flow cookie only on fresh successful commitment. This signal is not authentication, hardware identity or MFA trust. Clearing or copying it can evade/share correlation; IP and identifier controls remain independent requirements. Middleware variants retain protected dual-cookie ownership. Neither constructor alone proves a complete browser login journey.

For an original browser login flow, construct NewPreAuthHandler(PreAuthConfig{ Network: network, Service: preAuthService, Rate: authRate, Cookie: flowCookie, Lifetime: sessionLifetime, Origin: trustedOrigin, Action: trustedAction, CallbackURL: trustedCallback, PolicyID: policyID, PolicyVersion: policyVersion, Now: trustedClock, MaximumDuration: handlerBudget}). The selected reference FeatureSet.PreAuth() handle satisfies the borrowed service interface; do not write another flow store or issuance workflow. Use the secure __Host-identity_flow Lax/Strict cookie policy and the same configured issuer action/origin/callback/policy. Empty callback explicitly disables redirects.

By default, POST /v1/session/preauth-issue accepts bounded JSON {} or an optional boolean remember. Null, extra/duplicate fields, query, trailers, Authorization and Idempotency-Key deny. The request needs the exact configured HTTPS Origin and host-mapped tenant. Ambient cookies are not authentication for initiation. IP-only shared rate.auth admission precedes command/flow entropy and persistence. Each deliberate fresh start is independent; the handler never retries an unknown outcome or prepares another command as recovery. Subsequent OTP Send must use the returned original flow ID/cookie and its own keyed contract.

For separately selected phone-password recovery, set the base or named method's Purpose: session.PreAuthPhonePasswordRecovery, exact action identity.phone.password-reset-request and the genuine recovery issuer. A target IdentifierBinder is mandatory and must use the phone reset owner's binding domain, not signin. The request requires its bounded identifier; every remember member is forbidden, including false and null. Recovery carries zero RememberPolicy and never issues a session. A recovery-only handler needs no Lifetime; coexisting login methods retain their existing session policy. The client cannot select purpose/action/policy through JSON. This initiation alone sends no SMS and grants no account or recovery authority.

To support multiple login methods on this one route, explicitly populate PreAuthConfig.Methods with named PreAuthMethod profiles. For example, a password profile supplies a password-bound original issuer and its exact action, callback and policy. {"method":"password","remember":true} selects that profile; {} still selects the original base profile. All profiles share the configured origin, network, cookie, session lifetime, device protection and IP budget. The handler checks the chosen owner's prepared intent before issuing; it never relabels an OTP flow as password custody. Names are unique and bounded to sixteen profiles. Unknown, empty, null or duplicate method fields deny, and selected OpenAPI lists only configured names. With no Methods, the additional field remains forbidden. Callers own each borrowed issuer's lifetime and must not mutate configuration or service state concurrently with construction.

Target-bound methods additionally select PreAuthMethod.IdentifierBinder. The phone/password profile requires the reference's PhoneSigninPreAuth() handle as both issuer and binder. Its request is {"method":"phone-password","identifier":"+12025550123","remember":false}. The trusted owner canonicalizes and derives the original tenant/action-bound HMAC after IP admission and before preparation, without account lookup. The HTTP adapter verifies the prepared intent preserves that exact binding. Never accept a client-supplied digest. Unbound methods reject identifier; selecting PreAuthConfig.IdentifierBinder applies the same contract to the base profile.

The native issuer resolves the original five-minute flow independently of final session persistence. Remember is resolved once through Lifetime before Prepare and checked unchanged in the original command. Only an exact fresh committed, error-free result delivers the cookie; replay, expiry, failure and cancellation emit none while preserving known native outcome and safe original locators. The response contains no nonce, digest or bearer. Native recovery requires its current policy and never delivers or renews a cookie. For selected routing and OpenAPI, pass NewPreAuthFeature(config) to FeatureServices alongside any other explicitly selected features. This separate contribution does not select session reads/logout or OTP; NewFeature remains unchanged. Omission exposes no initiation route or metadata. NewPreAuthFeatureWithMiddleware retains the same owned typed phases. Construction performs no issuance or network IO. The real PostgreSQL/browser journey and complete release qualification remain pending.

To select reads/logout and initiation together, use NewFeatureWithPreAuth(signOutConfig, preAuthConfig) (or its NewFeatureWithPreAuthAndMiddleware variant). These operations retain one canonical session feature owner; do not add two identity/session contributions to the same selection. Duplicate owners reject atomically.

Cookie acquisition uses the base transport's shared strict decoder. Trailing SP/HTAB in either the selected cookie value or an unrelated cookie value denies before session lookup; values are not repaired into valid credentials. Token format validation remains session-owned, and bearer mode still ignores cookies.

For protected opt-in GET /openapi.json, call NewMetadataRouter(ctx, identityhttp.DocumentConfig{Title: title, Version: version}, MetadataConfig{ Read: getConfig, Authorization: authorizer, PolicyRevision: revision, Action: action, ResourceType: resourceType}, selectedServices, middleware). authorizer is the existing authorization engine, not a new role callback. It must explicitly allow the owner-established user and host-mapped tenant for the configured action/resource type and fixed identity.openapi.document resource ID at the configured revision. No permission is inferred from scopes, headers, a user ID supplied by the client, or mere authentication.

This selects only protected metadata plus the supplied services, not session routes or a public alternative. It uses getConfig's bounded session cookie or bearer profile, network, clock, whole-handler duration and shared safe-rate owner. Missing/inactive sessions return 401, explicit authorization denial 403, rate denial 429, and stale/expired/failed authority 503. Both authenticated and unauthenticated well-formed attempts share the trusted-IP budget; authenticated attempts additionally share the tenant/user budget with other safe operations. The exact immutable OpenAPI snapshot includes protected metadata once and no unselected routes; no credentials, cookies or runtime host details are exported. Global middleware sees real GetState and cannot replace original custody.

Select identityhttp.NewProbeFeature in the same FeatureServices to add public /healthz and /readyz alongside protected metadata, including with nonempty shared middleware. Those hooks receive no session principal or ambient credentials on probes; probe output remains fixed and readiness remains owned by the supplied lifecycle/dependency checks. Probe selection never grants metadata access or implicitly enables session routes. Omitting the probe feature omits both probe routes and their document entries.

This profile supports tenant-host administration and disables CORS grants. The shared transport rejects plaintext with JSON https_required (403). Cross-origin rejection is fixed text/plain identity.http.origin_denied (403, or 408 if cancellation is observed during origin admission); neither rejection enters session or policy work. OpenAPI describes both media profiles. Public-realm mapping and JWT/API-key administration are not inferred. Base HTTP's existing explicit public metadata profile remains distinct and metadata stays off in ordinary constructors. Use NewHTTPServer for raw IO/streaming bounds. Reference browser, provider, full feature and release qualification remain pending.

For shared secure transport, obtain descriptors with identityhttp.NewOperationDescriptors and pass them to identityhttp.NewRouter with TransportPolicy.Network equal to the feature network configuration, explicit handler timeout/document identity and selected route CORS policies. Router middleware binds into the actual GET/self-signout owner phases, retaining one credential acquisition and one rate admission. Cookie serialization, CSRF, primary revalidation and unknown-outcome recovery remain feature-owned. This is selected composition, not all-session/browser/release qualification.

Pass the same feature selection to identityhttp.NewDocumentedHandler for concrete OpenAPI 3.1.1 and its matching handler snapshot. The feature owns the real HTTP schemas separately from opaque direct contracts: optional cookie or bearer credentials, no GET body/query, strict JSON/form logout, conditional cookie CSRF, credential-free session metadata, exact clearing instructions and original recovery outcomes. Structured logout failures and generic dispatcher errors are both represented. actorKind serializes as an upstream string; optional linkMode remains a numeric identity enum. No trusted origin, tenant, host mapping, key or provider configuration enters the document. The full security router, metadata endpoint and generated-client/browser qualification remain required, not inferred from document export.

For both implemented routes, call NewFeature(SignOutConfig{...}), then pass the contribution in identityhttp.FeatureServices.Features to identityhttp.NewSelectedHandler. No application-written handlers or route registration are needed. The selected snapshot contains exactly session get and self-signout; omitting the feature exposes neither route nor their selected metadata. Probes may be selected independently. The factory constructs both real handlers without calling stores, clocks or policy callbacks, and rejects nil/zero services and closed rate owners atomically. Configuration inspection does not certify backend health; compose the same primary and policy owners and retain their lifetime responsibility. This remains root-mounted exact dispatch, not the full wire-OpenAPI/security-router implementation.

Call NewGetHandler(GetConfig{...}) with a real owner-constructed *session.SessionService, one shared *identityhttp.SafeRateLimiter, explicit session.CredentialCookie or session.CredentialBearer, valid cookie policy, trusted clock, whole-handler deadline and exact identityhttp.NetworkConfig. The native PostgreSQL session service supplies primary authority in reference composition. Do not supply a consumer-written domain store as authentication.

Construct dependencies before serving and retain them until handlers drain. The handler borrows dependencies, starts no goroutines, owns no store or transaction, and closes neither requests nor dependencies. The HTTP server owns connection/body cleanup and bounded writes. Earlier request deadlines win. Do not synthesize credential headers or mutate trusted request metadata in middleware. No hidden registration, default transport or global state exists.

API and security

NewGetHandlerWithMiddleware(config, middleware) adds trusted synchronous hooks at the real before-auth, after-auth, before-handler and after-handler boundaries. NewGetHandler uses the same pipeline without hooks. After-auth and later hooks may call identityhttp.PipelineValue[sessionhttp.GetState](r) and state.Principal() for detached credential-free metadata; neither the value nor that metadata is a reusable authority proof. Before-auth has no principal. Trusted hooks still receive original credential-bearing request headers in their detached view: never log, serialize or retain those headers. Only the typed state is credential-free. Middleware cannot replace the original tenant/credential inputs, read a body or repeat authentication/admission by calling next twice. Original validity is checked after admission and again after before-handler hooks; hooks cannot renew it. After-handler is read-only and observes an attempted read, not a transaction commit. Short circuits suppress later stages. Borrowed hooks must honor context, avoid retaining state and support concurrent requests. The read handler enforces no-store/nosniff, JSON media type and no Set-Cookie at response commit even for short circuits and attempted header removal. Hooks own their bounded JSON denial bodies. The full security router remains in development.

Only exact GET /v1/session/get is accepted. Requests have no body, query or Idempotency-Key. Tenant comes only from an exact trusted HTTPS host binding. Unknown/public hosts return 421 before tenant-state access. Malformed requests return 400, wrong path 404, wrong method 405, limits 429 with Retry-After, and unavailable/canceled authority 503. Responses use fixed safe errors, JSON, Cache-Control: no-store and X-Content-Type-Options: nosniff.

Cookie mode rejects Authorization and ambiguous selected cookies. Cookie bounds are 32 entries, 16KiB total value bytes and 3,800 bytes per selected name/value. Bearer mode uses exactly one strict Authorization header and ignores ambient session cookies; missing or invalid bearer cannot fall back. No credential is accepted from query, body, form or redirects. Both modes forbid proxy credentials.

Successful GetResponse contains admitted principal, public session timestamps, original lifetime deadlines and advisory refresh metadata, never token, lookup digest, CSRF state, complete authority vector or historical proof. The handler validates original owner custody after rate work; a copied result cannot renew freshness. Unauthenticated is {"authenticated":false,"needsRefresh":false}. Reading does not rotate, update activity, clear cookies or refresh authority.

Self-signout

NewSignOutHandlerWithMiddleware exposes the same four real phases for logout; NewFeatureWithMiddleware applies a selected list independently to both real routes. Existing constructors use those pipelines without hooks. Authenticate discovers the original subject once, then the signout domain service performs its existing required primary revalidation and protected transactional CSRF. No extra router authentication or rate debit is introduced. SignOutState provides credential-free Principal() and detached Attempt() projections. An attempted result may be unknown, denied or unavailable; it does not assert a commit. Copies of recovery metadata cannot change the original command. Only normally returned uncanceled Handle attempts reach after-handler observation. The original preliminary validity is rechecked after hooks, never extended. The shared response guard suppresses every hook-created cookie and permits only the owned exact deletion tuple after successful selected cookie logout. Bearer, denied, ambiguous and short-circuit responses clear no cookies. Hooks must not log/retain credential-bearing headers, retain state or start detached work.

Both handlers support base HTTP's WithOperationMiddleware binding after feature construction. It preserves the frozen network/origin policy and borrows the same domain owners, appends rather than replaces existing hooks, and keeps response protection outside every hook. Original handlers remain usable; binding never reconstructs them from mutable caller configuration.

Call NewSignOutHandler(SignOutConfig{...}) with the same GetConfig and exactly the selected CookieSignOut or BearerSignOut owner. Cookie mode uses the strict *session.CookieSignOutService, composed with the owned native transactional CSRF executor, and an explicit exact HTTPS TrustedOrigins allowlist. Referer fallback is denied. Bearer mode uses *session.SignOutService and no trusted-origin configuration. All services must share the same primary, policy and cookie configuration; a returned cookie tuple mismatch fails closed without clearing transport, even when the original command committed.

Only POST /v1/session/signout is accepted. Use JSON {} with a single X-CSRF-Token header, or URL-encoded form with that header or one csrf_token field. Empty URL-encoded forms are valid; media type is always explicit. No query, target/user/policy fields, Idempotency-Key or alternative credential channels are accepted. Duplicate/equal token channels deny. Bearer mode rejects CSRF inputs and ignores ambient cookies. Absent/inactive logout needs no CSRF token; active cookie logout requires its exact token under the original primary transaction, not merely syntactic parsing or an HTTP precheck. Origin remains required for every cookie-mode request, including absent logout.

Successful logout revokes only its authenticated session and clears only its selected transport. Cookie deletion uses the exact owned secure serializer; bearer success returns clearBearer:true, never a Set-Cookie. Outage, denial, ambiguity, conflict, in-progress and cancellation clear nothing, including a committed receipt accompanied by a delivery error or cancellation. Unknown outcome is never translated into rollback or automatically retried.

SignOutResponse includes a server-generated original commandID, explicit commitOutcome and, when prepared, SignOutRecovery with the original exact scope. none means no authoritative command observation. Recovery is a locator, not permission: query the durable owner's recovery API under current policy before retrying an unknown command. The underlying operation remains identity.session.revoke-one; transport does not rename that recovery scope. Errors are fixed redacted codes: malformed 400, denied 403, conflict/in-progress 409, rate 429 with Retry-After, and operational/ambiguous/canceled 503. Host, path and method errors follow the read profile. All responses are no-store.

Adoption, tradeoffs and FAQ

Use one rate limiter per process/deployment namespace across selected routes. Subject admission requires owner authentication, so read authentication precedes combined IP/subject admission. This handler does not claim a preauthentication database-work budget. Its canonical rate counters remain owned by the primitive.

Cookie issuance/CSRF-token delivery, enrichment/extensions, feature descriptors, rendered OpenAPI, browser/reference journeys and exact coverage/mutation/release gates remain required subsequent work. A bare handler is not that complete transport. Published dependency/checksum resolution is pending; local development uses immutable owned-source archives only in disposable test modfiles, never permanent module replacements. See CHANGELOG.md for release notes.

Documentation

Overview

Package sessionhttp supplies explicitly selected session HTTP integrations. Base identity/http does not import this module or any optional session owner.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewFeature

func NewFeature(c SignOutConfig) (identityhttp.Feature, error)

NewFeature constructs the currently implemented session get/self-signout contribution from real typed owner services, without IO or registration. It requires one consistent explicit GetConfig/SignOutConfig; nil/zero/closed owners deny atomically. Only those two operations are contributed, not future session capabilities. Cookie uses exact origin and active primary synchronizer state, with no independent token-age claim; bearer contributes CSRFNone and never borrows cookie authority. Disabled means omit this feature entirely.

Pass the returned immutable contribution to FeatureServices and NewSelectedHandler. Base HTTP never imports this integration. Domain stores, transactions, authentication, CSRF and rate remain owned by the configured handlers/services. Construction copies metadata; borrowed dependencies must outlive concurrent requests. Separate owned Wire contributions describe the actual selected JSON, credential and CSRF profile for NewDocumentedHandler; direct-contract summaries never become wire response schemas.

func NewFeatureWithMiddleware

func NewFeatureWithMiddleware(c SignOutConfig, middleware []identityhttp.MiddlewareDescriptor) (identityhttp.Feature, error)

NewFeatureWithMiddleware contributes the same two real operations with the selected trusted middleware applied independently at their owned phases. After-auth hooks obtain either GetState or SignOutState through PipelineValue; no shared global principal or duplicate authentication/rate owner is added. All selected dependencies are validated before wrapper construction. Hooks may be constructed once per operation and must support concurrent requests. The factory returns no partial feature on failure; it performs no owner IO.

func NewFeatureWithPreAuth

func NewFeatureWithPreAuth(c SignOutConfig, p PreAuthConfig) (identityhttp.Feature, error)

NewFeatureWithPreAuth explicitly combines session reads/logout with original flow initiation under their single canonical session owner. NewFeature remains unchanged. No owner dependency is optional when this combined profile is used.

func NewFeatureWithPreAuthAndMiddleware

func NewFeatureWithPreAuthAndMiddleware(c SignOutConfig, p PreAuthConfig, middleware []identityhttp.MiddlewareDescriptor) (identityhttp.Feature, error)

NewFeatureWithPreAuthAndMiddleware retains each operation's typed phases. Both profiles are validated before returning a contribution; construction performs no owner IO and never returns a partial selection on failure.

func NewGetHandler

func NewGetHandler(c GetConfig) (http.Handler, error)

NewGetHandler constructs only GET /v1/session/get, without registration or IO. It validates the request envelope, exact HTTPS tenant host, non-keyed empty request and explicitly selected credential channel before owner access. Cookie mode rejects Authorization; bearer mode ignores ambient cookies and never falls back. All decisions use canonical rate.safe admission. Positive owner custody is checked again after rate work, preserving original expiry. Success is 200 authenticated metadata or enumeration-safe unauthenticated; malformed input is 400, wrong host/realm 421, wrong path 404/method 405, rate denial 429 with Retry-After, operational/cancellation failure 503. Every response is no-store, nosniff JSON and never emits Set-Cookie. No read updates activity, rotates credentials, refreshes authority or starts work in a goroutine. The server retains body/connection cleanup and bounded writes. This handler alone is not full feature/router/OpenAPI or browser qualification.

func NewGetHandlerWithMiddleware

func NewGetHandlerWithMiddleware(c GetConfig, middleware []identityhttp.MiddlewareDescriptor) (http.Handler, error)

NewGetHandlerWithMiddleware preserves NewGetHandler's transport and security contract while exposing real typed execution boundaries. Decode acquires original network/credential input, Authenticate performs the single owner read, Admit applies the single safe-route admission, and Handle rechecks the original owner validity after before-handler hooks before serializing. Middleware follows identityhttp.NewOperationPipeline's trusted synchronous ownership rules. Its typed GetState discloses no credential material; detached request headers still contain original credentials and must never be logged or retained. Hooks have no readable body. After-handler observes attempts, not transaction commits. Read response protections also cover hook short circuits: no-store/nosniff JSON media type and no Set-Cookie. Hooks own their bounded JSON denial body. Constructor validation performs no owner reads or rate admission.

func NewMetadataRouter

NewMetadataRouter explicitly adds protected GET /openapi.json to selected services and constructs their secure router and exact immutable OpenAPI snapshot. Neither session routes nor public metadata are implicitly selected. Selecting a competing metadata route fails atomically. Construction invokes no authentication, authorization, rate, readiness or domain work.

The real owner phases are bounded decode -> one session read -> one shared rate.safe admission -> revision-fenced explicit administrator authorization -> exact snapshot export. Missing/inactive sessions return401; explicit policy deny returns403; stale/error/expired authority returns503; rate denial429. A 401 challenge names Bearer for bearer mode or the private Session scheme for cookie mode; it never advertises credential fallback or redirects. Shared transport rejects plaintext with JSON https_required (403); disabled cross-origin access returns fixed text/plain origin_denied (403, or408 when cancellation is observed during origin admission), before session work. Cookies never replace selected bearer credentials; cookie mode rejects Authorization. Metadata GET mutates nothing and emits no cookie or secret. Original session validity is rechecked after rate/policy work and hooks; no copied evidence gains freshness. Middleware observes the actual GetState.

Transport uses Read.Network and Read.MaximumDuration; CORS grants are disabled. Other selected features must share those policies and retain their own owners. Global hooks require every selected handler to support the existing binder. Start the returned handler with NewHTTPServer for bounded raw IO/streaming. This is an optional session profile, not JWT/API-key administration, public realm mapping, a complete reference server or release qualification.

func NewPreAuthFeature

func NewPreAuthFeature(c PreAuthConfig) (identityhttp.Feature, error)

NewPreAuthFeature selects only original browser-flow initiation. It does not select session reads, logout or OTP operations. Omit this contribution to disable its route and metadata. Construction validates borrowed dependencies without IO; their concurrency and lifetime contracts remain unchanged.

func NewPreAuthFeatureWithBrowserDevice

func NewPreAuthFeatureWithBrowserDevice(c PreAuthConfig, devices session.BrowserDeviceConfig) (identityhttp.Feature, error)

NewPreAuthFeatureWithBrowserDevice selects original flow initiation with non-authenticating browser correlation. Omission disables both its route and metadata; the existing flow-only feature remains unchanged.

func NewPreAuthFeatureWithBrowserDeviceAndMiddleware

func NewPreAuthFeatureWithBrowserDeviceAndMiddleware(c PreAuthConfig, devices session.BrowserDeviceConfig, middleware []identityhttp.MiddlewareDescriptor) (identityhttp.Feature, error)

NewPreAuthFeatureWithBrowserDeviceAndMiddleware selects the protected real dual-cookie pipeline with typed hooks and accurate selected wire metadata.

func NewPreAuthFeatureWithMiddleware

func NewPreAuthFeatureWithMiddleware(c PreAuthConfig, middleware []identityhttp.MiddlewareDescriptor) (identityhttp.Feature, error)

NewPreAuthFeatureWithMiddleware contributes the same real handler with its owned typed phases and concrete wire schema. Hooks cannot substitute original admission or manufacture cookie delivery. No global registration is used.

func NewPreAuthHandler

func NewPreAuthHandler(c PreAuthConfig) (http.Handler, error)

NewPreAuthHandler constructs only POST /v1/session/preauth-issue, with no registration or IO. Without Methods or IdentifierBinder, strict JSON accepts only optional boolean remember; absence selects configured default and null is invalid. Query, trailers, Idempotency-Key and Authorization are forbidden. Ambient cookies confer no authority and are ignored: a deliberate fresh start creates an independent flow. Exact HTTPS tenant host and configured Origin precede IP-only rate.auth, command generation, Prepare and Issue. There is no automatic retry. Only a fresh exact error-free native commitment emits its secure flow cookie; replay, outage, cancellation, mismatched state and ambiguity emit none. Responses are no-store/nosniff JSON: 200 issued, 400 malformed, 403 origin, 404 path, 405 method, 421 host, 429 rate, 409 conflict/in-progress, 503 unavailable. Native recovery stays outcome-only. Handler alone does not advertise a feature, satisfy browser/reference journeys or establish full release qualification. Explicit Methods additionally accepts an optional exact string method. It selects a frozen deployment profile; omission retains the base profile. Unknown names or malformed selection deny before rate debit or owner work. Explicit IdentifierBinder profiles additionally require bounded identifier; unbound profiles forbid it. Binding runs after IP admission before Prepare.

func NewPreAuthHandlerWithBrowserDevice

func NewPreAuthHandlerWithBrowserDevice(c PreAuthConfig, devices session.BrowserDeviceConfig) (http.Handler, error)

NewPreAuthHandlerWithBrowserDevice explicitly selects a server-random, non-authenticating browser correlation cookie alongside original flow issuance. Absent custody is issued only after IP admission; valid custody keeps its original expiry and is never redelivered. Malformed, duplicated, expired or tenant/origin-mismatched custody denies before owner preparation. Both new cookies are withheld on failure, replay, ambiguity or cancellation. Device configuration is validated and copied without IO. Clearing or copying the cookie can evade/share correlation; independent IP/identifier controls remain necessary. Existing flow-only constructors are unchanged.

func NewPreAuthHandlerWithBrowserDeviceAndMiddleware

func NewPreAuthHandlerWithBrowserDeviceAndMiddleware(c PreAuthConfig, devices session.BrowserDeviceConfig, middleware []identityhttp.MiddlewareDescriptor) (http.Handler, error)

NewPreAuthHandlerWithBrowserDeviceAndMiddleware retains real typed phases and protected dual-cookie ownership. Hooks cannot inspect or forge device custody.

func NewPreAuthHandlerWithMiddleware

func NewPreAuthHandlerWithMiddleware(c PreAuthConfig, middleware []identityhttp.MiddlewareDescriptor) (http.Handler, error)

NewPreAuthHandlerWithMiddleware retains the same security contract with real typed pipeline phases. Authenticate is explicitly public/no-user; Admit owns the original-flow rate debit, and Handle owns the single issuance attempt. Hooks cannot manufacture cookie output or inspect the preparation. Detached request headers remain potentially sensitive and must not be logged/retained.

func NewSignOutHandler

func NewSignOutHandler(c SignOutConfig) (http.Handler, error)

NewSignOutHandler constructs only POST /v1/session/signout. Bodies are strict JSON {} or URL-encoded empty fields/one csrf_token; JSON CSRF uses exactly one X-CSRF-Token header. Query parameters and Idempotency-Key are forbidden. Token absence reaches the domain owner: absent/inactive logout succeeds, but active cookie logout requires exact primary transaction-bound CSRF. Present empty, duplicate, malformed or mixed channels always deny. Bearer rejects CSRF input and ignores ambient cookies, never falling back or deleting their transport. Exact HTTPS host determines tenant. rate.safe admission precedes mutation; subject discovery is a primary read, not preauthentication database limiting. Only committed error-free or absent/inactive success clears transport. Errors preserve original command/outcome/recovery without automatic retries. Status codes are 200 success, 400 malformed, 403 denial, 404 path, 405 method, 421 host, 429 rate, 409 conflict/in-progress and 503 unavailable/ambiguous/canceled. Responses are no-store/nosniff JSON. Whole-request deadlines never extend the inbound deadline. No goroutines, stores, transactions or journals are added; the server retains bounded body/read/write cleanup. This is not full feature, OpenAPI, native HTTP/browser or release qualification.

func NewSignOutHandlerWithMiddleware

func NewSignOutHandlerWithMiddleware(c SignOutConfig, middleware []identityhttp.MiddlewareDescriptor) (http.Handler, error)

NewSignOutHandlerWithMiddleware preserves NewSignOutHandler's security and recovery contract with real typed hook phases. Decode owns original network, origin, body and credential acquisition; Authenticate performs preliminary subject discovery once; Admit debits safe-route limits once; Handle delegates primary revalidation and transactional CSRF/mutation to the same domain owner. Original preliminary validity is checked after hooks, never renewed by them. After-handler observes the actual attempted outcome, not assumed success. Trusted hooks retain original credential-bearing detached headers: do not log, serialize or retain them. Typed state is opaque/redacted; its getters expose only credential-free detached metadata. Hooks own bounded JSON denial bodies; no hook can clear cookies without confirmed owner success. Shared response policy preserves no-store/nosniff/JSON even on short circuits.

Types

type AnonymousGetConfig

type AnonymousGetConfig struct {
	Sessions *session.AnonymousSessionService
	Cookie   session.CookiePolicy
}

AnonymousGetConfig explicitly selects unprivileged guest recognition beside the permanent cookie profile. Sessions is a constructed, borrowed anonymous owner; Cookie must be valid and named differently from the permanent cookie. Both cookies are decoded strictly. A present permanent cookie selects only permanent validation, even if inactive or unavailable: no guest fallback. This option is cookie-only and never enables a route, signout or metadata administration by itself. Construction copies it without owner IO.

type AnonymousResponse

type AnonymousResponse struct {
	Recognized bool      `json:"recognized"`
	Principal  Principal `json:"principal"`
	Session    Session   `json:"session"`
}

AnonymousResponse is explicit, credential-free guest metadata. Recognized means only a live unprivileged guest session; it is never authenticated-user admission, an upgrade proof or refresh authority. The original owner lifetime is retained. Omitted entirely when this profile is unselected or inactive.

type GetConfig

type GetConfig struct {
	Network         identityhttp.NetworkConfig
	Sessions        *session.SessionService
	Rate            *identityhttp.SafeRateLimiter
	Transport       session.CredentialKind
	Cookie          session.CookiePolicy
	Now             func() time.Time
	MaximumDuration time.Duration
	// Anonymous optionally selects the distinct guest cookie/read profile for
	// GET only. It never grants verified-user admission or changes signout.
	Anonymous *AnonymousGetConfig
}

GetConfig explicitly selects one transport and immutable network policy. Sessions and Rate must be valid owner-constructed, concurrency-safe services; they remain borrowed and must outlive requests. Share Rate across all routes, never construct it per request. Cookie must be valid even in bearer mode. Now is a trusted prompt concurrency-safe clock. MaximumDuration is 1ms..30s; earlier inbound deadlines win. Construction copies network configuration.

type GetResponse

type GetResponse struct {
	Authenticated     bool               `json:"authenticated"`
	Principal         *Principal         `json:"principal,omitempty"`
	Session           *Session           `json:"session,omitempty"`
	NeedsRefresh      bool               `json:"needsRefresh"`
	RefreshEligibleAt *time.Time         `json:"refreshEligibleAt,omitempty"`
	Anonymous         *AnonymousResponse `json:"anonymous,omitempty"`
}

GetResponse is the explicit credential-free read projection. It conveys no reusable authentication authority. Without explicit guest selection, unauthenticated responses omit all subject/session fields. A recognized guest appears only in Anonymous, never the verified Principal/Session fields. Sensitive extension/enrichment data is not implicit.

type GetState

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

GetState is an immutable request-local session-read snapshot. Its private credential and owner result remain inaccessible to middleware. Zero is unauthenticated. Copies never renew the owner's original validity evidence. Obtain it only through identityhttp.PipelineValue[GetState] during a trusted synchronous after-auth or later hook; it is not an authorization proof.

func (GetState) Format

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

Format redacts private credential, network and historical owner custody.

func (GetState) MarshalJSON

func (GetState) MarshalJSON() ([]byte, error)

MarshalJSON rejects implicit export of the request-local owner snapshot.

func (GetState) MarshalText

func (GetState) MarshalText() ([]byte, error)

MarshalText rejects implicit text/map-key export of the owner snapshot.

func (GetState) Principal

func (s GetState) Principal() (Principal, bool)

Principal returns detached credential-free metadata from the actual session owner. The boolean is false for absent/unknown/inactive credentials. Metadata must not be retained or reused as authentication or authorization authority.

type MetadataConfig

type MetadataConfig struct {
	Read           GetConfig
	Authorization  authorization.Authorizer
	PolicyRevision authorization.Revision
	Action         authorization.Action
	ResourceType   authorization.ResourceType
}

MetadataConfig explicitly selects session-authenticated administrator metadata. Read supplies the same valid owner, cookie/bearer, host, clock, duration and shared rate configuration used by session reads; only mapped tenant hosts are supported. Public-host/global administration is not inferred. Authorization is a borrowed concurrency-safe existing policy engine/authorizer; PolicyRevision must be positive. Action and ResourceType are explicit bounded deployment policy identifiers, never request fields or inferred token scopes. The resource ID is always identity.openapi.document. All owners must honor context cancellation and outlive this handler; no owner is closed here.

type PreAuthConfig

type PreAuthConfig struct {
	Network                       identityhttp.NetworkConfig
	Service                       PreAuthService
	Rate                          *identityhttp.AuthRateLimiter
	Cookie                        session.CookiePolicy
	Lifetime                      session.LifetimeConfig
	Origin                        identityhttp.Origin
	Action, CallbackURL, PolicyID string
	PolicyVersion                 uint64
	Now                           func() time.Time
	MaximumDuration               time.Duration
	// Purpose zero retains session-issuing login. Phone-password recovery
	// requires IdentifierBinder and forbids remember input entirely. Recovery
	// alone needs no Lifetime configuration and never resolves session policy.
	Purpose session.PreAuthPurpose
	// IdentifierBinder optionally requires a target for the base profile.
	// Omission preserves the closed remember-only legacy request contract.
	IdentifierBinder PreAuthIdentifierBinder
	// Methods optionally enables exact named login-method selection. Omission
	// keeps the original profile and closed remember-only request contract.
	// Profiles share Network, Origin, Rate, Cookie, Lifetime and device custody;
	// each borrowed owner must enforce its own fixed original intent. The slice
	// is copied at construction. No method, action or policy is auto-registered.
	Methods []PreAuthMethod
}

PreAuthConfig freezes one explicit browser initiation profile. Network is copied; Service and Rate are borrowed concurrency-safe owners. Share Rate across requests. Origin, Action, callback and policy are deployment choices, never client input; they must match the selected issuer exactly. Empty callback disables redirects. Cookie must be __Host-identity_flow with Lax/Strict policy. Lifetime resolves remember once for the eventual session, not the fixed five-minute flow. Now is a prompt trusted concurrency-safe clock; MaximumDuration is 1ms..30s and never extends an inbound deadline.

func (PreAuthConfig) Format

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

Format prevents traversal of borrowed service and deployment configuration.

func (PreAuthConfig) MarshalJSON

func (PreAuthConfig) MarshalJSON() ([]byte, error)

MarshalJSON rejects implicit runtime composition export.

func (PreAuthConfig) MarshalText

func (PreAuthConfig) MarshalText() ([]byte, error)

MarshalText rejects implicit deployment configuration export.

type PreAuthIdentifierBinder

type PreAuthIdentifierBinder interface {
	BindPreAuthIdentifier(context.Context, identity.TenantID, string) ([32]byte, error)
}

PreAuthIdentifierBinder binds an explicitly selected original target before preparation. The borrowed concurrency-safe owner canonicalizes the bounded raw identifier under its fixed policy and deployment key; it must not look up accounts, authenticate, mutate state, or accept a client-supplied digest. Calls occur after shared IP admission, outside database work, with a bounded context. Return InvalidInputError for invalid input; other errors or a zero binding fail unavailable. Do not log or retain raw identifiers. The reference composition supplies the concrete phone binder; base HTTP imports no workflow.

type PreAuthMethod

type PreAuthMethod struct {
	Name                          string
	Service                       PreAuthService
	Action, CallbackURL, PolicyID string
	PolicyVersion                 uint64
	// Purpose selects the trusted issuer's exact original profile, not a body
	// option. Recovery requires a target binder and carries zero Remember.
	Purpose session.PreAuthPurpose
	// IdentifierBinder requires identifier only when this exact method is
	// selected. Phone/password initiation requires this owner; other methods
	// retain their explicitly unbound compatibility profile when it is nil.
	IdentifierBinder PreAuthIdentifierBinder
}

PreAuthMethod binds a public selection name to one trusted original-flow owner. Name is 1..32 lowercase ASCII letters, digits or hyphens. Service is borrowed, bounded and concurrency-safe. Action, callback and policy are fixed deployment values, never request overrides. One to sixteen distinct names may be selected; omitted method retains PreAuthConfig's base profile.

func (PreAuthMethod) Format

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

Format hides borrowed service and trusted deployment bindings.

func (PreAuthMethod) MarshalJSON

func (PreAuthMethod) MarshalJSON() ([]byte, error)

MarshalJSON rejects implicit deployment custody export.

func (PreAuthMethod) MarshalText

func (PreAuthMethod) MarshalText() ([]byte, error)

MarshalText rejects implicit deployment custody export.

type PreAuthResponse

type PreAuthResponse struct {
	Issued        bool                          `json:"issued"`
	CommandID     identity.CommandID            `json:"commandID,omitempty"`
	FlowID        identity.PreAuthTransactionID `json:"flowID,omitempty"`
	IssuedAt      time.Time                     `json:"issuedAt"`
	ExpiresAt     time.Time                     `json:"expiresAt"`
	CommitOutcome string                        `json:"commitOutcome"`
	Error         string                        `json:"error,omitempty"`
}

PreAuthResponse exposes only the original credential-free command/flow locator and timeline. It is neither a user session nor active authority. CommitOutcome none means no authoritative observation, not rollback. A known commitment can survive failed delivery. Retain the original locator for native outcome-only recovery; it grants no lookup permission and never renews a flow. Issued is true only when this response delivers the original secure cookie.

type PreAuthService

PreAuthService is the borrowed original-flow owner seam. The selected reference PreAuthService satisfies it without importing reference or stores. Implementations must retain native startup/tenant/shutdown admission, own entropy and authoritative execution, and honor bounded contexts. Issue runs one unchanged preparation; unknown outcomes never authorize another Prepare.

type PreAuthState

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

PreAuthState is opaque request-local public-flow state. It authenticates no user and cannot expose prepared nonce, digest or bearer to middleware. Do not retain it beyond the request. Zero has no attempted issuance.

func (PreAuthState) Attempt

func (s PreAuthState) Attempt() (PreAuthResponse, bool)

Attempt returns detached safe metadata. True means Handle entered, not that native execution committed or a cookie reached the browser.

func (PreAuthState) Format

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

Format prevents traversal of private request-local custody.

func (PreAuthState) MarshalJSON

func (PreAuthState) MarshalJSON() ([]byte, error)

MarshalJSON rejects implicit request-local export.

func (PreAuthState) MarshalText

func (PreAuthState) MarshalText() ([]byte, error)

MarshalText rejects implicit request-local text export.

type Principal

type Principal struct {
	TenantID identity.TenantID `json:"tenantID"`
	UserID   identity.UserID   `json:"userID"`
}

Principal identifies only the admitted owner and exact host-mapped tenant. It is response metadata, not an authorization proof.

type Session

type Session struct {
	ID                string    `json:"id"`
	IssuedAt          time.Time `json:"issuedAt"`
	LastActivityAt    time.Time `json:"lastActivityAt"`
	ExpiresAt         time.Time `json:"expiresAt"`
	AbsoluteExpiresAt time.Time `json:"absoluteExpiresAt"`
}

Session exposes stable public read metadata, never lookup/CSRF digests, authority vectors, credential IDs, historical proofs or bearer material.

type SignOutConfig

type SignOutConfig struct {
	GetConfig
	CookieSignOut  *session.CookieSignOutService
	BearerSignOut  *session.SignOutService
	TrustedOrigins []identityhttp.Origin
}

SignOutConfig selects the same immutable transport/network/rate profile as GetConfig. Exactly the selected CookieSignOut or BearerSignOut is required; compose it with the same primary SessionService and cookie policy. Cookie mode requires 1..128 exact trusted HTTPS origins and denies Referer fallback. Bearer mode rejects TrustedOrigins rather than silently using cookie policy. Services are borrowed, concurrency-safe and must outlive requests; no IO or resource ownership is transferred at construction.

type SignOutRecovery

type SignOutRecovery struct {
	CommandID       identity.CommandID        `json:"commandID"`
	TenantID        identity.TenantID         `json:"tenantID"`
	UserID          identity.UserID           `json:"userID,omitempty"`
	AnonymousFlowID string                    `json:"anonymousFlowID,omitempty"`
	ActorID         identity.ActorID          `json:"actorID"`
	ActorKind       authorization.SubjectKind `json:"actorKind"`
	OperationID     string                    `json:"operationID"`
	RecordID        string                    `json:"recordID,omitempty"`
	LinkMode        identity.AccountLinkMode  `json:"linkMode,omitempty"`
}

SignOutRecovery is the original durable command's exact scope locator, not authorization or renewed authentication. Query the owned recovery service under current result-access policy before retrying an ambiguous operation. These fields never contain credentials, digests, proof or CSRF material.

type SignOutResponse

type SignOutResponse struct {
	SignedOut     bool               `json:"signedOut"`
	ClearBearer   bool               `json:"clearBearer"`
	CommandID     identity.CommandID `json:"commandID,omitempty"`
	CommitOutcome string             `json:"commitOutcome"`
	Recovery      *SignOutRecovery   `json:"recovery,omitempty"`
	Error         string             `json:"error,omitempty"`
}

SignOutResponse is the explicit credential-free success/error projection. CommitOutcome is none, not_committed, committed, unknown, conflict or in_progress. none means no authoritative command observation, not rollback. A committed observation accompanied by an error does not clear transport. ClearBearer instructs the caller to discard only its presented bearer after success; cookie removal is conveyed only by the exact Set-Cookie tuple. CommandID is generated once per admitted call and never replaced or retried.

type SignOutState

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

SignOutState is opaque request-local owner state. Principal describes the original preliminary subject read, not reusable authorization. The protected signout service still revalidates primary authority and atomically checks CSRF. The owner alone updates attempted result state; no hook can change it. Zero is unauthenticated and unattempted. State must not be retained beyond a request.

func (SignOutState) Attempt

func (s SignOutState) Attempt() (SignOutResponse, bool)

Attempt returns a detached copy of the original operation's response state. The boolean means Handle entered, not that a transaction committed. Before Handle it is false; a true result may have outcome none/unknown or an error. After-handler observes normally returned attempts; cancellation/panic can suppress that phase. Recovery is a locator, never a retry or access grant.

func (SignOutState) Format

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

Format redacts private credential, CSRF and historical owner custody.

func (SignOutState) MarshalJSON

func (SignOutState) MarshalJSON() ([]byte, error)

MarshalJSON rejects implicit export of request-local owner custody.

func (SignOutState) MarshalText

func (SignOutState) MarshalText() ([]byte, error)

MarshalText rejects implicit text/map-key export of request-local state.

func (SignOutState) Principal

func (s SignOutState) Principal() (Principal, bool)

Principal returns detached metadata from the preliminary session owner read. It is neither fresh-login evidence nor authority for a future operation.

Jump to

Keyboard shortcuts

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