config

package
v1.8.0 Latest Latest
Warning

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

Go to latest
Published: Jun 30, 2026 License: AGPL-3.0 Imports: 7 Imported by: 0

Documentation

Overview

Package config loads the identity service configuration from environment variables with the GATEWAY_ prefix.

This is the Go port of backend/api_gateway/config.py. It uses os.Getenv with typed defaults — no external config library needed. Sensitive values (secrets, encryption keys, client secrets) are never logged.

Index

Constants

View Source
const (
	CaptchaProviderTurnstile   = "turnstile"
	CaptchaProviderRecaptchaV3 = "recaptcha_v3"

	// DefaultCaptchaRecaptchaScoreThreshold is the reCAPTCHA v3 score below
	// which a response is rejected when no threshold is configured.
	DefaultCaptchaRecaptchaScoreThreshold = 0.5

	// DefaultAgeGateChildMaxAge is the conventional COPPA child boundary:
	// users 12 and under (i.e. under 13) are in the protected CHILD band.
	DefaultAgeGateChildMaxAge = 12
	// DefaultAgeGateAdultAge is the age at or above which a user is an adult;
	// below it (and above child-max) they are a TEEN minor.
	DefaultAgeGateAdultAge = 18
)

CAPTCHA provider names accepted in GATEWAY_CAPTCHA_PROVIDER. They mirror the captcha.Provider* constants; config validates against these without importing pkg/captcha (config has no dependencies on the service tree).

View Source
const (
	SMSProviderTwilio = "twilio"
	SMSProviderSNS    = "sns"
	SMSProviderAzure  = "azure"
)

SMS provider names for GATEWAY_SMS_PROVIDER. Firebase/Google are intentionally out of scope — those are client-SDK flows, not server REST APIs.

View Source
const DefaultPostgresConnTimeoutMs = 5000

DefaultPostgresConnTimeoutMs is the default per-acquire Postgres connection timeout (milliseconds) when GATEWAY_POSTGRES_CONN_TIMEOUT_MS is unset. It mirrors pgrepo.DefaultConnTimeout (5s) so the boundary default matches the driver's own zero-value fallback.

View Source
const DefaultProjectIDFallback = "default"

DefaultProjectIDFallback is the project id used when none is configured. It is the env-loader default for GATEWAY_DEFAULT_PROJECT_ID and the value app.New normalizes an empty DefaultProjectID to, so a directly-constructed Config (tests, embedding callers) never reaches the repo boundary with an empty project shard id. The single source of truth for this literal.

View Source
const MinSCIMBearerTokenLength = 32

MinSCIMBearerTokenLength is the floor for GATEWAY_SCIM_BEARER_TOKEN. The token is the sole credential guarding account lifecycle operations across a whole project, so it must carry enough entropy to resist guessing; 32 chars is the minimum a generated secret should ever be.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {

	// GRPCPort is the native gRPC listener port (reserved; currently unused).
	GRPCPort int
	// ConnectPort is the Connect-RPC HTTP listener port — the main public RPC surface.
	ConnectPort int
	// MetricsPort is the port serving the Prometheus /metrics endpoint.
	MetricsPort int

	// RepoDriver selects the persistence driver — which Repository / DB
	// implementation the binary wires up: "postgres" (default, the primary
	// datastore), "sqlite" (embedded single-node), or "memory" (in-process,
	// for local dev and tests). Driven by GATEWAY_REPO_DRIVER.
	RepoDriver string

	// DefaultTenantID is the storage-scope (shard) id the default project maps
	// onto — the data-plane tenant zero-config requests are pinned to; distinct
	// from DefaultProjectID. Driven by GATEWAY_DEFAULT_TENANT_ID.
	DefaultTenantID string

	// DefaultProjectID is the id of the control-plane Project the service
	// seeds on boot (postgres driver) and pins zero-config requests to. It
	// is a logical control-plane entity that MAPS ONTO the storage scope
	// DefaultTenantID — the two are distinct values and must not be
	// conflated. Driven by GATEWAY_DEFAULT_PROJECT_ID (default "default").
	// Only the postgres driver has a control plane; the memory driver ignores it.
	DefaultProjectID string

	// AdminAPISecret is the shared secret that authenticates the
	// control-plane admin RPCs (AdminCreateProject and friends), which a
	// PLATFORM operator uses to provision projects/tenants out-of-band.
	// These RPCs are NOT user-authenticated: a caller proves it is the
	// operator by presenting this exact value in the
	// middleware.AdminAPISecretHeader header, compared in constant time.
	//
	// Empty (the default) DISABLES the admin RPCs entirely — they return
	// CodeUnimplemented — so a deployer who never sets it cannot have them
	// reached. Driven by GATEWAY_ADMIN_API_SECRET. Only the postgres driver
	// has a control plane; the memory driver ignores it.
	//
	// TODO(redesign): the shared secret is the shipped mechanism. Future
	// work hardens this with mTLS client-certificate auth and an optional
	// internal-only listener port bound away from the public RPC surface.
	AdminAPISecret string

	// DefaultProjectAuthDomains is a comma-separated list of serving
	// hostnames seeded onto the default project at boot (postgres driver),
	// so the Host→project resolver maps these branded hostnames to the
	// default project. The FIRST entry is the primary auth-domain (used to
	// build branded links and cookie domains); the rest are additional
	// serving hosts. All are seeded VERIFIED — they are deployer-owned (a
	// customer-supplied custom domain goes through DNS verification
	// instead). Empty disables seeding. Driven by
	// GATEWAY_DEFAULT_PROJECT_AUTH_DOMAINS.
	DefaultProjectAuthDomains string

	// RequireVerifiedAuthDomain governs whether an UNVERIFIED custom
	// auth-domain marked is_primary may become a project's primary
	// auth-domain — the host that drives branded link URLs (magic links,
	// invitations) and cookie domains. When true (the safe default), the
	// primary-auth-domain selection requires verified_at_ms > 0, so only a
	// DNS-verified host can drive branded links, matching the verified-only
	// Host→project resolver and the proto contract on is_primary. identity
	// is a library/OSS server, so whether to trust an unverified is_primary
	// host is the deployer's policy: set this false to opt in. Driven by
	// GATEWAY_REQUIRE_VERIFIED_AUTH_DOMAIN, default true.
	RequireVerifiedAuthDomain bool

	// EmailServiceHost is the host of the internal email-sending gRPC service.
	EmailServiceHost string
	// EmailServicePort is the port of the internal email-sending gRPC service.
	EmailServicePort int

	// JWTSigner selects the JWT signing backend: "file" (default, RS256 keys
	// read from JWTKeysFile, reloaded on SIGHUP) or "kms_aws" (AWS KMS).
	JWTSigner string
	// JWTKeysFile is the path to the RS256 key document read by the "file"
	// signer; empty auto-generates a throwaway dev key at startup (local dev /
	// CI only — emits a warning log).
	JWTKeysFile string
	// JWTKMSKeys is a CSV of "kid=keyARN" entries for the "kms_aws" signer;
	// required when GATEWAY_JWT_SIGNER=kms_aws.
	JWTKMSKeys string
	// JWTKMSAWSRegion is the AWS region for the "kms_aws" signer.
	JWTKMSAWSRegion string
	// JWTExpirySeconds is the access-token (JWT) lifetime in seconds; capped at
	// 900 (RevocationModeTTLAccessTokenCap) under revocation mode "ttl".
	JWTExpirySeconds int

	// JWTAudience, when non-empty, is stamped on minted access tokens as the
	// "aud" claim and enforced by the verifier on every request.
	JWTAudience string
	// JWTRequireAudience, when true, also rejects tokens that carry no "aud"
	// claim; the false default lets a deploy roll out the mint-side change
	// first, wait for in-flight tokens to expire, then flip to required.
	JWTRequireAudience bool

	// RefreshExpirySeconds is the refresh-token lifetime in seconds (default 7 days).
	RefreshExpirySeconds int

	// RevocationMode selects how a DeleteRefreshTokensForUser propagates to
	// in-flight access tokens — "ttl" (default) or "session".
	//
	//   "ttl"     (default) — refresh tokens are deleted; already-minted
	//             access tokens stay valid until natural JWT expiry. Zero
	//             hot-path cost. Hard startup assertion:
	//             `JWTExpirySeconds <= 900` so a deployer cannot raise the
	//             access-token lifetime without explicitly switching modes.
	//   "session" — opt-in. Access tokens carry an `sid` claim referencing
	//             a Session row; the verification middleware reads that
	//             row (via an in-process cache, configurable below) and
	//             rejects the request when `revoked_at_ms != 0`.
	//             DeleteRefreshTokensForUser additionally triggers
	//             RevokeSessionsForUser so the existing replay-detection
	//             code path also kills the access tokens.
	//
	// See docs/IDENTITY.md decision log §6 for the two-mode contract.
	RevocationMode RevocationMode

	// SessionCacheTTLSeconds bounds how long a session-state read from the
	// in-process cache may serve "active" before being re-read from the
	// repository. 0 = strict mode: every authenticated request reads the
	// row. Effective only when RevocationMode == RevocationModeSession.
	SessionCacheTTLSeconds int

	// ProjectResolutionCacheTTLSeconds bounds how long a per-request
	// project resolution (credential-key→project and Host→project) may be
	// served from the in-process cache before being re-read from the
	// control-plane store. Project resolution runs ahead of the rate
	// limiter and on every CORS preflight, so caching it removes 2-3
	// uncached DB queries from the hot path. Kept short so a suspended
	// project or revoked credential is never served stale beyond the TTL.
	// 0 = disabled: every request resolves against the store.
	ProjectResolutionCacheTTLSeconds int

	// ProjectResolutionCacheMaxEntries bounds the number of distinct
	// resolution keys (credential ids + hostnames) held in the cache,
	// evicting the least-recently-used entry past the bound so the cache
	// cannot grow unbounded under hostile or high-cardinality traffic.
	ProjectResolutionCacheMaxEntries int

	// GoogleClientID is the Google OAuth client ID.
	GoogleClientID string
	// GoogleClientSecret is the Google OAuth client secret.
	GoogleClientSecret string
	// GoogleAuthorizationURL overrides the Google OAuth authorization endpoint; empty = the real Google endpoint. For self-hosted proxies and end-to-end tests against a mock OIDC provider.
	GoogleAuthorizationURL string
	// GoogleTokenURL overrides the Google OAuth token endpoint; empty = the real Google endpoint.
	GoogleTokenURL string
	// GoogleJWKSURL overrides the Google OIDC JWKS (public keys) endpoint; empty = the real Google endpoint.
	GoogleJWKSURL string
	// GoogleDiscoveryURL overrides the Google OIDC discovery document URL; empty = the real Google endpoint. When set, the authorization / token / JWKS / userinfo endpoints are resolved from this document.
	GoogleDiscoveryURL string
	// GoogleUserinfoURL overrides the Google OIDC userinfo endpoint; empty = the real Google endpoint.
	GoogleUserinfoURL string
	// GoogleIssuer overrides the expected Google OIDC token issuer; empty = the real Google issuer.
	GoogleIssuer string
	// MicrosoftClientID is the Microsoft / Entra ID OAuth client ID.
	MicrosoftClientID string
	// MicrosoftClientSecret is the Microsoft / Entra ID OAuth client secret.
	MicrosoftClientSecret string
	// MicrosoftTenantID is the Microsoft directory (tenant) id, or "common" for multi-tenant.
	MicrosoftTenantID string
	// GitHubClientID is the GitHub OAuth client ID.
	GitHubClientID string
	// GitHubClientSecret is the GitHub OAuth client secret.
	GitHubClientSecret string
	// AppleClientID is the Apple Service ID used as the OAuth client ID.
	AppleClientID string
	// AppleTeamID is the Apple Developer Team ID.
	AppleTeamID string
	// AppleKeyID is the Apple private-key (Key) ID.
	AppleKeyID string
	// ApplePrivateKey is the Apple Sign in private key (PEM or base64).
	ApplePrivateKey string

	// OIDCEnabled turns on the generic, config-driven OIDC provider so an
	// operator can register an arbitrary standards-compliant IdP (Okta,
	// Auth0, Keycloak, a self-hosted issuer) without a code release.
	OIDCEnabled bool
	// OIDCProviderKey is the registry key the generic OIDC provider is
	// registered under and reported as Identity.Provider (e.g. "okta").
	OIDCProviderKey string
	// OIDCIssuer is the generic OIDC provider's issuer URL; the discovery,
	// authorization, token, JWKS, and userinfo endpoints are resolved from
	// <issuer>/.well-known/openid-configuration unless OIDCDiscoveryURL is set.
	OIDCIssuer string
	// OIDCDiscoveryURL overrides the generic OIDC provider's discovery
	// document URL; empty = derived from OIDCIssuer.
	OIDCDiscoveryURL string
	// OIDCClientID is the generic OIDC provider's OAuth client ID.
	OIDCClientID string
	// OIDCClientSecret is the generic OIDC provider's OAuth client secret.
	OIDCClientSecret string
	// OIDCScopes overrides the space-separated OAuth scopes requested from
	// the generic OIDC provider; empty = "openid email profile" ("openid"
	// is always ensured).
	OIDCScopes string

	// OAuthAllowedReturnURLs is the comma-separated allowlist of app URLs
	// the hosted OAuth flow may redirect back to (the `return_to` param of
	// GET /oauth/start/{provider}). Each entry is an exact origin or a path
	// prefix. A return_to must match the configured origin and, for path
	// entries, the configured path or one of its descendants. Validation is
	// fail-closed: a return_to that matches no entry is rejected with 400.
	//
	// Empty disables the hosted flow entirely — GET /oauth/start and
	// GET/POST /oauth/callback return 404. The headless BeginOAuthLogin / OAuthLogin
	// RPCs are unaffected. Driven by GATEWAY_OAUTH_ALLOWED_RETURN_URLS.
	OAuthAllowedReturnURLs string

	// NativeOAuthEnabled is the kill-switch for NativeOAuthLogin (verifying
	// Google/Apple ID tokens from mobile SDKs). It defaults true when at least
	// one provider's native audiences are configured, false otherwise; set it
	// explicitly to false to disable the RPC even with audiences present.
	NativeOAuthEnabled bool
	// NativeOAuthGoogleAudiences is the comma-separated allow-list of accepted
	// Google ID-token `aud` values for native login — the web client id plus
	// every per-platform (iOS/Android) OAuth client id. Empty disables Google.
	NativeOAuthGoogleAudiences string
	// NativeOAuthAppleAudiences is the comma-separated allow-list of accepted
	// Apple ID-token `aud` values for native login — the Services ID plus every
	// native bundle id. Empty disables Apple.
	NativeOAuthAppleAudiences string
	// NativeOAuthProductProjects maps a native client's product selector to an
	// identity project id, as comma-separated product=projectID pairs (e.g.
	// "easyloops=proj_abc,tortoise=proj_def"). A product not listed falls back
	// to being treated as a project id directly. Token issuance is scoped to
	// the resolved project.
	NativeOAuthProductProjects string

	// IDVProvider selects the IDV backend — "azure", "stub", or "" (disabled;
	// the IDV RPCs return CodeUnimplemented).
	IDVProvider string
	// IDVAzureEndpoint is the Azure Cognitive Services Face endpoint URL for
	// the azure provider (e.g. https://NAME.cognitiveservices.azure.com).
	IDVAzureEndpoint string
	// IDVAzureKey is the Azure Cognitive Services API key (azure provider).
	IDVAzureKey string
	// IDVAzureSessionTTLSec is the IDV session-token lifetime in seconds.
	IDVAzureSessionTTLSec int
	// When true, PasswordLogin / OAuthLogin reject users without an
	// approved identity verification. The default is false (verification
	// is offered but not required) to match the existing email-verified
	// pattern. Tenants that need stricter onboarding flip this on.
	IDVRequired bool

	// CaptchaEnabled is the global on/off for CAPTCHA on unauthenticated endpoints.
	CaptchaEnabled bool
	// CaptchaProvider selects the pkg/captcha implementation — "turnstile" or
	// "recaptcha_v3"; the matching secret must be set.
	CaptchaProvider string
	// CaptchaTurnstileSecret is the Cloudflare Turnstile secret key (provider "turnstile").
	CaptchaTurnstileSecret string
	// CaptchaRecaptchaSecret is the reCAPTCHA v3 secret key (provider "recaptcha_v3").
	CaptchaRecaptchaSecret string
	// CaptchaRecaptchaScoreThreshold is the reCAPTCHA v3 score below which a
	// response is rejected; must be in [0,1].
	CaptchaRecaptchaScoreThreshold float64
	// CaptchaEnforcePasswordSignup gates CAPTCHA on the PasswordSignup endpoint.
	CaptchaEnforcePasswordSignup bool
	// CaptchaEnforcePasswordLogin gates CAPTCHA on the PasswordLogin endpoint.
	CaptchaEnforcePasswordLogin bool
	// CaptchaEnforcePasswordReset gates CAPTCHA on the RequestPasswordReset endpoint.
	CaptchaEnforcePasswordReset bool
	// CaptchaEnforceEmailLoginCode gates CAPTCHA on the RequestEmailLoginCode endpoint.
	CaptchaEnforceEmailLoginCode bool
	// CaptchaEnforceMagicLink gates CAPTCHA on the RequestMagicLink endpoint.
	CaptchaEnforceMagicLink bool

	// AgeGateEnabled is the global on/off for age-gating.
	AgeGateEnabled bool
	// AgeGateChildMaxAge is the inclusive upper age of the protected CHILD band
	// (default 12 → under-13).
	AgeGateChildMaxAge int
	// AgeGateAdultAge is the age at or above which a user is an adult; between it
	// and AgeGateChildMaxAge a user is a TEEN minor (default 18).
	AgeGateAdultAge int
	// AgeGateRequireDOB rejects a signup that omits a date of birth
	// (INVALID_ARGUMENT) instead of treating it as adult.
	AgeGateRequireDOB bool

	// MinorDataMinimization gates COPPA-style data-minimization for accounts
	// the age gate classifies as AGE_BAND_CHILD. When true (and the age gate
	// is enabled), the server refuses to collect or persist non-essential PII
	// from a child: RequestPhoneVerification and BeginIdentityVerification are
	// rejected with ErrMinorDataMinimized, and a recovery_email / avatar_url
	// supplied for a child at signup or profile-update is dropped rather than
	// stored. Default false preserves today's behavior — adults, teens, and
	// accounts with an unknown age band are never affected.
	MinorDataMinimization bool // GATEWAY_MINOR_DATA_MINIMIZATION (default false)

	// SCIMEnabled gates the inbound SCIM 2.0 routes (default false).
	SCIMEnabled bool // GATEWAY_SCIM_ENABLED (default false)
	// SCIMBearerToken is the shared secret an external IdP presents in the
	// Authorization: Bearer header on every SCIM request (required when SCIMEnabled).
	SCIMBearerToken string // GATEWAY_SCIM_BEARER_TOKEN (required when SCIMEnabled)
	// SCIMProjectID is the single project whose users the SCIM endpoint
	// provisions; every SCIM operation is scoped to this project (required
	// when SCIMEnabled).
	SCIMProjectID string // GATEWAY_SCIM_PROJECT_ID (required when SCIMEnabled)

	// PasswordSignupEnabled gates self-serve PasswordSignup; set false to
	// disable it (admin-driven invitations still work).
	PasswordSignupEnabled bool
	// PasswordResetEnabled gates RequestPasswordReset; when false the RPC stays
	// enumeration-safe but is a no-op (admin resets still work).
	PasswordResetEnabled bool
	// PasswordResetExpirySeconds is the recovery-email reset-link lifetime in seconds.
	PasswordResetExpirySeconds int

	// PasswordlessSignupEnabled gates auto-create on a passwordless verify for
	// an unknown email: when false the unknown email gets the same
	// anti-enumeration decoy as a known one, so the endpoint never reveals which
	// addresses exist. Mirrors PasswordSignupEnabled.
	PasswordlessSignupEnabled bool
	// PasswordlessCodeTTLSeconds is the one-time-code lifetime in seconds (code length is fixed at 6 digits).
	PasswordlessCodeTTLSeconds int
	// PasswordlessCodeMaxAttempts is the max verify attempts per OTP before it
	// is invalidated (brute-force cap).
	PasswordlessCodeMaxAttempts int
	// PasswordlessMagicLinkTTLSeconds is the magic-link token lifetime in seconds.
	PasswordlessMagicLinkTTLSeconds int

	// SMSEnabled turns on phone (SMS OTP) verification.
	SMSEnabled bool
	// SMSProvider selects the SMS backend: "twilio", "sns", or "azure".
	SMSProvider string

	// SMSTwilioAccountSID is the Twilio Account SID (provider "twilio").
	SMSTwilioAccountSID string
	// SMSTwilioAuthToken is the Twilio auth token (provider "twilio").
	SMSTwilioAuthToken string
	// SMSTwilioFrom is the Twilio sender phone number (provider "twilio").
	SMSTwilioFrom string

	// SMSAWSRegion is the AWS region for the SNS sender (provider "sns").
	SMSAWSRegion string
	// SMSAWSAccessKeyID is the AWS access key id for SNS (provider "sns").
	SMSAWSAccessKeyID string
	// SMSAWSSecretAccessKey is the AWS secret access key for SNS (provider "sns").
	SMSAWSSecretAccessKey string
	// SMSAWSSenderID is the optional AWS SNS sender id (provider "sns").
	SMSAWSSenderID string

	// SMSAzureConnectionString is the Azure Communication Services connection
	// string (provider "azure").
	SMSAzureConnectionString string
	// SMSAzureFrom is the Azure Communication Services sender number (provider "azure").
	SMSAzureFrom string

	// PhoneCodeTTLSeconds is the phone-verification OTP lifetime in seconds.
	PhoneCodeTTLSeconds int
	// PhoneCodeMaxAttempts is the max wrong-code guesses before the OTP is invalidated.
	PhoneCodeMaxAttempts int
	// PhoneCodeCooldownSeconds is the per-request cooldown (seconds) between phone-verification sends.
	PhoneCodeCooldownSeconds int

	// SAMLIDPEnabled turns on the SAML 2.0 IdP surface (default false).
	SAMLIDPEnabled bool
	// SAMLEntityID is the IdP entityID published in metadata (metadata URL).
	SAMLEntityID string
	// SAMLSSOURL is the HTTP-POST/Redirect single sign-on endpoint.
	SAMLSSOURL string
	// SAMLSLOURL is the optional single-logout endpoint.
	SAMLSLOURL string
	// SAMLSigningKey is the PEM-encoded RSA private key used to sign assertions.
	SAMLSigningKey string
	// SAMLSigningCert is the PEM-encoded X.509 certificate published in metadata.
	SAMLSigningCert string

	// TOTPEncryptionKey is the base64-encoded 32-byte AES-256 key that encrypts
	// TOTP secrets at rest. Throwaway dev key if unset; required in prod (a
	// deterministic dev fallback must never be used in production).
	TOTPEncryptionKey string
	// TOTPIssuer is the issuer name shown in users' authenticator apps.
	TOTPIssuer string

	// Pepper used as the HMAC-SHA-256 key for recovery-code hashing.
	// Base64-encoded; must decode to >= 32 bytes. Required whenever
	// TOTPEncryptionKey is set (i.e. any non-dev deployment). The
	// pepper turns a stolen DB into a brute-force-resistant artifact:
	// without it, an attacker cannot precompute or enumerate hashes.
	TOTPRecoveryPepper string

	// LoginChallengeExpirySeconds is the window (seconds) after a password
	// success in which the user must complete the 2FA challenge.
	LoginChallengeExpirySeconds int

	// PasskeyRPID is the WebAuthn relying-party ID — must match the registrable
	// domain (e.g. example.com).
	PasskeyRPID string
	// PasskeyRPName is the human-readable WebAuthn relying-party name.
	PasskeyRPName string
	// PasskeyOrigin is the allowed origin for passkey ceremonies (scheme + host + port).
	PasskeyOrigin string
	// PasskeyChallengeExpirySeconds is the lifetime in seconds of registration / login challenges.
	PasskeyChallengeExpirySeconds int
	// PasskeySignupEnabled gates passkey-first signup (the unauthenticated
	// BeginPasskeySignup / CompletePasskeySignup pair that creates a brand-new
	// account from a passkey); set false to disable it while leaving
	// authenticated add-a-passkey registration untouched.
	PasskeySignupEnabled bool

	// QRLoginBaseURL is the base URL embedded in the cross-device login QR code.
	QRLoginBaseURL string
	// QRLoginExpirySeconds is the QR login-session lifetime in seconds.
	QRLoginExpirySeconds int

	// LoginMaxFailedAttempts is the consecutive failed-login count that triggers a lockout.
	LoginMaxFailedAttempts int
	// LoginLockoutSeconds is how long (seconds) an account stays locked after the threshold is hit.
	LoginLockoutSeconds int

	// DefaultEmailDomain is the default email domain applied to new accounts.
	DefaultEmailDomain string

	// PublicEmailDomains extends the built-in set of consumer/public email
	// providers (gmail, outlook, yahoo, …) used by IsPublicEmailDomain. A
	// verified email under a public domain does NOT imply company
	// affiliation, so a tenant is never auto-formed from one. Comma-
	// separated; entries are punycode-canonicalised. Driven by
	// GATEWAY_PUBLIC_EMAIL_DOMAINS (default empty — the built-in set
	// already covers the major global providers).
	PublicEmailDomains string

	// AllowedOrigins is the comma-separated list of CORS allowed origins.
	AllowedOrigins string

	// CookieDomain is the Domain attribute set on auth cookies (empty = host-only).
	CookieDomain string
	// CookieSecure sets the Secure attribute on auth cookies; enable in prod (HTTPS-only).
	CookieSecure bool
	// CookieSameSite is the SameSite attribute on auth cookies — "Lax", "Strict", or "None".
	CookieSameSite string

	// AuthAllowLocal enables local username/password auth; set false to require
	// OAuth (intended for development).
	AuthAllowLocal bool

	// AuthRequireVerifiedEmail blocks authentication until the account's email
	// is verified. Default ON. Closes an account pre-hijacking vector: an
	// attacker who plants a password account for an unverified address cannot
	// use it, and a session is never issued for an unverified account.
	AuthRequireVerifiedEmail bool

	// SMTPHost is the SMTP server hostname.
	SMTPHost string
	// SMTPPort is the SMTP server port.
	SMTPPort int
	// SMTPUser is the SMTP auth username.
	SMTPUser string
	// SMTPPass is the SMTP auth password.
	SMTPPass string
	// SMTPFrom is the envelope/From address for outbound mail.
	SMTPFrom string
	// SMTPTLS enables STARTTLS / TLS for the SMTP connection.
	SMTPTLS bool

	// SMTPProviders is a JSON array of SMTP configs ([]email.SMTPConfig) used as
	// a failover chain in order; overrides the single-provider SMTP_* vars.
	SMTPProviders string

	// EmailBrandProductName is the product name shown in email bodies.
	EmailBrandProductName string
	// EmailBrandFrom is the From address for transactional mail (falls back to SMTPFrom).
	EmailBrandFrom string
	// EmailBrandFromName is the From display name (rendered as "Name" <addr>).
	EmailBrandFromName string
	// EmailBrandLogoURL is the absolute https logo URL shown in HTML email.
	EmailBrandLogoURL string
	// EmailBrandPrimaryColor is the CSS colour used to tint branded HTML email.
	EmailBrandPrimaryColor string
	// EmailBrandSupportEmail is the support address shown in footers and set as the Reply-To header.
	EmailBrandSupportEmail string
	// EmailListUnsubscribe is the List-Unsubscribe header value applied to
	// configured mail (e.g. "<mailto:unsubscribe@example.com>"). Empty omits the
	// header; auth/transactional mail stays deliverable either way.
	EmailListUnsubscribe string

	// Public app URLs used in email links.
	AppBaseURL string // GATEWAY_APP_BASE_URL — e.g. "https://app.example.com"

	// How long an email-verification or password-reset token is valid for.
	EmailTokenExpirySeconds int // GATEWAY_EMAIL_TOKEN_EXPIRY_SECONDS (default 86400)

	// How long a tenant-membership invitation is valid for before it must be
	// reissued. Longer than an email token because joining a team is a less
	// time-sensitive action than a password reset.
	TenantInvitationExpirySeconds int // GATEWAY_TENANT_INVITATION_EXPIRY_SECONDS (default 604800 = 7 days)

	// Per-recipient cooldown between transactional email sends. Defeats
	// inbox-bombing via repeated unauthenticated RequestPasswordReset /
	// SendEmailVerification calls. In-memory per replica.
	EmailSendCooldownSeconds int // GATEWAY_EMAIL_SEND_COOLDOWN_SECONDS (default 60)

	// Per-email cooldown on PasswordSignup. Throttled signups return
	// the same anti-enumeration decoy as a duplicate-email signup so the
	// endpoint cannot be used to probe for which addresses are
	// rate-limited (which would itself reveal recent attempts).
	// Complements the per-IP rate limit at the middleware layer.
	SignupEmailCooldownSeconds int // GATEWAY_SIGNUP_EMAIL_COOLDOWN_SECONDS (default 60)

	// Audit log queue depth for the async flusher. Drops happen if the
	// auth hot path produces events faster than the datastore can absorb them.
	// Surface via audit.Logger.DroppedCount() on a metric.
	AuditQueueSize int // GATEWAY_AUDIT_QUEUE_SIZE (default 4096)

	// Maximum HTTP request body size in bytes, enforced via
	// http.MaxBytesHandler so a slow-POST / oversize-payload attacker
	// can't exhaust memory. Default 1 MiB — auth RPC bodies are tiny.
	HTTPMaxBodyBytes int64 // GATEWAY_HTTP_MAX_BODY_BYTES (default 1048576)

	// Trusted proxies: comma-separated list of CIDRs whose
	// X-Forwarded-For headers the service will honour. Anything outside
	// these ranges is treated as an untrusted client and its forwarded
	// headers are ignored — TCP peer IP is used instead.
	TrustedProxies string // GATEWAY_TRUSTED_PROXIES (default "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1/32,::1/128")

	// RateLimitWindowSeconds is the sliding window length (seconds) for the per-IP limiter.
	RateLimitWindowSeconds int
	// RateLimitSignupPerIP is the per-IP request cap per window on PasswordSignup.
	RateLimitSignupPerIP int
	// RateLimitLoginPerIP is the per-IP request cap per window on the login endpoints.
	RateLimitLoginPerIP int
	// RateLimitResetPerIP is the per-IP request cap per window on RequestPasswordReset.
	RateLimitResetPerIP int
	// RateLimitVerifyPerIP is the per-IP request cap per window on the verification endpoints.
	RateLimitVerifyPerIP int
	// RateLimitPasswordlessPerIP is the per-IP cap per window on
	// RequestEmailLoginCode + RequestMagicLink.
	RateLimitPasswordlessPerIP int
	// RateLimitPhonePerIP is the per-IP cap per window on RequestPhoneVerification.
	RateLimitPhonePerIP int
	// RateLimitBootstrapPerIP is the per-IP cap per window on CreateFirstPlatformAdmin.
	RateLimitBootstrapPerIP int

	// PostgresDSN is the Postgres connection string (e.g.
	// "postgres://user:pass@host:5432/identity?sslmode=disable"); required when
	// RepoDriver is "postgres" (the default).
	PostgresDSN string
	// PostgresMaxConns is the connection-pool size.
	PostgresMaxConns int
	// PostgresConnTimeoutMs is the per-acquire connection timeout in
	// milliseconds, applied when checking a connection out of the pgx pool
	// (pgxpool ConnectTimeout). It bounds how long a connect/acquire may block,
	// not total query time — callers still pass a context deadline. Default 5000.
	PostgresConnTimeoutMs int
	// PostgresAutoMigrate runs pending migrations on connect; leave false in
	// production and run migrations out-of-band (a rolling deploy can race replicas).
	PostgresAutoMigrate bool

	// SQLitePath is the SQLite database file path, or ":memory:" for an
	// ephemeral in-process database; required when RepoDriver is "sqlite".
	SQLitePath string
	// SQLiteMaxConns is the connection-pool size for a file database.
	SQLiteMaxConns int

	// OTelEnabled turns on OTLP trace export.
	OTelEnabled bool
	// OTelExporterEndpoint is the OTLP collector host:port; required when OTelEnabled.
	OTelExporterEndpoint string
	// OTelExporterProtocol is the OTLP transport — "grpc" or "http".
	OTelExporterProtocol string
	// OTelSampleRatio is the trace head-sampling ratio in [0.0, 1.0].
	OTelSampleRatio float64
	// OTelDeploymentEnv sets the deployment.environment.name resource attribute.
	OTelDeploymentEnv string
	// OTelServiceVersion overrides the build version baked into the binary on emitted traces.
	OTelServiceVersion string

	// SweeperIntervalSeconds is the sweep tick interval in seconds; 0 disables
	// sweeping entirely (for tests or deployers who run their own GC).
	SweeperIntervalSeconds int
	// SweeperBatchSize is the per-table, per-tick deletion cap.
	SweeperBatchSize int
	// SweeperGraceSeconds is extra grace past expires_at before a row is
	// eligible for deletion (covers flows that just consumed the token).
	SweeperGraceSeconds int

	// WebhooksEnabled is the master switch for outbound user-lifecycle
	// eventing; when false the service emits to a no-op publisher and runs no
	// delivery worker. Driven by GATEWAY_WEBHOOKS_ENABLED (default false).
	WebhooksEnabled bool
	// WebhooksMaxAttempts is the per-delivery retry budget before a webhook is
	// abandoned and surfaced via the structured logger. Driven by
	// GATEWAY_WEBHOOKS_MAX_ATTEMPTS (default 6).
	WebhooksMaxAttempts int
	// WebhooksBackoffBaseSeconds is the first-retry delay; it doubles per
	// attempt up to the ceiling. Driven by GATEWAY_WEBHOOKS_BACKOFF_BASE_SECONDS
	// (default 2).
	WebhooksBackoffBaseSeconds int
	// WebhooksBackoffMaxSeconds is the exponential-backoff ceiling. Driven by
	// GATEWAY_WEBHOOKS_BACKOFF_MAX_SECONDS (default 300).
	WebhooksBackoffMaxSeconds int
	// WebhooksWorkerIntervalSeconds is the outbox drain tick interval. Driven
	// by GATEWAY_WEBHOOKS_WORKER_INTERVAL_SECONDS (default 1).
	WebhooksWorkerIntervalSeconds int
	// WebhooksBatchSize is the number of due deliveries claimed per tick.
	// Driven by GATEWAY_WEBHOOKS_BATCH_SIZE (default 50).
	WebhooksBatchSize int
}

Config holds all identity service configuration.

func Load

func Load() *Config

Load reads configuration from environment variables with GATEWAY_ prefix, falling back to sensible defaults for local development.

func (*Config) DefaultPrimaryAuthDomain added in v0.17.0

func (c *Config) DefaultPrimaryAuthDomain() string

DefaultPrimaryAuthDomain returns the default project's primary serving hostname — the first entry of DefaultProjectAuthDomainList — or "" when none is configured.

func (*Config) DefaultProjectAuthDomainList added in v0.17.0

func (c *Config) DefaultProjectAuthDomainList() []string

DefaultProjectAuthDomainList returns the configured default-project auth domains, lower-cased and de-duplicated, in order — the first entry is the primary. Blank entries are dropped; an empty config yields nil.

func (*Config) EmailServiceAddress

func (c *Config) EmailServiceAddress() string

EmailServiceAddress returns the host:port for the email service.

func (*Config) IsPublicEmailDomain added in v0.17.0

func (c *Config) IsPublicEmailDomain(emailOrDomain string) bool

IsPublicEmailDomain reports whether emailOrDomain belongs to a public / consumer email provider — a domain a Tenant must never be auto-formed from. The input may be a full address (the part after the last '@' is used) or a bare domain; it is lower-cased, trimmed, stripped of a trailing FQDN dot, and punycode-canonicalised before the built-in set and GATEWAY_PUBLIC_EMAIL_DOMAINS are consulted, so an IDN homograph cannot slip past the check.

func (*Config) JWTExpiry

func (c *Config) JWTExpiry() time.Duration

JWTExpiry returns the JWT expiry as a time.Duration.

func (*Config) NativeOAuthAppleAudienceList added in v1.8.0

func (c *Config) NativeOAuthAppleAudienceList() []string

NativeOAuthAppleAudienceList returns the configured Apple native audiences, trimmed, blanks dropped, in order. An empty config yields nil.

func (*Config) NativeOAuthGoogleAudienceList added in v1.8.0

func (c *Config) NativeOAuthGoogleAudienceList() []string

NativeOAuthGoogleAudienceList returns the configured Google native audiences, trimmed, blanks dropped, in order. An empty config yields nil.

func (*Config) NativeOAuthProductProjectMap added in v1.8.0

func (c *Config) NativeOAuthProductProjectMap() map[string]string

NativeOAuthProductProjectMap parses the product=projectID pairs into a map keyed by the lower-cased product selector. Malformed or blank entries are dropped; an empty config yields an empty (non-nil) map.

func (*Config) OIDCScopeList added in v1.7.6

func (c *Config) OIDCScopeList() []string

OIDCScopeList returns the configured generic-OIDC scopes split on whitespace, dropping blanks. An empty config yields nil, which lets the provider fall back to its default scope set ("openid email profile").

func (*Config) PasswordResetExpiry

func (c *Config) PasswordResetExpiry() time.Duration

PasswordResetExpiry returns the password reset expiry as a time.Duration.

func (*Config) ProjectResolutionCacheTTL added in v1.2.0

func (c *Config) ProjectResolutionCacheTTL() time.Duration

ProjectResolutionCacheTTL returns the configured project-resolution cache TTL as a time.Duration. 0 means disabled (resolve on every request).

func (*Config) RefreshExpiry

func (c *Config) RefreshExpiry() time.Duration

RefreshExpiry returns the refresh token expiry as a time.Duration.

func (*Config) SessionCacheTTL added in v0.8.0

func (c *Config) SessionCacheTTL() time.Duration

SessionCacheTTL returns the configured cache TTL as a time.Duration. 0 means strict mode (read on every request).

func (*Config) Validate added in v0.8.0

func (c *Config) Validate() error

Validate enforces invariants that are too complex to express as per-field defaults: most importantly the `mode=ttl` access-token TTL ceiling. The binary calls this at startup; tests pin their configs through the same path so misuse surfaces immediately rather than as a silent revocation-window gap.

Why a method rather than running inside Load(): tests build *Config values directly (without going through Load) and a silent failure mode there would re-introduce the bug this function prevents. Callers that synthesise a Config must invoke Validate before handing it to app.New.

type RevocationMode added in v0.8.0

type RevocationMode string

RevocationMode names the two refresh-token revocation models the service supports. See the Config.RevocationMode comment for the semantics; the two-mode contract is in docs/IDENTITY.md decision log §6.

const (
	// RevocationModeTTL keeps the existing zero-cost hot path.
	// DeleteRefreshTokensForUser deletes refresh tokens; in-flight
	// access tokens stay valid until natural JWT expiry. The default.
	RevocationModeTTL RevocationMode = "ttl"

	// RevocationModeSession mints access tokens with an `sid` claim
	// referencing a Session row. The verification middleware reads
	// that row (via an in-process cache) and rejects the request when
	// the session is revoked.
	RevocationModeSession RevocationMode = "session"

	// RevocationModeTTLAccessTokenCap is the maximum access-token TTL
	// (seconds) compatible with the `ttl` revocation model. A deployer
	// who needs a longer-lived access token must switch to
	// `RevocationModeSession`, where cache TTL bounds the revocation
	// latency.
	RevocationModeTTLAccessTokenCap = 900
)

Jump to

Keyboard shortcuts

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