mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
Compare commits
4 Commits
@auth/dyna
...
fix/improv
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f42ef7cc62 | ||
|
|
35a708a2c3 | ||
|
|
4f919ca76f | ||
|
|
036e34b4b6 |
@@ -1,14 +1,23 @@
|
||||
interface ErrorCause extends Record<string, unknown> {}
|
||||
|
||||
/** @internal */
|
||||
export class AuthError extends Error {
|
||||
metadata?: Record<string, unknown>
|
||||
constructor(message: Error | string, metadata?: Record<string, unknown>) {
|
||||
constructor(message: string | Error | ErrorCause, cause?: ErrorCause) {
|
||||
if (message instanceof Error) {
|
||||
super(message.message)
|
||||
this.stack = message.stack
|
||||
} else super(message)
|
||||
this.name = this.constructor.name
|
||||
this.metadata = metadata
|
||||
super(undefined, {
|
||||
cause: { err: message, ...(message.cause as any), ...cause },
|
||||
})
|
||||
} else if (typeof message === "string") {
|
||||
if (cause instanceof Error) {
|
||||
cause = { err: cause, ...(cause.cause as any) }
|
||||
}
|
||||
super(message, cause)
|
||||
} else {
|
||||
super(undefined, message)
|
||||
}
|
||||
Error.captureStackTrace?.(this, this.constructor)
|
||||
this.name =
|
||||
message instanceof AuthError ? message.name : this.constructor.name
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +37,45 @@ export class AdapterError extends AuthError {}
|
||||
/** @todo */
|
||||
export class AuthorizedCallbackError extends AuthError {}
|
||||
|
||||
/** @todo */
|
||||
/**
|
||||
* There was an error while trying to finish up authenticating the user.
|
||||
* Depending on the type of provider, this could be for multiple reasons.
|
||||
*
|
||||
* :::tip
|
||||
* Check out `[auth][details]` in the error message to know which provider failed.
|
||||
* @example
|
||||
* ```sh
|
||||
* [auth][details]: { "provider": "github" }
|
||||
* ```
|
||||
* :::
|
||||
*
|
||||
* For an **OAuth provider**, possible causes are:
|
||||
* - The user denied access to the application
|
||||
* - There was an error parsing the OAuth Profile:
|
||||
* Check out the provider's `profile` or `userinfo.request` method to make sure
|
||||
* it correctly fetches the user's profile.
|
||||
* - The `signIn` or `jwt` callback methods threw an uncaught error:
|
||||
* Check the callback method implementations.
|
||||
*
|
||||
* For an **Email provider**, possible causes are:
|
||||
* - The provided email/token combination was invalid/missing:
|
||||
* Check if the provider's `sendVerificationRequest` method correctly sends the email.
|
||||
* - The provided email/token combination has expired:
|
||||
* Ask the user to log in again.
|
||||
* - There was an error with the database:
|
||||
* Check the database logs.
|
||||
*
|
||||
* For a **Credentials provider**, possible causes are:
|
||||
* - The `authorize` method threw an uncaught error:
|
||||
* Check the provider's `authorize` method.
|
||||
* - The `signIn` or `jwt` callback methods threw an uncaught error:
|
||||
* Check the callback method implementations.
|
||||
*
|
||||
* :::tip
|
||||
* Check out `[auth][cause]` in the error message for more details.
|
||||
* It will show the original stack trace.
|
||||
* :::
|
||||
*/
|
||||
export class CallbackRouteError extends AuthError {}
|
||||
|
||||
/** @todo */
|
||||
@@ -93,3 +140,10 @@ export class UnsupportedStrategy extends AuthError {}
|
||||
|
||||
/** @todo */
|
||||
export class UntrustedHost extends AuthError {}
|
||||
|
||||
/**
|
||||
* The user's email/token combination was invalid.
|
||||
* This could be because the email/token combination was not found in the database,
|
||||
* or because it token has expired. Ask the user to log in again.
|
||||
*/
|
||||
export class Verification extends AuthError {}
|
||||
|
||||
@@ -133,7 +133,8 @@ export async function handleLogin(
|
||||
// with is already associated with another user, then we cannot link them
|
||||
// and need to return an error.
|
||||
throw new AccountNotLinked(
|
||||
"The account is already associated with another user"
|
||||
"The account is already associated with another user",
|
||||
{ provider: account.provider }
|
||||
)
|
||||
}
|
||||
// If there is no active session, but the account being signed in with is already
|
||||
@@ -193,7 +194,8 @@ export async function handleLogin(
|
||||
// want to link them in case it's not safe to do so, so instead we prompt the user
|
||||
// to sign in via email to verify their identity and then link the accounts.
|
||||
throw new AccountNotLinked(
|
||||
"Another account already exists with the same e-mail address"
|
||||
"Another account already exists with the same e-mail address",
|
||||
{ provider: account.provider }
|
||||
)
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { handleLogin } from "../callback-handler.js"
|
||||
import { CallbackRouteError } from "../../errors.js"
|
||||
import { CallbackRouteError, Verification } from "../../errors.js"
|
||||
import { handleOAuth } from "../oauth/callback.js"
|
||||
import { createHash } from "../web.js"
|
||||
import { handleAuthorized } from "./shared.js"
|
||||
@@ -8,7 +8,6 @@ import type { AdapterSession } from "../../adapters.js"
|
||||
import type {
|
||||
RequestInternal,
|
||||
ResponseInternal,
|
||||
User,
|
||||
InternalOptions,
|
||||
} from "../../types.js"
|
||||
import type { Cookie, SessionStore } from "../cookie.js"
|
||||
@@ -154,9 +153,13 @@ export async function callback(params: {
|
||||
const token = query?.token as string | undefined
|
||||
const identifier = query?.email as string | undefined
|
||||
|
||||
// If these are missing, the sign-in URL was manually opened without these params or the `sendVerificationRequest` method did not send the link correctly in the email.
|
||||
if (!token || !identifier) {
|
||||
return { redirect: `${url}/error?error=configuration`, cookies }
|
||||
const e = new TypeError(
|
||||
"Missing token or email. The sign-in URL was manually opened without token/identifier or the link was not sent correctly in the email.",
|
||||
{ cause: { hasToken: !!token, hasEmail: !!identifier } }
|
||||
)
|
||||
e.name = "Configuration"
|
||||
throw e
|
||||
}
|
||||
|
||||
const secret = provider.secret ?? options.secret
|
||||
@@ -166,10 +169,10 @@ export async function callback(params: {
|
||||
token: await createHash(`${token}${secret}`),
|
||||
})
|
||||
|
||||
const invalidInvite = !invite || invite.expires.valueOf() < Date.now()
|
||||
if (invalidInvite) {
|
||||
return { redirect: `${url}/error?error=Verification`, cookies }
|
||||
}
|
||||
const hasInvite = !!invite
|
||||
const expired = invite ? invite.expires.valueOf() < Date.now() : undefined
|
||||
const invalidInvite = !hasInvite || expired
|
||||
if (invalidInvite) throw new Verification({ hasInvite, expired })
|
||||
|
||||
// @ts-expect-error -- Verified in `assertConfig`.
|
||||
const profile = await getAdapterUserFromEmail(identifier, adapter)
|
||||
@@ -252,33 +255,22 @@ export async function callback(params: {
|
||||
} else if (provider.type === "credentials" && method === "POST") {
|
||||
const credentials = body
|
||||
|
||||
let user: User | null
|
||||
|
||||
try {
|
||||
// TODO: Forward the original request as is, instead of reconstructing it
|
||||
// TODO: Forward the original request as is, instead of reconstructing it
|
||||
Object.entries(query ?? {}).forEach(([k, v]) =>
|
||||
url.searchParams.set(k, v)
|
||||
)
|
||||
const user = await provider.authorize(
|
||||
credentials,
|
||||
// prettier-ignore
|
||||
Object.entries(query ?? {}).forEach(([k, v]) => url.searchParams.set(k, v))
|
||||
user = await provider.authorize(
|
||||
credentials,
|
||||
// prettier-ignore
|
||||
new Request(url, { headers, method, body: JSON.stringify(body) })
|
||||
)
|
||||
if (!user) {
|
||||
return {
|
||||
status: 401,
|
||||
redirect: `${url}/error?${new URLSearchParams({
|
||||
error: "CredentialsSignin",
|
||||
provider: provider.id,
|
||||
})}`,
|
||||
cookies,
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
new Request(url, { headers, method, body: JSON.stringify(body) })
|
||||
)
|
||||
if (!user) {
|
||||
return {
|
||||
status: 401,
|
||||
redirect: `${url}/error?error=${encodeURIComponent(
|
||||
(e as Error).message
|
||||
)}`,
|
||||
redirect: `${url}/error?${new URLSearchParams({
|
||||
error: "CredentialsSignin",
|
||||
provider: provider.id,
|
||||
})}`,
|
||||
cookies,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,11 +21,21 @@ const reset = "\x1b[0m"
|
||||
export const logger: LoggerInstance = {
|
||||
error(error: AuthError) {
|
||||
const url = `https://errors.authjs.dev#${error.name.toLowerCase()}`
|
||||
console.error(error.stack)
|
||||
console.error(
|
||||
`${red}[auth][error][${error.name}]${reset}: Read more at ${url}`
|
||||
`${red}[auth][error][${error.name}]${reset}:${
|
||||
error.message ? ` ${error.message}.` : ""
|
||||
} Read more at ${url}`
|
||||
)
|
||||
error.metadata && console.error(JSON.stringify(error.metadata, null, 2))
|
||||
if (error.cause) {
|
||||
const { err, ...data } = error.cause as any
|
||||
console.error(`${red}[auth][cause]${reset}:`, (err as Error).stack)
|
||||
console.error(
|
||||
`${red}[auth][details]${reset}:`,
|
||||
JSON.stringify(data, null, 2)
|
||||
)
|
||||
} else if (error.stack) {
|
||||
console.error(error.stack.replace(/.*/, "").substring(1))
|
||||
}
|
||||
},
|
||||
warn(code) {
|
||||
const url = `https://errors.authjs.dev#${code}`
|
||||
|
||||
Reference in New Issue
Block a user