Documentation
¶
Index ¶
- Constants
- func DefaultRateLimits() map[string]ratelimit.Limit
- func JWKSHandler(jwks jwtkit.JWKS) http.Handler
- func LanguageMiddleware(cfg *LanguageConfig) func(http.Handler) http.Handler
- func MountHandler(svc *Service, opts MountOptions) (h http.Handler, err error)
- type ActionAvailability
- type AuthCapabilities
- type AuthPasskeyCapabilities
- type AuthPasswordCapabilities
- type AuthPasswordlessCapabilities
- type AuthProviderSummary
- type AuthRegistrationCapabilities
- type AuthSolanaCapabilities
- type AuthVerificationCapabilities
- type ClientIPFunc
- type Config
- type DocumentProvider
- type LanguageConfig
- type MountOptions
- type RateLimitResult
- type RateLimiter
- type RateLimiterWithResult
- type RouteAuthTier
- type RouteGroup
- type RouteRef
- type RouteSpec
- type Service
- func (s *Service) APIRoutes(groups ...RouteGroup) []RouteSpec
- func (s *Service) CheckSMSHealth(ctx context.Context) error
- func (s *Service) Close()
- func (s *Service) JWKSHandler() http.Handler
- func (s *Service) OIDCBrowserRoutes(groups ...RouteGroup) []RouteSpec
- func (s *Service) PermissionGroupRoutes() []RouteSpec
- func (s *Service) SMSAvailable() bool
- func (s *Service) SMSHealthy() bool
- func (s *Service) Verifier() *verify.Verifier
Constants ¶
const ( ActionUpdateUsername = authkit.ActionUpdateUsername ActionRequestPasswordReset = authkit.ActionRequestPasswordReset ActionRequestVerification = authkit.ActionRequestVerification )
const ( // 2FA-specific rate limit buckets RL2FAStartPhone = "auth_2fa_start_phone" RL2FAStartTOTP = "auth_2fa_start_totp" RL2FAEnable = "auth_2fa_enable" RL2FADisable = "auth_2fa_disable" RL2FARegenerateCodes = "auth_2fa_regenerate_codes" RL2FAVerify = "auth_2fa_verify" RLAuthToken = "auth_token" RLAuthRegister = "auth_register" RLAuthRegisterAvailability = "auth_register_availability" RLAuthRegisterAbandon = "auth_register_abandon" RLInviteCreate = "auth_invite_create" RLPasswordLogin = "auth_password_login" RLPasswordlessStart = "auth_passwordless_start" RLPasswordlessConfirm = "auth_passwordless_confirm" RLPasskeyRegister = "auth_passkey_register" RLPasskeyLogin = "auth_passkey_login" RLDeviceKeyEnrollBegin = "auth_device_key_enroll_begin" RLDeviceKeyEnrollFinish = "auth_device_key_enroll_finish" RLDeviceKeyLoginBegin = "auth_device_key_login_begin" RLDeviceKeyLoginFinish = "auth_device_key_login_finish" RLDeviceKeysManage = "auth_device_keys_manage" RLAuthLogout = "auth_logout" RLAuthSessionsList = "auth_sessions_list" RLAuthSessionsRevoke = "auth_sessions_revoke" RLAuthSessionsRevokeAll = "auth_sessions_revoke_all" // #264 application self-registration (per-IP AND per-domain/slug keys). RLApplicationRegister = "application_register" // #264 anti-squat velocity: group settings (slug rename IS a claim), // keyed per-IP and per-user. RLGroupSettings = "group_settings" // #263 anti-squat velocity: generated persona-instance creation (a create // IS a claim), keyed per-IP and per-user. RLGroupCreate = "group_create" // #261 delegated-token mint (authenticated; bounds signing cost per IP). RLDelegatedTokenMint = "delegated_token_mint" RLPasswordResetRequest = "auth_pwd_reset_request" RLPasswordResetConfirm = "auth_pwd_reset_confirm" // #312: one bucket per contact flow, whichever channel the identifier names. RLVerifyRequest = "auth_verify_request" RLVerifyConfirm = "auth_verify_confirm" RLRegisterResend = "auth_register_resend" RLContactChangeRequest = "auth_contact_change_request" RLOIDCStart = "auth_oidc_start" RLOIDCCallback = "auth_oidc_callback" RLUserPasswordChange = "auth_user_password_change" RLUserMe = "auth_user_me" RLUserUpdateUsername = "auth_user_update_username" RLUserPreferredLanguage = "auth_user_preferred_language" RLUserDelete = "auth_user_delete" RLUserUnlinkProvider = "auth_user_unlink_provider" RLAdminUserSessionsList = "auth_admin_user_sessions_list" // The admin session route revokes ALL of a user's sessions; there is no // single-session admin revoke, so no RLAdminUserSessionsRevoke bucket. RLAdminUserSessionsRevokeAll = "auth_admin_user_sessions_revoke_all" // Solana SIWS authentication RLSolanaChallenge = "auth_solana_challenge" RLSolanaLogin = "auth_solana_login" RLSolanaLink = "auth_solana_link" )
Bucket names used by authkit endpoints.
const ( DefaultAPIPrefix = "/api/v1" DefaultOIDCPath = "/oidc" JWKSPath = "/.well-known/jwks.json" // DocumentsPath is the root-anchored published-document surface (#260). // Addressable in ExcludeRoutes as GET DocumentsPath (dropping GET+HEAD). DocumentsPath = "/.well-known/authkit/documents/{digest}" )
Mount anchors. JWKS and browser OIDC are root-anchored by spec/convention (verifiers derive the JWKS URL from the issuer; OIDC redirect URIs are registered with providers), while the JSON API is prefix-anchored. The resolution (#250): MountHandler is ONE handler mounted at the HOST ROOT — JWKS at JWKSPath, browser OIDC under DefaultOIDCPath, API under APIPrefix.
const AccessTokenType = jwtkit.AccessTokenType
AccessTokenType is the canonical JOSE `typ` header value for an AuthKit user access token.
const DelegatedAccessTokenType = jwtkit.DelegatedAccessTokenType
DelegatedAccessTokenType is the canonical JOSE `typ` header value for a delegated access token.
const RefreshCookieName = "authkit_rt"
RefreshCookieName is the cookie the refresh token rides in.
const RemoteApplicationAccessTokenType = jwtkit.RemoteApplicationAccessTokenType
RemoteApplicationAccessTokenType is the JOSE `typ` for a remote application access token. AuthKit resolves authority from the stored remote_application assignment, never from role claims in the token.
Variables ¶
This section is empty.
Functions ¶
func DefaultRateLimits ¶
DefaultRateLimits returns AuthKit's built-in per-endpoint rate limits.
These limits are enforced per client IP (as determined by the Service's ClientIPFunc). Hosts can override by supplying their own limiter via WithRateLimiter(...).
func JWKSHandler ¶
JWKSHandler serves the public JWKS document for the given key set.
func LanguageMiddleware ¶
func LanguageMiddleware(cfg *LanguageConfig) func(http.Handler) http.Handler
LanguageMiddleware infers request language and attaches it to the request context.
func MountHandler ¶ added in v0.84.0
func MountHandler(svc *Service, opts MountOptions) (h http.Handler, err error)
MountHandler returns the full AuthKit surface — JSON API, browser OIDC, and JWKS — as ONE framework-neutral net/http handler. The host mounts it once (a gin host uses gin.WrapH) and rewrites nothing. Every route keeps the gate its RouteSpec carries; the mount adds no auth and removes none.
Types ¶
type ActionAvailability ¶
type ActionAvailability = authkit.ActionAvailability
type AuthCapabilities ¶
type AuthCapabilities struct {
Registration AuthRegistrationCapabilities `json:"registration"`
Providers []AuthProviderSummary `json:"providers"`
Password AuthPasswordCapabilities `json:"password"`
Passwordless AuthPasswordlessCapabilities `json:"passwordless"`
Passkeys AuthPasskeyCapabilities `json:"passkeys"`
Solana AuthSolanaCapabilities `json:"solana"`
Verification AuthVerificationCapabilities `json:"verification"`
Languages []string `json:"languages,omitempty"`
}
AuthCapabilities is the public, static auth feature-discovery response.
type AuthPasskeyCapabilities ¶
type AuthPasskeyCapabilities struct {
Login bool `json:"login"`
}
type AuthPasswordCapabilities ¶
type AuthPasswordCapabilities struct {
Login bool `json:"login"`
}
type AuthProviderSummary ¶
type AuthSolanaCapabilities ¶
type AuthSolanaCapabilities struct {
Login bool `json:"login"`
}
type AuthVerificationCapabilities ¶
type AuthVerificationCapabilities struct {
Registration string `json:"registration"`
}
type ClientIPFunc ¶
ClientIPFunc determines the client IP used for rate limiting and auditing.
Returning an empty string means "unknown" and causes rate limiting to fail open.
func ClientIPFromForwardedHeaders ¶
func ClientIPFromForwardedHeaders(trusted, cloudflare []netip.Prefix) ClientIPFunc
ClientIPFromForwardedHeaders derives the client IP behind proxies the host declared. A peer inside trusted or cloudflare enables the right-to-left X-Forwarded-For walk (hops in either set are skipped as our own). Only a peer inside cloudflare may additionally be trusted for CF-Connecting-IP, and only as a fallback when X-Forwarded-For yields nothing: a generic reverse proxy forwards CF-Connecting-IP verbatim, so honouring it from any trusted peer let a client pick its own rate-limit key (ak#298). Any other peer resolves to itself.
Hosts that pass a cloudflare set must also lock the origin down to Cloudflare ingress; otherwise a client that reaches the origin directly is its own peer and both headers are ignored, which is the safe outcome.
func DefaultClientIP ¶
func DefaultClientIP() ClientIPFunc
DefaultClientIP returns the immediate peer IP from RemoteAddr.
This intentionally includes private and loopback peers so embedded/local deployments still get default rate-limit protection. Hosts behind reverse proxies should use ClientIPFromForwardedHeaders with trusted proxy CIDRs when they need the original public client IP instead of the proxy peer.
type Config ¶ added in v0.98.0
type Config struct {
// Redis overrides the engine's Redis client for the HTTP layer's OIDC/SIWS
// state caches and rate limiter. Nil reuses embedded.Deps.Redis (#210), so
// most hosts never set it.
Redis *redis.Client
// RateLimits overlays bucket-specific limits onto DefaultRateLimits (#242).
RateLimits map[string]ratelimit.Limit
// Limiter replaces AuthKit's automatic limiter. ADVANCED: normal
// deployments let AuthKit own the policy (Redis-backed when Redis is wired,
// in-memory otherwise). RateLimits are not applied to a custom limiter.
Limiter RateLimiter
// DisableRateLimiting turns rate limiting off. TESTS ONLY: it removes the
// brute-force and spam protection.
DisableRateLimiting bool
// Client-IP posture (ak#299). Exactly what sits in front of AuthKit must be
// declared — behind an undeclared proxy every client shares the proxy's one
// per-IP rate-limit bucket. One of the four is required.
//
// TrustedProxies are the CIDRs of reverse proxies / load balancers whose
// X-Forwarded-For is honoured (walked right-to-left past our own hops).
// CF-Connecting-IP is never trusted from these peers.
TrustedProxies []string
// CloudflareProxies are Cloudflare's published egress ranges: X-Forwarded-For
// like a trusted proxy plus CF-Connecting-IP when that header is absent.
// Set it ONLY where Cloudflare fronts the origin, and lock the origin down
// to Cloudflare ingress.
CloudflareProxies []string
// DirectPeerIP asserts nothing sits in front: RemoteAddr IS the end client.
DirectPeerIP bool
// ClientIP is a bespoke extraction strategy. ADVANCED: it replaces the
// proxy handling above entirely.
ClientIP ClientIPFunc
// Languages declares the supported UI languages; the zero value is
// English-only.
Languages LanguageConfig
// Documents are the published-document providers (normally
// *documents.Service values) served at the RouteDocuments mount and
// stamped by the delegated-token mint route (#260/#261). Requires
// embedded.Config.Documents.Readers.
Documents []DocumentProvider
// contains filtered or unexported fields
}
Config is the HTTP layer's configuration. Engine data lives in embedded.Config and engine dependencies in embedded.Deps; this is only what the transport itself decides: client-IP posture, rate limiting, languages, published documents.
type DocumentProvider ¶ added in v0.91.0
type DocumentProvider interface {
// Reference is the process snapshot reference stamped into minted tokens.
Reference() documents.Reference
// Lookup serves any persisted digest for the publication route.
Lookup(ctx context.Context, digest string) (documents.SignedDocument, error)
// CurrentDigest re-validates (and repairs) the persisted snapshot artifact.
CurrentDigest(ctx context.Context) (string, error)
// EnsureSigningKID re-signs the artifact when it is not signed by the key
// that just minted a token referencing it.
EnsureSigningKID(ctx context.Context, tokenKID string) error
}
DocumentProvider is the published-document seam shared by the publication route (#260) and the delegated-token mint's document stamping (#261). *documents.Service implements it.
type LanguageConfig ¶
LanguageConfig declares the supported UI languages and default. The query parameter name is NOT configurable — hardcoded to langSelector ("lang") (#143). No language config means English-only (Supported ["en"], default "en").
type MountOptions ¶ added in v0.84.0
type MountOptions struct {
// Groups selects the mounted route groups. Nil mounts the default API
// surface plus browser OIDC. Non-nil mounts exactly the named groups —
// include RouteBrowserOIDC to keep the browser redirect flows.
Groups []RouteGroup
// APIPrefix anchors the JSON API routes. "" means DefaultAPIPrefix; "/"
// mounts the API at root.
APIPrefix string
// ExcludeRoutes drops routes the host shadows with its own handlers.
// Matched by method + prefix-neutral RouteSpec path. Exclusion does NOT
// alter the verifier's MFA-enrollment exempt-path set — that is derived
// from the full route registry at authhttp.New time, so a shadowed enroll
// route stays reachable through the host's replacement.
ExcludeRoutes []RouteRef
// Wrap decorates every RouteSpec-backed handler (API + browser OIDC) at
// mount time. JWKS is not wrapped (it carries no RouteSpec).
Wrap func(RouteSpec, http.Handler) http.Handler
// RefreshCookie (ak#271) delivers the rotating refresh token as an
// HttpOnly+Secure+SameSite=Lax cookie (RefreshCookieName) instead of a JSON
// body field, so an injected script cannot read the durable credential.
// OFF by default: a host that leaves this false gets byte-identical
// behaviour, refresh_token in every body as before.
//
// When on, every session-establishing response sets the cookie and omits
// refresh_token from its body/fragment/postMessage payload; POST /token
// accepts the cookie when the body carries no token (body still wins, so a
// mid-migration client is never stranded); and DELETE /logout clears it.
// The cookie is Path-scoped to this mount's POST /token — the only route
// that reads a refresh token — so it never rides the SPA document or assets.
//
// Browser-facing by construction: the host must serve the SPA and this
// mount on the SAME origin, or the cookie never reaches the refresh call.
RefreshCookie bool
}
MountOptions configures the combined AuthKit surface (MountHandler).
type RateLimitResult ¶
type RateLimitResult struct {
Allowed bool
RetryAfter time.Duration
Availability *ActionAvailability
}
type RateLimiter ¶
RateLimiter is a minimal interface used by adapters.
type RateLimiterWithResult ¶
type RouteAuthTier ¶ added in v0.98.0
type RouteAuthTier string
RouteAuth is the authentication tier a route enforces before its handler runs (#328).
const ( AuthPublic RouteAuthTier = "public" // no principal AuthOptional RouteAuthTier = "optional" // principal used when present AuthRequired RouteAuthTier = "required" // valid principal AuthPermission RouteAuthTier = "permission" // valid principal holding RouteSpec.Permission AuthSigned RouteAuthTier = "signed" // per-message proof (domain fetch / JWS) )
type RouteGroup ¶
type RouteGroup string
RouteGroup identifies a prefix-neutral AuthKit route capability. Host applications can mount all default groups or select only the capabilities they want to expose.
const ( RouteAuth RouteGroup = "auth" // RouteDeviceKeys is the refreshless native-client login surface: email // enrollment plus Ed25519 challenge authentication. RouteDeviceKeys RouteGroup = "device_keys" RouteRegistration RouteGroup = "registration" RouteAccount RouteGroup = "account" RouteAdmin RouteGroup = "admin" RoutePermissionGroups RouteGroup = "permission_groups" RouteBrowserOIDC RouteGroup = "browser_oidc" // RouteApplications is the #264 application self-registration surface // (register / rotate / repoint). Mounted only when the host enables // Config.Applications.SelfRegistration. RouteApplications RouteGroup = "applications" // RouteDelegated is the #261 delegated-token mint surface // (POST /delegated/token). Mounted only when Config.Delegated declares an // audience allowlist. RouteDelegated RouteGroup = "delegated" // RouteDocuments is the #260 published signed-document surface // (GET|HEAD /.well-known/authkit/documents/{digest} — root-anchored like // JWKS, not under the API prefix). Mounted only when document providers // are wired via WithDocuments. RouteDocuments RouteGroup = "documents" )
type RouteRef ¶ added in v0.84.0
RouteRef identifies a route by HTTP method and prefix-neutral RouteSpec path (e.g. "/admin/users", NOT "/api/v1/admin/users"). JWKS is addressable as GET JWKSPath.
type RouteSpec ¶
type RouteSpec struct {
Method string
Path string
Group RouteGroup
Handler http.Handler
// Auth is the tier the handler wrapper enforces before the handler runs;
// Permission names the root/group permission for AuthPermission (#328).
Auth RouteAuthTier
Permission authkit.Perm
// Bucket is the per-IP rate-limit bucket APIRoutes applies in front of the
// handler ("" = none). Per-identifier and branch-specific buckets stay in
// the handler.
Bucket string
// MFAEnrollmentExempt marks a route as part of the 2FA enroll/challenge/
// verify surface a forced-enrollment-gated user (verify.WithRequireMFAEnrollment)
// must still be able to reach. authhttp.New derives the verifier's exempt-path
// allowlist from routes tagged here (#243) — the route table is the single
// source of truth, so a rename/add stays consistent by construction.
MFAEnrollmentExempt bool
}
RouteSpec is a concrete, prefix-neutral route with its AuthKit handler attached. Path parameters use net/http ServeMux syntax, e.g. "/namespaces/{slug}".
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service wraps the internal AuthKit engine with net/http mounting helpers.
func New ¶ added in v0.80.0
New constructs the HTTP adapter over a client the host already built — client-first construction (#142). The host wires the engine and its dependencies on embedded.New; New takes only the HTTP layer's Config. Postgres is REQUIRED: the durable user/role and permission-group store has no in-memory fallback (#106), so the client must be Postgres-backed; pure token verification with no storage uses verify.NewVerifier instead.
Construction fails (returns an error, never panics — #212) when the configuration cannot be served: Config.Validate refuses a missing client-IP posture or a bad CIDR, and the cross-layer checks refuse a "required" registration verification with no sender (Deps.Email / Deps.SMS), document providers without readers, and a delegated route without its authorizer.
Redis is taken ONCE (#210): the engine's Redis client (Deps.Redis) also backs the HTTP layer's OIDC/SIWS state caches and rate limiter; Config.Redis is an override, not a requirement.
client, err := embedded.New(cfg, embedded.Deps{Postgres: pg, Redis: rdb, Email: mailer})
srv, err := authhttp.New(client, authhttp.Config{TrustedProxies: []string{"10.0.0.0/8"}})
func (*Service) APIRoutes ¶
func (s *Service) APIRoutes(groups ...RouteGroup) []RouteSpec
APIRoutes returns AuthKit's enabled JSON API routes. With no groups it returns the default API surface. With groups, it returns only matching routes.
func (*Service) CheckSMSHealth ¶
CheckSMSHealth probes (without sending an SMS) whether the configured sender can actually deliver, caching the result to gate phone-based flows. Returns the probe error (nil = healthy) so the host app can log it at startup.
func (*Service) Close ¶ added in v0.98.0
func (s *Service) Close()
Close stops the background work New started (the in-memory limiter's sweep). Idempotent; safe on a nil Service.
func (*Service) JWKSHandler ¶
JWKSHandler returns a handler for GET /.well-known/jwks.json.
func (*Service) OIDCBrowserRoutes ¶
func (s *Service) OIDCBrowserRoutes(groups ...RouteGroup) []RouteSpec
OIDCBrowserRoutes returns browser redirect routes with no mount prefix.
func (*Service) PermissionGroupRoutes ¶
PermissionGroupRoutes returns the auto-generated management routes implied by this Service's declared permission-group schema, plus the cross-persona GET /me/groups discovery route. Mirrors APIRoutes: prefix-neutral RouteSpecs in the RoutePermissionGroups group, language-wrapped and auth-required. The set is fully config-derived from svc.PermissionGroupSchema().GeneratedRoutes(); a capability a profile disables is simply absent (=> 404).
func (*Service) SMSAvailable ¶
SMSAvailable reports whether phone-based flows should be offered (a sender is configured and, if checked, found able to deliver).
func (*Service) SMSHealthy ¶
SMSHealthy reports the last CheckSMSHealth result (true until a check runs).
Source Files
¶
- admin_routes.go
- admin_signins.go
- applications.go
- audit.go
- auth_token_post.go
- auth_tokens.go
- availability.go
- browser_error.go
- buckets.go
- client_ip.go
- config.go
- confirm_errors.go
- contact_channel.go
- delegated_token.go
- delegation.go
- device_keys.go
- documents_route.go
- errors.go
- internal_errors.go
- jwks_get.go
- language.go
- logout_delete.go
- mount.go
- oidc_browser.go
- oidc_handler.go
- oidc_state_consume.go
- oidc_util.go
- passkeys.go
- password_login_post.go
- passwordless.go
- permission_group_create.go
- permission_group_operations.go
- permission_group_routes.go
- providers.go
- providers_get.go
- ratelimit.go
- ratelimit_defaults.go
- refresh_cookie.go
- register.go
- register_availability.go
- routes.go
- server.go
- service.go
- siws_cache.go
- solana_siws.go
- step_up.go
- user_2fa.go
- user_2fa_verify_post.go
- user_me_get.go
- user_password_post.go
- user_routes.go
- user_sessions.go
- util.go