mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
Compare commits
4 Commits
@auth/soli
...
fix/improv
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f42ef7cc62 | ||
|
|
35a708a2c3 | ||
|
|
4f919ca76f | ||
|
|
036e34b4b6 |
@@ -1,14 +1,23 @@
|
|||||||
|
interface ErrorCause extends Record<string, unknown> {}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export class AuthError extends Error {
|
export class AuthError extends Error {
|
||||||
metadata?: Record<string, unknown>
|
constructor(message: string | Error | ErrorCause, cause?: ErrorCause) {
|
||||||
constructor(message: Error | string, metadata?: Record<string, unknown>) {
|
|
||||||
if (message instanceof Error) {
|
if (message instanceof Error) {
|
||||||
super(message.message)
|
super(undefined, {
|
||||||
this.stack = message.stack
|
cause: { err: message, ...(message.cause as any), ...cause },
|
||||||
} else super(message)
|
})
|
||||||
this.name = this.constructor.name
|
} else if (typeof message === "string") {
|
||||||
this.metadata = metadata
|
if (cause instanceof Error) {
|
||||||
|
cause = { err: cause, ...(cause.cause as any) }
|
||||||
|
}
|
||||||
|
super(message, cause)
|
||||||
|
} else {
|
||||||
|
super(undefined, message)
|
||||||
|
}
|
||||||
Error.captureStackTrace?.(this, this.constructor)
|
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 */
|
/** @todo */
|
||||||
export class AuthorizedCallbackError extends AuthError {}
|
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 {}
|
export class CallbackRouteError extends AuthError {}
|
||||||
|
|
||||||
/** @todo */
|
/** @todo */
|
||||||
@@ -93,3 +140,10 @@ export class UnsupportedStrategy extends AuthError {}
|
|||||||
|
|
||||||
/** @todo */
|
/** @todo */
|
||||||
export class UntrustedHost extends AuthError {}
|
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
|
// with is already associated with another user, then we cannot link them
|
||||||
// and need to return an error.
|
// and need to return an error.
|
||||||
throw new AccountNotLinked(
|
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
|
// 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
|
// 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.
|
// to sign in via email to verify their identity and then link the accounts.
|
||||||
throw new AccountNotLinked(
|
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 {
|
} else {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { handleLogin } from "../callback-handler.js"
|
import { handleLogin } from "../callback-handler.js"
|
||||||
import { CallbackRouteError } from "../../errors.js"
|
import { CallbackRouteError, Verification } from "../../errors.js"
|
||||||
import { handleOAuth } from "../oauth/callback.js"
|
import { handleOAuth } from "../oauth/callback.js"
|
||||||
import { createHash } from "../web.js"
|
import { createHash } from "../web.js"
|
||||||
import { handleAuthorized } from "./shared.js"
|
import { handleAuthorized } from "./shared.js"
|
||||||
@@ -8,7 +8,6 @@ import type { AdapterSession } from "../../adapters.js"
|
|||||||
import type {
|
import type {
|
||||||
RequestInternal,
|
RequestInternal,
|
||||||
ResponseInternal,
|
ResponseInternal,
|
||||||
User,
|
|
||||||
InternalOptions,
|
InternalOptions,
|
||||||
} from "../../types.js"
|
} from "../../types.js"
|
||||||
import type { Cookie, SessionStore } from "../cookie.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 token = query?.token as string | undefined
|
||||||
const identifier = query?.email 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) {
|
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
|
const secret = provider.secret ?? options.secret
|
||||||
@@ -166,10 +169,10 @@ export async function callback(params: {
|
|||||||
token: await createHash(`${token}${secret}`),
|
token: await createHash(`${token}${secret}`),
|
||||||
})
|
})
|
||||||
|
|
||||||
const invalidInvite = !invite || invite.expires.valueOf() < Date.now()
|
const hasInvite = !!invite
|
||||||
if (invalidInvite) {
|
const expired = invite ? invite.expires.valueOf() < Date.now() : undefined
|
||||||
return { redirect: `${url}/error?error=Verification`, cookies }
|
const invalidInvite = !hasInvite || expired
|
||||||
}
|
if (invalidInvite) throw new Verification({ hasInvite, expired })
|
||||||
|
|
||||||
// @ts-expect-error -- Verified in `assertConfig`.
|
// @ts-expect-error -- Verified in `assertConfig`.
|
||||||
const profile = await getAdapterUserFromEmail(identifier, adapter)
|
const profile = await getAdapterUserFromEmail(identifier, adapter)
|
||||||
@@ -252,13 +255,11 @@ export async function callback(params: {
|
|||||||
} else if (provider.type === "credentials" && method === "POST") {
|
} else if (provider.type === "credentials" && method === "POST") {
|
||||||
const credentials = body
|
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
|
||||||
// prettier-ignore
|
Object.entries(query ?? {}).forEach(([k, v]) =>
|
||||||
Object.entries(query ?? {}).forEach(([k, v]) => url.searchParams.set(k, v))
|
url.searchParams.set(k, v)
|
||||||
user = await provider.authorize(
|
)
|
||||||
|
const user = await provider.authorize(
|
||||||
credentials,
|
credentials,
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
new Request(url, { headers, method, body: JSON.stringify(body) })
|
new Request(url, { headers, method, body: JSON.stringify(body) })
|
||||||
@@ -273,15 +274,6 @@ export async function callback(params: {
|
|||||||
cookies,
|
cookies,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
return {
|
|
||||||
status: 401,
|
|
||||||
redirect: `${url}/error?error=${encodeURIComponent(
|
|
||||||
(e as Error).message
|
|
||||||
)}`,
|
|
||||||
cookies,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @type {import("src").Account} */
|
/** @type {import("src").Account} */
|
||||||
const account = {
|
const account = {
|
||||||
|
|||||||
@@ -21,11 +21,21 @@ const reset = "\x1b[0m"
|
|||||||
export const logger: LoggerInstance = {
|
export const logger: LoggerInstance = {
|
||||||
error(error: AuthError) {
|
error(error: AuthError) {
|
||||||
const url = `https://errors.authjs.dev#${error.name.toLowerCase()}`
|
const url = `https://errors.authjs.dev#${error.name.toLowerCase()}`
|
||||||
console.error(error.stack)
|
|
||||||
console.error(
|
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) {
|
warn(code) {
|
||||||
const url = `https://errors.authjs.dev#${code}`
|
const url = `https://errors.authjs.dev#${code}`
|
||||||
|
|||||||
Reference in New Issue
Block a user