v0.29.0¶
Release Date: 2026-10-29
A release that decides consent by who owns a client, proves email addresses, and reads secrets from a vault:
- Consent by party. Each client says whether the service provider, a
customer or a partner owns it. Only first-party clients skip the consent
screen;
social_login.skip_consentno longer grants consent for every client. - OIDC
promptand a named login page. The authorization endpoint honoursprompt, so a first-party application can sign a user in silently, and the login chooser names the requesting client and shows its logo. - Verified email. With a new
mailsection, SystemAuth verifies an unverified provider email at sign-in, lets a signed-in person change their email, and tells relying parties when the address was verified. - Secrets through omnivault. Secret references in the configuration
resolve through a vault named in
secrets.vault, with the environment as the default.
It also adds systemauth org member add|remove|list, organization slug
resolution for relying parties, and read-only application vocabularies in
the product contract API.
The release is breaking. An existing database needs the
sf_oauth_apps.party column, and users of a client registered at runtime
without a party are now asked for consent. Clients declared in the
configuration file stay first-party, so they keep signing in without a
consent screen. Read Upgrading first.
Highlights¶
- Client parties:
partyisfirst_party,second_partyorthird_party, with an optionallogo_url. Configuration-file clients default tofirst_party; clients registered at runtime default tothird_party. See Client party and Parties. prompt:none,login,consentandselect_account. See Silent sign-in and prompt.- Verified email: a verify-email step
at sign-in, email change at
/account/email, and theemail_verified_atclaim, with mail sent through Amazon SES, SMTP or, in development, the log. secretspackage: a resolver over an omnivault vault that expands${NAME}and$NAMEexactly likeos.ExpandEnvby default. See Secrets.systemauth org member: add, remove and list an organization's members and their application roles from the command line.- Slug resolution:
GET /oauth/userinfo/memberships/resolve?slug=andrelyingparty.Client.ResolveOrganizationmap a slug, or the old slug of a renamed organization, to the caller's membership.
Security¶
- Consent.
social_login.skip_consentgranted consent for every client to users signed in through social login, andDefaultSessionProvider'sWithSkipConsentdid the same for its users. Once a client the operator does not own was registered, its users were never asked. The authorization endpoint now decides by the client's party, whatever theSessionProvider: a first-party client needs no consent, and a second- or third-party client needs the user's recorded consent to every requested scope. A client from a customStoragethat does not report its party is treated as third-party. - Verification links. Tokens are single-use, scoped to a purpose
(
verify_emailoremail_change) and stored as SHA-256 hashes. Averify_emaillink completes only in the browser that started the sign-in, bound to a__Host-cookie; anemail_changelink only from a session of the principal that asked. Each confirmation is a POST, origin-checked with a CSRF token, so a forwarded or scanned link, or another person's pending sign-in, cannot complete it, and a wrong binding leaves the token usable for its owner. A link verifies an address; it is not a way to sign in. - Mail limits. Verification mail is limited per recipient address and per client address on the shared rate limit storage. A used-up recipient limit only suppresses the message, and requests answer the same whether or not a message was sent or the address belongs to an account.
- No links in production logs. Production mode refuses
mail.provider: log, with no waiver, because it writes verification links to the server log. - Client logo.
logo_urlmust be an absolute https URL. It is rendered byhtml/template, and only the login chooser that shows it allows images from its origin in the content security policy. - Security events record verification mail sent, emails verified, and email change requests, cancellations and changes, without addresses. See Security events.
Breaking Changes¶
sf_oauth_apps.party. Reading or writing an OAuth application fails without the column. Startup migration adds it; otherwise runmigrations/0012_oauth_app_party.sql, which also fills it fromfirst_party.skip_consentandWithSkipConsentno longer grant consent. Users of a second- or third-party client, including any client registered at runtime without a party, are asked for consent.DefaultSessionProviderrecords no consent and reports none. A deployment using it with second- or third-party clients needs aSessionProviderthat records consent.rate_limit.redis_urlis expanded like the other secret fields. It was documented as expanded but was not, so a literal$in it is now expanded.
Added¶
- Client party and logo:
partyandlogo_urlonClient, static clients,OAuthClientand Ent storage (sf_oauth_apps.party,sf_oauth_apps.logo_url). Unknown parties are refused byConfig.Validate,NewClientFromConfig,RegisterClientand both storages. - OIDC
prompt: noneshows no page: without a login session it answerslogin_required, and when consent is neededconsent_required, at the client's redirect URI with its state;loginsends the user to the login page even with a login session;consentshows the consent page even when consent is recorded, for first-party clients too;select_accountis accepted and has no effect;nonewith another value, or an unknown value, isinvalid_request.- Login chooser names the client ("to continue to ...") and shows its logo when the page is reached from an authorization request.
- Mail settings:
mail.provider(ses,smtporlog),from,reply_to,product_name,support_url,token_lifetime(5m to 24h, default 30m),per_address(default 5 an hour),per_client(default 20 an hour),smtp.*,ses.regionandses.configuration_set;systemauth.WithMailSenderandsystemauthsvc.Options.MailSender. The library packages depend only on the omnimailSender; thesystemauthbinary builds the Amazon SES sender frommail.ses. - Verification tokens:
VerificationTokenStorewith memory and Ent implementations (sf_verification_tokens) and thestoretest.VerificationTokenStoreconformance suite. - Verify-email step at sign-in: with mail configured, a social sign-in
whose provider email is not verified is held, a link is mailed, and the
verify-email page offers a rate-limited resend. A confirmed address
counts as proven (
ExternalLogin.EmailProven), so the sign-in may create an account or link to an existing account with the same verified email. - Email change at
/account/email, with a notice to the current address;PrincipalEmailChanger, implemented by the Ent and memory directories. email_verified_at: with theemailscope, ID tokens and UserInfo carry when the current address was verified, as a NumericDate, and discovery lists it inclaims_supported.HumanPrincipal.EmailVerifiedAt;relyingparty.IDTokenClaims.EmailVerifiedAtandUserInfo.EmailVerifiedAt(*jwt.NumericDate) andVerifiedIdentity.EmailVerifiedAt.- Security events
systemauth.email_verification.sent,systemauth.email.verified,systemauth.email_change.requested,systemauth.email_change.cancelledandsystemauth.email.changed, and the sign-in refusal reasonverification_invalid. secrets:Resolver,NewandEnv; thesecrets.vaultsetting;systemauth.LoadConfigWithResolver,ParseConfigWithResolver,DecodeConfigWithResolverandDecodeConfigFileWithResolver;systemauthsvc.LoadConfigWithResolverandLoadConfigBytesWithResolver.identity/organization/membercmd: theorg membercommand a host CLI mounts.add <org> <person>adds a member or changes their role and assigns--role app:role;removeremoves the membership, or with--apponly those applications' roles;listprints members with their roles (--json,--include-inactive). It applies changes throughorganization.Serviceandapprole.Service, so removals record access changes as the API does.- Slug resolution:
GET /oauth/userinfo/memberships/resolve?slug=, authenticated like the memberships endpoint, returns the caller's membership with the current slug andaliastrue for a slug given up within its cooling-off period; an unknown slug and an organization the caller does not belong to both answer404. The optionalMembershipResolverinterface, implemented byEntOrgsSource;relyingparty.Client.ResolveOrganizationandErrOrganizationNotResolved. - Application vocabularies:
GET {base}/policy/vocabulariesin the product contract API lists each application's roles with their levels and permissions, the union of its permissions and its OAuth scopes, from the newcontract.WithVocabulariesoption. It is gated bypolicy:read. - Dependencies:
github.com/plexusone/omnimailv0.1.0 andgithub.com/plexusone/omni-awsv0.11.0.
Changed¶
- Clients declared in the configuration file default to
first_partyand are rewritten from it at startup; clients registered at runtime default tothird_party. - The example configuration and the CLI help no longer mention
skip_consent.
Deprecated¶
social_login.skip_consent: ignored; SystemAuth logs a warning at startup when it is set.WithSkipConsentonDefaultSessionProvider: a no-op.sf_oauth_apps.first_party: useparty. It is kept equal toparty == first_party.
Upgrading¶
New SystemAuth settings. Values reach the YAML through ${VAR} references,
so the variable name is a convention.
| YAML key | Suggested variable | Needed when |
|---|---|---|
clients[].party |
— | A configuration-file client is not the operator's own |
clients[].logo_url |
— | The login chooser should show the client's logo |
mail.provider, mail.from |
— | Unverified provider emails should be verified |
mail.smtp.password |
SYSTEMAUTH_SMTP_PASSWORD |
mail.provider: smtp with authentication |
mail.ses.region |
— | mail.provider: ses |
secrets.vault |
SECRETS_VAULT |
Secrets live in a vault other than the environment |
- Migrate the database. Run
systemauth migrate(or start with--migrate, the default). It addssf_oauth_apps.partyandsf_oauth_apps.logo_urland createssf_verification_tokens. Without startup migration, also runmigrations/0012_oauth_app_party.sql, which fillspartyfromfirst_party. See Client party column. - Check the party of runtime-registered clients. Clients in the
configuration file are rewritten as
first_partyat startup unless they setparty, so existing suite applications keep signing in without a consent screen. A client registered at runtime isthird_partyunless itsfirst_partyflag was set. Setparty: first_partyon clients the operator owns andsecond_partyon clients a customer owns; otherwise their users now see the consent screen. - Remove
skip_consent.social_login.skip_consentandWithSkipConsentare ignored. A deployment that relied onDefaultSessionProviderfor second- or third-party clients needs aSessionProviderthat records consent. -
Add
mailto verify emails. It is opt-in; without it, sign-ins with an unverified provider email are refused as before. Thesystemauthbinary builds thesessender frommail.seswith the AWS default credential chain; an application usingsystemauthsvcpasses it asOptions.MailSender, and one embeddingsystemauthpasses it withWithMailSender.smtpandlogare built from the configuration, and production refuseslog:mail: provider: ses from: "Example <[email protected]>" ses: region: us-east-1See Verified email and Sending Mail with Amazon SES. 5. Read secrets from a vault, if you want to. Nothing changes by default: references expand from the environment as before. To read them from another vault, set
secrets.vaultto an omnivault URI (env://,file://andmemory://are built in). For a cloud secret manager, register its scheme withomnivault.RegisterProviderin your own binary, or pass a resolver tosystemauthsvc.LoadConfigWithResolver. SystemForge imports no cloud SDK for this. See Secrets. 6. Checkrate_limit.redis_url. It is now expanded, so a literal$in it is replaced; write it as a${VAR}reference instead.
Walk the production checklist before going live.
Documentation¶
- Verified email: configuration, messages, abuse controls, the sign-in step, email change and what relying parties receive
- Sending Mail with Amazon SES: domain identity, Easy DKIM, SPF through a custom MAIL FROM domain, DMARC, a configuration set, leaving the sandbox and a least-privilege policy
- Client party,
consent and
prompt - Secrets and
secrets.vault - Reading email verification and resolving slugs in a relying party
- Entitlements in tokens
- List application vocabularies
For release history, see the changelog.