Pull in #9845 (runtime-safe redirect-uri validation and fragment rejection) now that it merged to main, so next gets the corrected validation directly instead of waiting for a later sync.
Brings the v1.6.13 stable fixes into next: redirect_uri scheme validation in oidc-provider and mcp (#9838), clientPrivileges enforcement on dynamic client registration (#9837), null organization logo support (#9842), the consumeOne non-numeric deleteMany guard (#9831), and the db-adapter guide alignment (#9830).
Conflicts were limited to release version bumps and CHANGELOG entries; resolved by keeping next's 1.7.0-beta.3 line. The ten changesets consumed by the v1.6.13 release are accepted as deleted, and no external dependencies changed.
The concurrent authorization-code redemption test (added on main for
GHSA-7w99-5wm4-3g79) exchanged the code against `/api/auth/oauth2/callback/test`,
but next renamed that callback path to `/api/auth/callback/test`. The merge
kept next's code-minting helper while preserving main's exchange path, so the
race winner failed redirect_uri validation and no caller minted a token,
tripping the "exactly one success" assertion.
Because that test threw before closing its listener on port 3000, the leaked
server poisoned the later EdDSA and HS256 oidc-jwt tests (stale server, code
not found on consume). Aligning the exchange path to next fixes all three.
These test files auto-merged main's additions onto next's refactored test
surface, producing runtime failures tsc could not catch:
- token.test.ts called main's sync createRefreshAccessTokenRequest, which next
renamed to the async refreshAccessTokenRequest; the calls now await it and
the concurrent-rotation helper is async.
- private-key-jwt-e2e.test.ts hit the merged-in #9578 gate: both the provider
and RP test instances create the same default user, so the OAuth sign-in
links to the RP's unverified local user and is blocked. The RP sets
requireLocalEmailVerified: false so the test exercises private_key_jwt
rather than the (separately covered) email-verification gate.
- utils/basic-auth.test.ts is removed: next made basicToClientCredentials
private, and its colon-preservation coverage now lives in core's
basic-credentials.test.ts.
Full oauth-provider suite green (24 files, 378 tests).
Reconcile pnpm-lock.yaml (root) and demo/nextjs/pnpm-lock.yaml with the merged
package.json and pnpm-workspace.yaml overrides: next's workspace versions plus
main's security bumps (samlify 2.13.1, fast-xml-parser 5.8.0) and the #9820
transitive-version overrides. demo/nextjs is a standalone pnpm project whose
lockfile was resolved to next's during the merge while its workspace took
main's overrides, so it is regenerated to match.
Two root overrides pin dependencies to next's working versions:
- kysely 0.28.17 (main's resolved version). The catalog range permits 0.29,
but kysely 0.29's ESM build dropped the migration-table exports the
kysely-adapter node:sqlite introspector imports, breaking table discovery.
- zod 4.x to 4.3.6. better-call's zod peer otherwise resolves to 4.4.3, which
produces a third zod copy that makes the docs app resolve zod to the 3.x
line and fails react-hook-form's zodResolver type check.
main reaches these versions through its release-age policy; next pins them
explicitly because that policy does not resolve in every environment. Remove
the pins once the kysely 0.29 and zod 4.4 upgrades land with their fixes.
Verified: frozen install (root and all demos) under the strict policy, docs
tsc, build, and the OAuth/SSO/oauth-provider regression suites.
Re-applies main's #9821 onto next's rebuilt SAML routes. next kept the
createSP/createIdP helpers and consolidated saml-pipeline, so the fix is
expressed there rather than in the inline construction main patched.
- normalizePem trims leading indentation from PEM private keys so samlify
2.13.1 (OpenSSL 3 native parsing) loads keys pasted with surrounding
whitespace; applied in the SP/IdP helpers.
- processSAMLResponse reads identity attributes through an attr() accessor
that collapses samlify 2.13's string | string[] attribute values to the
single-valued identity fields.
- the SLO LogoutResponse is built by passing the parsed request to samlify
(filling InResponseTo/Issuer/IssueInstant/Destination/StatusCode) instead
of manual template string replacement.
The samlify ^2.13.1 and fast-xml-parser ^5.8.0 bumps land via the merge.
saml.test.ts unions next's SAML suites with #9821's key-normalization and
SLO regression tests (120 passing).
Re-applies five main fixes onto next's rebuilt SSO routes. next's refactor
kept the supporting helpers (the SSRF host check, the SAMLSessionRecord
token field) but dropped the call sites, so the merge left them present yet
unused. This wires them back.
- #9574: registerSSOProvider and updateSSOProvider now run
validateSkipDiscoveryEndpoints on user-supplied OIDC endpoints, rejecting
private/link-local hosts (for example 169.254.169.254). The merge included
the helper but never called it, leaving advisory GHSA-5rr4-8452-hf4v open
on next. Validation runs even without skipDiscovery.
- #9220: registering an SSO provider requires an organization owner/admin
role when the organization plugin is active, blocking a low-privilege
member from registering a malicious IdP.
- #9818: SAML Single Logout revokes the session by token; the SAML session
record now carries the session token.
- #9722: the OIDC callback error redirect encodes the error value.
- #9702: a before-callback hook rejection in the OIDC and SAML callbacks
redirects to the per-flow errorURL with the hook's code, instead of
surfacing a raw response.
Two stale IDP-bounce assertions are updated to next's migrated state-error
vocabulary (state_not_found). All SSO regression suites pass (SSRF,
org-admin, SLO, hook-rejection, encoding).
(cherry picked from commit c3b238f71a6f66914f12db5f615504e2cf855bfc)
The merge auto-combined these test files without textual conflict, but they
referenced APIs next changed, so type checking failed:
- mcp.test.ts drove one (skipped) confidential-client test through the
removed generic-oauth client (genericOAuthClient + signIn.oauth2). It now
uses signIn.social like the surrounding tests.
- two-factor.security.test.ts read enrollment.totpURI directly; next's #9057
made the enable response a discriminated { method } union, so the tests now
narrow on method === "totp" before reading totpURI.
tsc --build is clean across the workspace.
(cherry picked from commit d3cd3c2eb39f0c7209f45ff27ac4b943250b93be)
Resolves the team and username test-file conflicts as the union of next's
new feature suites and main's regression tests, whose source fixes merged in
cleanly:
- team.test.ts keeps next's listUserTeams suites and adds main's
comma-in-team-id rejection test (#9616), plus the
databaseHooks.user.create.before -> emailVerified:true setup needed because
the merged invitation gate now defaults on (#9577).
- username.test.ts keeps next's immutable-username suite and adds main's
verify-email callbackURL encoding test (#9792); the admin createUser
username validation (#9464) is orthogonal and merged cleanly.
team.test.ts 43 pass, username.test.ts 41 pass.
(cherry picked from commit ea3ed7af30c12901252085c5ddf43a7dca57f929)
next's #9069 rewrote generic-oauth to flow through the core social provider
and callback. The #9799 accessTokenExpiresIn source fix merged in cleanly
(its config field and the applyDefaultAccessTokenExpiry wiring land on the
three token paths); main's regression tests still targeted the old
signIn.oauth2 plugin API. This ports them onto signIn.social so they keep
guarding the behavior:
- #9799: getAccessToken refreshes once the accessTokenExpiresIn window passes
when the provider omits expires_in, does not refresh when it is unset, and
applies it to a custom getToken result.
- #9792: the verify-email link preserves the encoded callbackURL.
- #9702: a session hook throwing an APIError redirects to the cross-origin
errorCallbackURL with the hook's code (proves the rewritten callback's
hook-error forwarding).
- #9578: implicit-link tests mark the local user verified so linking is
allowed under the default-on requireLocalEmailVerified gate.
A stale IDP-bounce assertion is updated to next's state-error vocabulary.
All 71 tests pass.
(cherry picked from commit 52adab0db3f463295a0c4e3152d770f4ce40b2d2)
Layers main's #9607 clarification onto next's #9057 OTP-enablement rewrite.
The "Sign In with 2FA" section now states that 2FA sign-in enforcement
applies to the credential endpoints (/sign-in/email, /sign-in/username,
/sign-in/phone-number) and that passwordless flows are not gated by default,
with guidance to add custom hook handling to require it. The passwordless
callout and the allowPasswordless option note that the flag does not change
which sign-in methods are challenged. main's edit to the removed
skipVerificationOnEnable bullet is dropped, since next deleted that option.
(cherry picked from commit 6156cef51329e1c38544aa06eb5072d8a06ece7a)
Re-applies two main fixes onto next's refactored metadata layer.
- #9448: registration_endpoint is now advertised only when dynamic client
registration is enabled. With DCR off, the field was still advertised in
authorization-server and OpenID metadata while the endpoint itself was
disabled, so clients attempted a registration that failed.
- #9668: the authorization-server and OpenID discovery documents are served
again at their path-prefixed (RFC 8414 path-insertion and issuer-appended)
aliases via an onRequest handler, with HEAD support and a 405 for
non-GET/HEAD. next's refactor kept the server-only metadata endpoints but
dropped the request handler that exposes them over HTTP. The handler is
adapted to next's metadata (CIMD-aware public-client flag and merged
discovery metadata) and metadataResponse is exported for it.
#9601 (preserve colons in Basic Auth secret) is already covered: next routes
Basic parsing through @better-auth/core decodeBasicCredentials, which splits
on the first colon, so no change to utils/index.ts.
The merged metadata test suite (union of next and main, including the
DCR-conditional and alias cases) passes.
(cherry picked from commit 66a7b0b62d76e36474ebdca189f59200daa3ec76)
Re-applies three main fixes onto next's rewritten OAuth callback, which
now routes generic-oauth through the shared callback route.
- #9702/#9788: a before-callback hook that rejects sign-in (the banned-user
guard is the flagship case) now redirects to the per-flow errorURL
carrying the hook's machine-readable code and message, instead of
surfacing a raw error response. handleOAuthUserInfo is wrapped in
try/catch; the hook's APIError code is forwarded verbatim (it is
app-defined, not a member of OAUTH_CALLBACK_ERROR_CODES) via next's
local redirectOnError closure. The admin plugin's stale inline /callback
redirect is removed so the banned-user path flows through that single
forwarder and emits BANNED_USER with a URL-encoded description.
- #8758: accountLinking.updateUserInfoOnLink now applies on the explicit
OAuth link path, not only id-token linking.
The regression tests (banned social sign-in emits error=BANNED_USER, the
cross-origin errorCallbackURL case, and the 403-JSON id-token case) arrived
via the merge and were red on the baseline; these source changes make them
pass.
(cherry picked from commit 1d893ae572277447025e29462946a7aeb11443ac)
Sync main (through #9821/#9820/#9822/#9768) into next. Conflicting
package.json files keep next's version and intentional divergences (for
example the cli's c12 v4) while taking main's dependency updates, including
the samlify 2.13.1 and fast-xml-parser security bumps; the lockfile is
regenerated from the reconciled set. The 13 source/doc/test conflicts take
next's refactored architecture as the baseline; main's colliding fixes are
re-applied on top in the following per-domain commits.
Clean main fixes land via this merge, including the auto-restored
account-takeover defenses #9578 and #9577.