Improper Handling of `callbackUrl` parameter in next-auth
Description
NextAuth.js is a complete open source authentication solution for Next.js applications. In affected versions an attacker can send a request to an app using NextAuth.js with an invalid callbackUrl query parameter, which internally is converted to a URL object. The URL instantiation would fail due to a malformed URL being passed into the constructor, causing it to throw an unhandled error which led to the API route handler timing out and logging in to fail. This has been remedied in versions 3.29.5 and 4.5.0. If for some reason you cannot upgrade, the workaround requires you to rely on Advanced Initialization. Please see the documentation for more.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
next-authnpm | < 3.29.5 | 3.29.5 |
next-authnpm | >= 4.0.0, < 4.5.0 | 4.5.0 |
Affected products
1- Range: < 3.29.5
Patches
2e498483b2327test: add test for invalid `callbackUrl` handling
1 file changed · +16 −0
packages/next-auth/tests/assert.test.ts+16 −0 modified@@ -13,6 +13,22 @@ it("Show error page if secret is not defined", async () => { expect(log.error).toBeCalledWith("NO_SECRET", expect.anything()) }) +it("Should show configuration error page on invalid `callbackUrl`", async () => { + const { res, log } = await handler( + { providers: [] }, + { prod: true, params: { callbackUrl: "invalid-callback" } } + ) + + expect(res.status).toBe(500) + expect(res.html).toMatch(/there is a problem with the server configuration./i) + expect(res.html).toMatch(/check the server logs for more information./i) + + expect(log.error).toBeCalledWith( + "INVALID_CALLBACK_URL_ERROR", + expect.anything() + ) +}) + it("Allow relative `callbackUrl`", async () => { const { res, log } = await handler( { providers: [] },
25517b731533fix: handle invalid `callbackUrl`
5 files changed · +48 −4
docs/docs/errors.md+7 −3 modified@@ -99,7 +99,7 @@ This is required to store the verification token. Please see the [email provider The Credentials Provider can only be used if JSON Web Tokens are used for sessions. -JSON Web Tokens are used for Sessions by default if you have not specified a database. However, if you are using a database, then Database Sessions are enabled by default and you need to [explicitly enable JWT Sessions](https://next-auth.js.org/configuration/options#session) to use the Credentials Provider. +JSON Web Tokens are used for Sessions by default if you have not specified a database. However, if you are using a database, then Database Sessions are enabled by default and you need to [explicitly enable JWT Sessions](/configuration/options#session) to use the Credentials Provider. If you are using a Credentials Provider, NextAuth.js will not persist users or sessions in a database - user accounts used with the Credentials Provider must be created and managed outside of NextAuth.js. @@ -119,13 +119,17 @@ The default `code_challenge_method` is `"S256"`. This is currently not configura > If the client is capable of using "S256", it MUST use "S256", as S256" is Mandatory To Implement (MTI) on the server. +#### INVALID_CALLBACK_URL_ERROR + +The `callbackUrl` provided was either invalid or not defined. See [specifying a `callbackUrl`](/getting-started/client#specifying-a-callbackurl) for more information. + --- ### Session Handling #### JWT_SESSION_ERROR -https://next-auth.js.org/errors#jwt_session_error JWKKeySupport: the key does not support HS512 verify algorithm +JWKKeySupport: the key does not support HS512 verify algorithm The algorithm used for generating your key isn't listed as supported. You can generate a HS512 key using @@ -161,7 +165,7 @@ Make sure the file is there and the filename is written correctly. #### NO_SECRET -In production, we expect you to define a `secret` property in your configuration. In development, this is shown as a warning for convenience. [Read more](https://next-auth.js.org/configuration/options#secret) +In production, we expect you to define a `secret` property in your configuration. In development, this is shown as a warning for convenience. [Read more](/configuration/options#secret) #### oauth_callback_error expected 200 OK with body but no body was returned
packages/next-auth/src/core/errors.ts+5 −0 modified@@ -63,6 +63,11 @@ export class UnsupportedStrategy extends UnknownError { code = "CALLBACK_CREDENTIALS_JWT_ERROR" } +export class InvalidCallbackUrl extends UnknownError { + name = "InvalidCallbackUrl" + code = "INVALID_CALLBACK_URL_ERROR" +} + type Method = (...args: any[]) => Promise<any> export function upperSnake(s: string) {
packages/next-auth/src/core/index.ts+2 −1 modified@@ -108,7 +108,8 @@ export async function NextAuthHandler< let signinUrl = `${pages.signIn}${ pages.signIn.includes("?") ? "&" : "?" }callbackUrl=${encodeURIComponent(options.callbackUrl)}` - if (error) signinUrl = `${signinUrl}&error=${encodeURIComponent(error)}` + if (error) + signinUrl = `${signinUrl}&error=${encodeURIComponent(error)}` return { redirect: signinUrl, cookies } }
packages/next-auth/src/core/lib/assert.ts+33 −0 modified@@ -4,7 +4,10 @@ import { MissingAuthorize, MissingSecret, UnsupportedStrategy, + InvalidCallbackUrl, } from "../errors" +import parseUrl from "../../lib/parse-url" +import { defaultCookies } from "./cookie" import type { NextAuthHandlerParams } from ".." import type { WarningCode } from "../../lib/logger" @@ -18,6 +21,14 @@ type ConfigError = let twitterWarned = false +function isValidHttpUrl(url: string) { + try { + return /^https?:/.test(new URL(url).protocol) + } catch { + return false + } +} + /** * Verify that the user configured `next-auth` correctly. * Good place to mention deprecations as well. @@ -44,8 +55,30 @@ export function assertConfig( } } + const callbackUrlParam = req.query?.callbackUrl as string | undefined + + if (callbackUrlParam && !isValidHttpUrl(callbackUrlParam)) { + return new InvalidCallbackUrl( + `Invalid callback URL. Received: ${callbackUrlParam}` + ) + } + if (!req.host) return "NEXTAUTH_URL" + const url = parseUrl(req.host) + + const { callbackUrl: defaultCallbackUrl } = defaultCookies( + options.useSecureCookies ?? url.base.startsWith("https://") + ) + const callbackUrlCookie = + req.cookies?.[options.cookies?.callbackUrl?.name ?? defaultCallbackUrl.name] + + if (callbackUrlCookie && !isValidHttpUrl(callbackUrlCookie)) { + return new InvalidCallbackUrl( + `Invalid callback URL. Received: ${callbackUrlCookie}` + ) + } + let hasCredentials, hasEmail let hasTwitterOAuth2
packages/next-auth/src/core/lib/cookie.ts+1 −0 modified@@ -68,6 +68,7 @@ export function defaultCookies(useSecureCookies: boolean): CookiesOptions { callbackUrl: { name: `${cookiePrefix}next-auth.callback-url`, options: { + httpOnly: true, sameSite: "lax", path: "/", secure: useSecureCookies,
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-g5fm-jp9v-2432ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-31093ghsaADVISORY
- github.com/nextauthjs/next-auth/commit/25517b73153332d948114bacdff3b5908de91d85ghsax_refsource_MISCWEB
- github.com/nextauthjs/next-auth/commit/e498483b23273d1bfc81be68339607f88d411bd6ghsax_refsource_MISCWEB
- github.com/nextauthjs/next-auth/security/advisories/GHSA-g5fm-jp9v-2432ghsax_refsource_CONFIRMWEB
- next-auth.js.org/configuration/initializationghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.