Skip to content

chore: update version and changelog - #566

Open
github-actions[bot] wants to merge 1 commit into
mainfrom
changeset-release/main
Open

github-actions[bot] wants to merge 1 commit into
mainfrom
changeset-release/main

Conversation

@github-actions

@github-actions github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or setup this action to publish automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

seamless-auth-api@0.23.0

Minor Changes

  • bcab4d1: Back-channel logout now reaches an application whose grant ends while the first-party session goes on (feat(oidc): send back-channel logout when one application's grant ends #543). When the provider service deletes a Grant (the user chooses "only sign out of this app", or the grant is revoked), the API sends that application a logout token for the sid it was given, with the same sender and the same oidc_backchannel_logout_sent and oidc_backchannel_logout_failed events as a session logout. Other applications on the session are not told, and nothing is sent while another live grant still signs the application in on that session. Applications that register a backchannel_logout_uri should expect a logout token for a partial logout as well as a full one.

  • bcab4d1: GET /oidc/payloads/DeviceCode/:id now returns a device code for 10 minutes after it expires, with its exp, instead of 404, and the purge job keeps it that long (fix(oidc): answer expired_token for an expired device code #542). The provider service hands that record to its library, which answers a client polling with an expired code expired_token (RFC 8628 section 3.5) rather than invalid_grant. A code that never existed, one past that window, the by-user-code lookup and every other model still answer 404 once expired, and an expired code can never be consumed.

  • bcab4d1: External delivery now requires the service token in x-seamless-service-token to carry subject seamless-auth-external-delivery (Harden secret responses, external delivery tokens and provider fetches outside production #556). Any other valid service token, such as an adapter's proxy token, no longer unlocks codes, links or account notices in the response; the API sends them through its own transport instead. The Node, Go, Python and Rust adapters already sign their delivery requests with that subject, so adopters on current adapters need no change. A caller that hand-mints its own service token for external delivery must set sub to seamless-auth-external-delivery.

  • 25c4cc6: Application notice text can no longer carry its own links (Keep application notice text from carrying its own links and attribution #554). POST /applications/account-notices refuses with 400 a text that contains a URL, a www. address, a domain name or an email address, since mail and SMS clients turn all of them into links; actionUrl, checked against the application's origins, is the only link a notice carries. The Sent by <application> through <instance>. attribution is now the first line of the message, ahead of the text, rather than a footer the text could imitate. Adapters that render notices from external delivery receive the new layout in text.

  • f82ec0c: A confidential OpenID Provider application can send account notices for the public applications it acts for (ADR 0002, N4 and N5). An admin links them with PUT /admin/applications/:id/links/:linkedId, lists links with GET /admin/applications/:id/links and removes one with DELETE, each write with step-up and audited as admin_oidc_application_link_added or admin_oidc_application_link_removed. A link needs a confidential acting application and a different public linked one, both first-party or both third-party. POST /applications/account-notices takes an optional clientId: the caller must be linked to it, the grant check uses it and the template names it, and a client the caller is not linked to answers the same 404 notice_unknown_user as an unknown user. Applications gain noticeActionOrigins, bare https origins that a notice's action link may point at besides the https redirect origins, and every application response lists noticeActionOrigins, links and linkedFrom. A new migration adds the oidc_client_links table and the column. ADR 0001 D14 is amended to match.

  • bed4994: A backend can authorize per organization from the access token alone (feat: organization-scoped roles and permissions in the access token #416, ADR 0004). Inside an active organization, tokens gain org_roles and org_permissions beside org_id, and GET /users/me and GET /users/me/permissions report the same. Organization roles never appear in roles, so a guard on admin does not pass for an organization admin. Organization roles are now a catalogue, managed with POST /admin/organization-roles and PATCH and DELETE /admin/organization-roles/:roleName, listed by GET /admin/roles, and mapped to permissions with PUT /admin/roles/organization/:roleName/permissions. A new migration seeds it with the built-in owner, admin and member and every value memberships already hold, and rewrites no membership. Behavior changes: assigning a role outside the catalogue now answers 400. PATCH /organizations/:id/members/:userId now needs step-up. Only an owner or an instance admin:write can grant or remove owner, or remove an owner from an organization, and any other manager can only grant a role whose permissions they hold, so an organization admin can no longer make themselves owner. Adding, changing and removing members is audited as organization_member_added, organization_member_updated and organization_member_removed, and a refused change as organization_member_role_grant_refused. Fixes: removing a member now reaches the token. Previously every refresh copied org_id from the session until it ended. Now the next token drops the organization claims and the session stops naming the organization. Recovery tokens carry no organization claims.

  • 25c4cc6: Passkey hardware assurance now comes from the registered credential rather than the assertion (Derive hardware-bound passkey assurance from the registered credential #552). Sign-in and step-up refuse an assertion whose backup eligibility flag differs from the one stored at registration (401, audited as webauthn_login_failed or step_up_failed with reason Backup eligibility differs from registration). A passkey counts as device-bound, and earns acr=phrh and amr=hwk, only when it was registered device-bound with attestation verified against the FIDO Metadata Service. Deployments that use the default none attestation, and credentials registered before attestation was recorded, now get phr and swk instead of phrh and hwk.

  • da66738: Roles can now grant permissions, and access tokens carry them (feat: permissions mapped to roles #415, ADR 0004). An admin manages the catalogue with GET and POST /admin/permissions and PATCH and DELETE /admin/permissions/:permissionName, reads roles with GET /admin/roles, and replaces what a role grants with PUT /admin/roles/:kind/:roleName/permissions. Each write needs step-up and is audited. Deleting a permission a role still grants answers 409 with those roles. Instance roles stay in available_roles, and removing one also removes its mappings, so adding the name back later does not restore old grants. Access tokens gain a permissions claim, matched exactly with no wildcards or implied grants, which is empty on a recovery session. When the permission claims would pass 1 KiB, which keeps an adapter's session cookie under the browser limit, the token instead carries permissions_omitted: true and a guard reads GET /users/me/permissions, which answers from the database on every call. GET /users/me gains user.permissions. Changes reach a token when it is next issued, within the access token lifetime. New migration adds the permissions and role_permissions tables.

  • 2f1c3db: GET /system-config/public can now tell a native admin tool how to sign in through the OpenID Provider (ADR 0003). A new system config key, oidc_cli_client_id (seeded from OIDC_CLI_CLIENT_ID, or set through PATCH /system-config/admin), names the application. The response gains an optional oidcProvider: { issuer, cliClientId }, present only when the provider is on, the issuer and key are set, and that application is active and eligible for session exchange with it turned on. Setting the key to an unknown or ineligible application answers 400; later disabling the application or turning its exchange off makes the advertisement disappear. Also fixes PATCH /system-config/admin answering 500 when it set a nullable key such as oidc_issuer, hosted_ui or max_concurrent_sessions to null: the row is now removed, which reads back as null. No migration is needed. Needs the @seamless-auth/types release that adds these fields.

  • 25c4cc6: Owner notices that a delivery-owning adapter does not advertise are now sent directly instead of dropped (Send owner notice kinds the adapter does not deliver #557). Under external delivery with a service token, a notice whose kind is missing from x-seamless-auth-delivery-kinds (for example the SMS notice on /recovery/verify when the adapter lists only account_notice_email) goes out through the API's own email or SMS transport when one is configured, and is recorded as sent or failed rather than not_sent_external_delivery. The same applies to the recovery completed and recovery code used notices and to POST /applications/account-notices. A recovery that previously answered 503 recovery_notice_failed for an adapter listing no notice kinds now proceeds when the API can notify the owner itself. Admin identity-change notices are unchanged.

  • 52ff3c3: A first-party native client signed in through the OpenID Provider can now reach the admin API, by exchanging the sign-in for a first-party session of its own (ADR 0003). An admin turns sessionExchangeEnabled on for a first-party public native application, with step-up, audited as admin_oidc_application_session_exchange_updated. Such a client can ask for a 60 second access token for <oidc_issuer>/seamless with the single scope session:exchange, and /oidc/sign records each one. POST /sessions/exchange (adapter: false, RFC 8693 parameter names) consumes that token once and returns the /refresh body plus issued_token_type, for a new session linked to the provider sign-in. A second presentation answers 400 invalid_request with token_already_used and revokes the session the first one created, audited as session_exchange_replayed. A successful exchange is audited as session_exchanged.

    The new session has its own refresh chain and step-up state, ends whenever its parent ends, never outlives it, and refreshing it counts as activity on the parent. It copies the parent's auth context and step-up for an authorization code grant, and nothing for a device grant. A device-grant session answers 403 step_up_required until it has a fresh step-up, on every route behind requireAdmin and every /organizations route except POST /organizations/:id/switch, reads included, and on /totp/enroll/*, /totp/disable, /webauthn/register/*, POST /recovery-codes, DELETE /users/credentials, /registration/phone, /registration/phone/verify and DELETE /recovery/requests/:id, so a device sign-in with no TOTP cannot enroll one and the user does it from a browser. It can step up with TOTP or a real passkey assertion on the relying party's domain. OpenAPI now documents this 403 on those routes and on admin reads. It does not count against max_concurrent_sessions. Disabling or deleting the application, or turning the flag off, revokes the sessions it exchanged for. Session responses gain parentSessionId, exchangeClientId and exchangeGrantType on such a session, and application responses gain sessionExchangeEnabled. A new migration adds the columns. ADR 0001 D11 is amended to match.

  • 25c4cc6: third_party_clients_enabled now gates existing third-party OpenID Provider clients, not only creating and updating them (Enforce third_party_clients_enabled when serving and signing for existing clients #555). While it is off, a client that is not first-party is treated like a disabled one: /oidc/clients/:id answers 404, its shared secret does not verify, /oidc/sign refuses with client_inactive, its notice tokens are refused and back-channel logout skips it. An instance that turned the flag off after registering third-party clients stops serving them on upgrade; turn the flag back on, or mark the applications first-party, to keep them working.

Patch Changes

  • 25c4cc6: Two hardening fixes from Harden secret responses, external delivery tokens and provider fetches outside production #556. Every API response now carries Cache-Control: no-store unless its route sets its own caching (the JWKS and the admin dashboard's hashed assets do), so tokens, client secrets from create and rotate, recovery codes and delivery payloads are never stored by a cache. Provider requests (OAuth token, userinfo and discovery, and back-channel logout) now go through the address guard in every mode: outside production, http and private or loopback addresses are still allowed, but link-local 169.254.0.0/16 and unspecified 0.0.0.0/8 addresses are refused whether named directly or reached through DNS.
  • 25c4cc6: The per-user limit on POST /applications/account-notices now folds the userId to lower case before counting (Key the application notice per-user limit on the canonical user id #553). Upper-case variants of one UUID no longer each get their own allowance of 10 notices an hour.
  • 1400b20: A failed OAuth start now says why. The oauth_login_failed event with reason start_failed carries a detail of redirect_not_allowed or provider_unusable, and the error is logged. A refused redirect is logged by scheme and host only. The answer to the client is unchanged.
  • 5b1707a: Starting the server no longer rewrites an env-backed system_config row, or warns that an admin change is being reverted, when the stored value only orders its keys differently. Postgres JSONB does not keep key order, so object values such as lockout_policy and flow_rate_limits looked changed on every boot.

@github-actions
github-actions Bot force-pushed the changeset-release/main branch from 6326942 to 0185063 Compare November 6, 2026 06:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants