diff --git a/docs/docs/errors.md b/docs/docs/errors.md index f9f99d15bd..076c2b7993 100644 --- a/docs/docs/errors.md +++ b/docs/docs/errors.md @@ -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 diff --git a/packages/next-auth/src/core/errors.ts b/packages/next-auth/src/core/errors.ts index 84a45255fd..1270da9f0b 100644 --- a/packages/next-auth/src/core/errors.ts +++ b/packages/next-auth/src/core/errors.ts @@ -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 export function upperSnake(s: string) { diff --git a/packages/next-auth/src/core/index.ts b/packages/next-auth/src/core/index.ts index 933e7ac672..ab73c0f224 100644 --- a/packages/next-auth/src/core/index.ts +++ b/packages/next-auth/src/core/index.ts @@ -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 } } diff --git a/packages/next-auth/src/core/lib/assert.ts b/packages/next-auth/src/core/lib/assert.ts index 597c2321c7..8f5a3f3e3c 100644 --- a/packages/next-auth/src/core/lib/assert.ts +++ b/packages/next-auth/src/core/lib/assert.ts @@ -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 diff --git a/packages/next-auth/src/core/lib/cookie.ts b/packages/next-auth/src/core/lib/cookie.ts index 8cb6532b9c..1805121a95 100644 --- a/packages/next-auth/src/core/lib/cookie.ts +++ b/packages/next-auth/src/core/lib/cookie.ts @@ -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,