v0.23 Release Notes
v0.23 makes Seamless Auth answer “what may this user do” as well as “who is this”. Roles grant permissions, tokens carry them per organization, every adapter can guard on them, and the auth server calls out to your systems: signed webhooks, login hooks that can deny or add claims, static custom claims, and audit streaming to a SIEM. It also finishes v0.21’s native app work and the fixes from its security review.
It shipped as auth API 0.23.0 and OpenID Provider 0.4.0 (0.4.1 is the patch to run), with releases of the shared types (0.40.0), server adapters (core and Express 0.26.0, Fastify 0.17.0, Next.js 0.10.0), the admin dashboard (0.14.0) and the CLI (0.30.0). The Go, Python and Rust adapters’ permission guards shipped in 0.6.0. The versions that go together are in the Compatibility Matrix. Full details are in each package’s changelog and GitHub release.
Roles And Permissions
Section titled “Roles And Permissions”- Permissions in the access token. An admin defines a permission catalogue and maps instance
roles to permissions. Access tokens carry a
permissionsclaim, matched exactly with no wildcards. See How To Use Roles And Permissions. - Organization roles. Organization roles are a catalogue now, seeded with
owner,adminandmemberplus every value memberships already hold. Inside an active organization, tokens carryorg_id,org_rolesandorg_permissions. Organization roles never appear inroles, so a guard onadmindoes not pass for an organization admin. - Guards in every adapter.
requirePermissionin@seamless-auth/express, and the matching guards in Fastify, Next.js, Go, Python and Rust, checkpermissionsandorg_permissions.usePermissions()in@seamless-auth/react0.20.0 gates UI. Guards deny every request against an auth API older than 0.23.0, since no token carries permissions there. - A token budget. Permission and custom claims share 1 KiB
(
AUTHORIZATION_CLAIMS_BUDGET_BYTES), which keeps an adapter’s session cookie under the browser limit. Over it, the token carriespermissions_omitted: trueand a guard readsGET /users/me/permissionsinstead. - Admin tools. The console’s Roles page, and
seamless permissionsandseamless rolesin the CLI.
Extensibility
Section titled “Extensibility”- Signed webhooks. Endpoints under
/admin/webhooks, signed with the Standard Webhooks scheme, retried eight times over about 42 hours and then dead-lettered, with redelivery. Verify them withverifyWebhookfrom@seamless-auth/core/webhook, or any Standard Webhooks library. See How To Receive Webhooks. - Login hooks. A
post_loginhook runs once the factor is proven and may deny the sign-in. Apre_tokenhook runs for a new session and on organization switch, and may add claims or deny. Each hook fails open or closed, as you choose. Break-glass administrators and recovery sessions never call a hook. - Custom claims. Static claims per instance role, organization role or OpenID Provider client,
under
/admin/custom-claims. See How To Use Login Hooks And Custom Claims, which also maps Auth0 Actions to them. - SIEM streaming. Audit sinks under
/admin/audit-sinksstream every audit event over OTLP/HTTP, carrying the hash chain so it verifies from the SIEM’s copy. Audit retention never deletes an event a sink has yet to deliver. See How To Stream Audit Events To A SIEM. - One set of reachability rules. Webhooks, hooks and sinks refuse link-local, multicast and the
API’s own host, never follow redirects, and in production refuse private addresses unless the
host is listed in
CALLOUT_PRIVATE_HOSTS. - Admin tools. The console groups Webhooks, Login hooks and Audit streaming under
Integrations. The CLI adds
seamless webhooks,seamless hooks,seamless claimsandseamless audit-sinks.
OpenID Provider
Section titled “OpenID Provider”- Roles, permissions and custom claims in provider tokens. New scopes
permissionsandcustom_claimsput them in ID tokens,/userinfoand RFC 9068 access tokens. The API signs only values it computes again, so the edge cannot add or change one. org_idis the active organization. For provider tokens it now names the session’s active organization, not the user’s oldest membership.- Key rotation without a restart. The edge re-reads the signing keys every five minutes, and at once when the API refuses a key as unknown.
TRUST_PROXYtakes a hop count or addresses, so the edge forwards the right client IP behind more than one proxy.- Back-channel logout for one app. An application whose grant ends while the first-party session goes on now gets a logout token too.
- Device codes. A client polling an expired device code gets
expired_token, as RFC 8628 says. - Hosted pages meet WCAG 2.2 AA with the default palette, and errors the provider cannot send back to the app render on them with your branding.
Native And Desktop Apps
Section titled “Native And Desktop Apps”- Session exchange. A first-party native client that signs in through the OpenID Provider can
exchange the sign-in once for a first-party session of its own at
POST /sessions/exchange, and reach the admin API with it. A device-grant session needs a fresh step-up before admin and organization routes, reads included. - CLI sign-in through the provider.
seamless profile loginopens the system browser with a loopback redirect, or uses the device grant with--device, which brings passkey sign-in to the CLI. An instance can advertise the provider and the CLI’s client inGET /system-config/public(oidc_cli_client_id). - A confirmation for native clients. The hosted pages ask the user to confirm “Continue to” the app for every native client request, so a program listening on a loopback port cannot get a code without a click.
Security Fixes
Section titled “Security Fixes”From the post-v0.21 security review:
- A passkey’s backup eligibility must match the one stored at registration, and hardware assurance
(
acr=phrh,amr=hwk) comes from the registered credential. - Application account notice text can no longer carry its own links, and the attribution is the first line.
third_party_clients_enabledgates existing third-party clients, not only new ones.- External delivery needs a service token with subject
seamless-auth-external-delivery. - Every API response is
Cache-Control: no-storeunless its route sets its own caching. - Provider requests go through the address guard in every mode.
- The per-user notice limit folds the user id to lower case before counting.
- The edge no longer leaves a grant behind when finishing a login fails, and
/session/endno longer signs a browser out of the hosted pages when there was no provider sign-in.
Sharing rate limit counters across instances moved to v1.0 (seamless-auth-api#581). Until then each instance keeps its own counters, so several instances allow several times the budget, as Security describes.
Breaking Changes
Section titled “Breaking Changes”Check each of these before you upgrade the auth API.
| Change | Who it affects | What to do |
|---|---|---|
Sign-in completion and POST /organizations/:id/switch can answer 403 login_denied or 503 login_hook_unavailable |
Only instances that configure a login hook | Show the 403 message when present, and offer a retry for the 503 |
| A passkey whose backup eligibility changed since registration is refused with 401 | Rare. Usually a credential that moved between a device-bound and a synced store | The user signs in with another factor and registers the passkey again |
acr=phrh and amr=hwk need attestation verified against the FIDO Metadata Service |
Deployments on the default none attestation, and credentials registered before attestation was recorded |
They now get phr and swk. Relax any guard that required phrh, or turn on attestation |
third_party_clients_enabled off now stops existing third-party clients |
Instances that turned the flag off after registering third-party clients | Turn the flag back on, or mark those applications first-party |
External delivery needs the seamless-auth-external-delivery service token subject |
Only callers that mint their own service token for external delivery | Set sub to seamless-auth-external-delivery. Current adapters already do |
Account notice text with a URL, domain or email address is refused with 400 |
Applications calling POST /applications/account-notices |
Put the link in actionUrl. Adapters rendering notices receive the attribution as the first line |
| Assigning an organization role outside the catalogue answers 400 | Admin tools that assign free-form roles | Create the role first. Existing memberships are seeded into the catalogue |
PATCH /organizations/:id/members/:userId needs step-up, and only an owner or an instance admin:write can grant or remove owner |
Organization admins | Organization admins step up first. They can no longer make themselves owner |
Provider tokens’ org_id is the active organization |
Provider clients that read org_id |
Read it as the active organization, or switch organization first |
Applications with a backchannel_logout_uri get logout tokens for one-app logouts too |
Provider clients with back-channel logout | Treat it like a full logout for that sid |
The Express, Fastify and Next.js adapters (0.25 and later) answer 415 to a body-less POST or DELETE on the auth routes with no JSON content type and no x-seamless-auth-transport header |
Browser apps whose lockfile holds @seamless-auth/client 0.8.2 or earlier, and clients of your own |
Update to @seamless-auth/client 0.8.3 or later (npm ls @seamless-auth/client). Otherwise passkey step-up, authenticator app enrollment, organization switch and account deletion are refused |
Upgrading
Section titled “Upgrading”Upgrade the auth API first, then the OpenID Provider, the server adapters, the dashboard and the CLI. Clients need no change. Coming from v0.21, read the v0.22 Release Notes first: provider sign-ins made before the upgrade sign in again.
- Upgrade the auth API to 0.23.0. It runs eight additive migrations on start unless
RUN_MIGRATIONS=false: application links, session exchange, permissions, organization roles, webhooks, audit sinks, login hooks and custom claims. If you setRUN_MIGRATIONS=false, run them first, because the server refuses to start while any migration is pending. No setting is required to upgrade. - Set
CALLOUT_SECRET_ENCRYPTION_KEYbefore you create a webhook or a login hook in production, which requires it once one exists. It encrypts their stored signing secrets. Credentials for audit sinks are env vars ending_AUDIT_SINK_SECRET, named by the sink and never stored. - Upgrade the OpenID Provider to 0.4.1, if you run it. It speaks the same
/oidc/*contract as API 0.22.0 and 0.23.0, but needs 0.23.0 for the new scopes. Skip 0.4.0 if you have native or desktop clients: it gives a step-up or second sign-in the first sign-in’s grant, so an app that revokes the refresh token it replaced signs itself out. - Upgrade the server adapters:
@seamless-auth/express0.26.0,@seamless-auth/fastify0.17.0,@seamless-auth/nextjs0.10.0, or the Go, Python or Rust adapter 0.6.0. With a Node adapter, check that the browser app has@seamless-auth/client0.8.3 or later. - Upgrade the dashboard to 0.14.0 if you deploy it separately, and the CLI to 0.30.0.
- Define permissions and map roles before you add guards. A guard on a permission no role grants refuses everyone.
Not In v0.23
Section titled “Not In v0.23”- Syslog audit sinks, in 0.23.1 (seamless-auth-api#586).
- Rate limit counters are per instance (seamless-auth-api#581).
- The server adapter’s cookie holds the token budget at 1 KiB. A larger budget waits on the cookie change in seamless-auth-server#278, planned for v1.0.
- OpenID certification is not done. The provider passes the OpenID conformance suite in CI, and the certification fee waiver is pending (seamless-auth-api#412).