v0.12.0¶
Release Date: 2026-10-06
SystemAuth becomes a deployable, central login service for every SystemForge application: GitHub/Google social login, a hardened session and token lifecycle, OpenID Connect ID tokens and UserInfo, and a reusable relying-party package so applications federate to SystemAuth instead of each implementing social login. Sessions and login state are durable in PostgreSQL, so restarts and multi-instance deployments keep users signed in. Design of record: ADR-002 and INIT-SYSTEMFORGE-005.
Highlights¶
- Social login on SystemAuth —
/login,/login/{provider}and callback routes for GitHub and Google, principal upsert keyed by provider subject (verified-email linking only), a hardened__Host-sf_loginsession, state CSRF + PKCE, and an allowlisted post-login redirect. - Session and token lifecycle — refresh-token rotation with reuse detection and absolute expiry, real logout with token revocation, and a minimal consent page.
- OpenID Connect — SystemAuth now issues ID tokens, optional RS256 JWT access tokens (
features.enable_jwt_access_tokens), and serves/oauth/userinfo. identity/relyingparty— OIDC client,sub→sf_principal_idprincipal linking, a/bff/*cookie-session surface for browser apps, and bearer middleware for JWT access tokens and API keys.- Durable stores — Ent-backed SystemAuth login sessions, login state and consent grants;
identity/relyingparty/pgstorePostgreSQL session and login-state stores with AES-256-GCM encryption at rest and key rotation (pgstore.NewStores,pgstore.KeysFromEnv). - Production-deployable
systemauthbinary — PostgreSQL via pgx, signing key from a secret with a stable key ID,/healthzand/readyz, and a production mode that refuses unsafe configuration.
Breaking Changes¶
session/oauth removed¶
The unused duplicate social-login package is gone; identity/oauthclient is the single sanctioned package and gains everything worth keeping from it.
// Old
import "github.com/plexusone/systemforge/session/oauth"
// New
import "github.com/plexusone/systemforge/identity/oauthclient"
// oauthclient.NewGitHubConnector / NewGoogleConnector, StateStore, User.EmailVerified
systemauth production mode¶
Without --dev, cmd/systemauth now refuses to start unless it has a signing key (keys.private_key_file, keys.private_key_pem, --signing-key-file, or SYSTEMAUTH_SIGNING_KEY), an https issuer, and a persistent database, and it rejects social_login.insecure_cookies. Local setups pass --dev. --listen is kept as a deprecated alias of --addr.
Refresh-token reuse revokes the token family¶
Every refresh issues a new refresh token and retires the old one. Presenting a rotated refresh token returns invalid_grant and revokes the whole family; clients must store the newest refresh token after each refresh. Absolute lifetime is tokens.refresh_token_absolute_lifetime (default 720h).
Header-trusting session provider bypassed when social login is enabled¶
With social_login configured, the X-User-ID-trusting DefaultSessionProvider can no longer bypass login; the __Host-sf_login cookie session is consulted first. Custom providers set with WithSessionProvider still apply after the cookie.
Schema additions¶
New tables sf_external_identities, sf_login_sessions, sf_login_states, sf_consent_grants; OAuth codes and tokens gain a subject column and the auth-code user_id becomes nullable (social-login principals have no legacy user row). Run systemauth migrate (or --migrate). Relying-party applications create sf_rp_sessions / sf_rp_login_states in their own database with pgstore.EnsureSchema.
New Features¶
identity/oauthclient: providerConnectorwith overridable endpoints,User.EmailVerified(Googleemail_verified; GitHub/user/emails), pluggable single-useStateStorewith a race-freeMemoryStateStore.identity/systemauth:PrincipalDirectory(Ent and memory) with external-login account linking;social_loginconfiguration with${ENV}expansion;CurrentLoginSession; optionsWithPrincipalDirectory,WithLoginSessionStore,WithLoginStateStore,WithSocialConnector,WithConsentStore,WithReadinessCheck;idp_hintauthorization parameter.identity/relyingparty:Client(Discover,AuthCodeURL,Exchange,Refresh,Revoke,VerifyIDToken,VerifyAccessToken,UserInfo),ResolvePrincipal,MembershipSource(claims-based memberships),SessionStorewithDeleteBySubject/DeleteBySIDfor future back-channel logout,NewBFF,BearerMiddleware,PrincipalFromContext.identity/relyingparty/pgstore:NewSessionStore,NewLoginStateStore,NewStores,Keys,KeysFromEnv,EnsureSchema,DecodeKey,DeleteExpired.- Shared store conformance suites (
identity/systemauth/storetest,identity/relyingparty/storetest) run against memory and PostgreSQL implementations.
Bug Fixes¶
- SystemAuth did not issue ID tokens: the Fosite OpenID Connect handlers were not registered, and discovery did not serve the advertised UserInfo endpoint.
- Ent storage could not complete a real login: the login session was not persisted, access tokens were inserted twice, PKCE marked the code used before the exchange finished, and auth codes required a legacy user row.
- A reused refresh token could crash the server (nil request after the inactive-token error).
cmd/systemauthregistered no PostgreSQL driver forent.Open("postgres").- Configured clients that already existed in a persistent database failed startup with a unique-constraint error; they are now updated.
DefaultSessionProviderconcatenatedreturnURLinto redirects without query escaping.- The relying-party seal nonce is generated in a dedicated buffer (sealed format unchanged).
Documentation¶
- New guides: Social Login, Relying Parties, Deployment, Production Stores; updated SystemAuth overview and OAuth client.
- README: social login and relying-party features, package layout, and the new tables.
Upgrade Notes¶
- Replace any
session/oauthimport withidentity/oauthclient. - Run
systemauth migrateagainst existing SystemAuth databases. - For production, provide a signing key and key ID, an
httpsissuer, and PostgreSQL; use--devonly locally. - Applications federating to SystemAuth: register an OIDC client (PKCE, redirect
https://<app>/bff/auth/callback), mountrelyingparty.NewBFF, and usepgstore.NewStoreswith a 32-byte session key for durable sessions.
What's Next¶
INIT-SYSTEMFORGE-005 Phase 3: consumer convergence onto the relying-party contract and a CI gate against direct GitHub/Google OAuth wiring outside identity/oauthclient. Suite single sign-on follow-ups: memberships and entitlements in token claims and UserInfo, silent first-party SSO with back-channel logout, and token exchange for on-behalf-of calls between applications.