v0.20.0¶
Release Date: 2026-10-23
This is a security release. It closes gaps where a tenant or application boundary was assumed but not enforced: tokens accepted by applications they were not issued for, SCIM credentials that reached every organization, revocation that left access behind, and authorization checks that failed open. It is breaking: read the upgrade notes before upgrading.
Highlights¶
- Tokens are bound to their application. A relying party accepts only
access tokens issued to its own client (or the clients it lists) and never
an ID token; the session JWT service requires its application audience on
every token; multi-app routing requires a token addressed to the routed
app. SystemAuth types its JWT access tokens
at+jwt(RFC 9068). - SCIM is per organization. Each SCIM credential is bound to one organization. A customer's identity provider sees and provisions only its own members, cannot change the identity of users who also belong elsewhere, and cannot deprovision the organization's owner. Membership changes go through the organization service, so they sync the authorization graph and revoke existing tokens. See SCIM Provisioning.
- Revocation removes access. Deactivating a membership takes the member out of the SpiceDB graph; a connection's creator loses their rights on leaving the organization; logout always ends the session, including with the documented Huma setup; and a crafted session cookie can no longer hide a user's sessions from "log out everywhere".
- Authorization fails closed. The GuardSQL adapter refuses to build a policy that grants nothing, resource checks use the organization that owns the resource, and an AAuth agent token is accepted only for its issuer's own agents.
Security¶
- Relying-party bearer authentication accepted access tokens issued to any other OAuth client, and ID tokens, along with the organization memberships in their claims.
- The session JWT service and multi-app routing accepted tokens minted for another application sharing the signing key.
- The GuardSQL adapter turned "no entity authorized" into an unrestricted policy.
RequireResourceActionchecked a resource against the caller's role in their own organization rather than the resource's.- SCIM had no tenant scoping, could rewrite the login email of users shared with other organizations, skipped graph sync and token revocation, could deprovision an owner, and restored a re-added member's previous role.
- A deactivated membership kept its SpiceDB permissions.
RegisterHumaRoutesstub handlers shadowed the real logout, session and refresh handlers.- A connection's creator kept rights after leaving the organization.
- BFF logout passed unverified cookie values to the session store.
- AAuth agent tokens from any provider could claim another domain's agents.
Upgrade notes¶
- Go modules. No dependency changes. The
godirective stays at 1.26.6. - Session JWT. Set
jwt.Config.Audienceto a value naming the application;NewServicerefuses to start without it. Access tokens issued before the upgrade are refused and clients refresh; refresh tokens issued without the audience require signing in again. - Relying parties. List in
relyingparty.Config.AcceptedClientIDsevery client whose tokens the API accepts, including the application's ownClientID, such as service clients using client credentials. For an API that many clients call with an audience, setAccessTokenAudienceinstead. - Multi-app. Mint tokens with
jwt.TokenOptions{Audience: []string{appID}}, or setMiddlewareConfig.AppAudiencesto the audiences your tokens carry.AllowCrossAppTokensopts out. - Resource routes. Replace
authz.WithResourceIDwithauthz.WithResourceLookupwhere access depends on the organization role. - GuardSQL. Refuse the query when
PolicyBuilder.Buildreturnsauthzguardsql.ErrNoAuthorizedEntities. - SCIM. Authenticate with
API.CredentialMiddlewareand bind each credential to its organization (scim.Scope{OrganizationID: ...}); giveScope{Platform: true}only to platform operators. Pass the application's organization service, with its authz syncer, and principal service to the SCIM store asConfig.MembershipsandConfig.Principals. - Connections. Creator rights need an
Options.Authorizerthat implementsauthz.OrgAuthorizer; the rbac and SpiceDB providers do. - BFF. A custom
SessionIDGeneratormust produce IDs thatbff.ValidSessionIDaccepts.
Known limits¶
- The Go toolchain stays at 1.26.6, which misses standard-library security fixes released in later Go versions; a toolchain update follows once CI moves to the patched Go.