Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow custom errors to propagate to the client from authorized callback #9099

Closed
amit548 opened this issue Nov 9, 2023 · 18 comments
Closed
Labels
enhancement New feature or request

Comments

@amit548
Copy link

amit548 commented Nov 9, 2023

Environment

System:
OS: Windows 11 10.0.22631
CPU: (12) x64 AMD Ryzen 5 5500U with Radeon Graphics
Memory: 4.76 GB / 15.38 GB
Binaries:
Node: 20.9.0 - C:\Program Files\nodejs\node.EXE
Yarn: 1.22.19 - ~\AppData\Roaming\npm\yarn.CMD
npm: 9.8.1 - C:\Program Files\nodejs\npm.CMD
pnpm: 8.9.2 - ~\AppData\Roaming\npm\pnpm.CMD
Browsers:
Edge: Chromium (119.0.2151.44)
Internet Explorer: 11.0.22621.1

npmPackages:
next: ^14.0.1 => 14.0.1
next-auth: ^5.0.0-beta.3 => 5.0.0-beta.3
react: 18.2.0 => 18.2.0

npmPackages:
@bomdi/codebox: ^2.0.0 => 2.0.0
@editorjs/attaches: ^1.3.0 => 1.3.0
@editorjs/checklist: ^1.5.0 => 1.5.0
@editorjs/code: ^2.8.0 => 2.8.0
@editorjs/delimiter: ^1.3.0 => 1.3.0
@editorjs/editorjs: ^2.28.2 => 2.28.2
@editorjs/embed: ^2.6.0 => 2.6.0
@editorjs/header: ^2.7.0 => 2.7.0
@editorjs/image: ^2.8.2 => 2.8.2
@editorjs/inline-code: ^1.4.0 => 1.4.0
@editorjs/link: ^2.5.0 => 2.5.0
@editorjs/list: ^1.8.0 => 1.8.0
@editorjs/marker: ^1.3.0 => 1.3.0
@editorjs/nested-list: ^1.3.0 => 1.3.0
@editorjs/paragraph: ^2.11.3 => 2.11.3
@editorjs/quote: ^2.5.0 => 2.5.0
@editorjs/raw: ^2.4.0 => 2.4.0
@editorjs/simple-image: ^1.5.1 => 1.5.1
@editorjs/table: ^2.2.2 => 2.2.2
@editorjs/underline: ^1.1.0 => 1.1.0
@editorjs/warning: ^1.3.0 => 1.3.0
@hookform/resolvers: ^3.3.2 => 3.3.2
@nx/cypress: 17.0.3 => 17.0.3
@nx/esbuild: 17.0.3 => 17.0.3
@nx/eslint: 17.0.3 => 17.0.3
@nx/eslint-plugin: 17.0.3 => 17.0.3
@nx/jest: 17.0.3 => 17.0.3
@nx/js: 17.0.3 => 17.0.3
@nx/next: 17.0.3 => 17.0.3
@nx/node: 17.0.3 => 17.0.3
@nx/react: 17.0.3 => 17.0.3
@nx/workspace: 17.0.3 => 17.0.3
@swc-node/register: ~1.6.7 => 1.6.8
@swc/cli: ~0.1.62 => 0.1.62
@swc/core: ~1.3.85 => 1.3.95
@swc/helpers: ~0.5.2 => 0.5.2
@tailwindcss/aspect-ratio: ^0.4.2 => 0.4.2
@tailwindcss/typography: ^0.5.10 => 0.5.10
@tanstack/eslint-plugin-query: ^5.6.0 => 5.6.0
@tanstack/react-query: ^5.7.2 => 5.7.2
@tanstack/react-query-devtools: ^5.7.2 => 5.7.2
@tanstack/react-query-next-experimental: ^5.7.2 => 5.7.2
@testing-library/react: 14.0.0 => 14.0.0
@types/bcrypt: ^5.0.1 => 5.0.1
@types/compression: ^1.7.4 => 1.7.4
@types/cookie-parser: ^1.4.5 => 1.4.5
@types/cors: ^2.8.15 => 2.8.15
@types/express: ~4.17.13 => 4.17.20
@types/hpp: ^0.2.4 => 0.2.4
@types/jest: ^29.4.0 => 29.5.6
@types/morgan: ^1.9.7 => 1.9.7
@types/multer: ^1.4.9 => 1.4.9
@types/node: 18.14.2 => 18.14.2
@types/nodemailer: ^6.4.13 => 6.4.13
@types/react: 18.2.24 => 18.2.24
@types/react-dom: 18.2.9 => 18.2.9
@types/validator: ^13.11.5 => 13.11.5
@typescript-eslint/eslint-plugin: ^5.60.1 => 5.62.0
@typescript-eslint/parser: ^5.60.1 => 5.62.0
autoprefixer: ^10.4.16 => 10.4.16
axios: ^1.6.0 => 1.6.0
babel-jest: ^29.4.1 => 29.7.0
bcrypt: ^5.1.1 => 5.1.1
clsx: ^2.0.0 => 2.0.0
compression: ^1.7.4 => 1.7.4
cookie-parser: ^1.4.6 => 1.4.6
cookies-next: ^4.0.0 => 4.0.0
cors: ^2.8.5 => 2.8.5
cypress: ^13.0.0 => 13.3.3
daisyui: ^3.9.4 => 3.9.4
date-fns: ^2.30.0 => 2.30.0
dotenv: ^16.3.1 => 16.3.1
editorjs-text-color-plugin: ^2.0.4 => 2.0.4
esbuild: ^0.19.2 => 0.19.5
eslint: ~8.46.0 => 8.46.0
eslint-config-next: 13.4.1 => 13.4.1
eslint-config-prettier: ^9.0.0 => 9.0.0
eslint-plugin-cypress: ^2.13.4 => 2.15.1
eslint-plugin-import: 2.27.5 => 2.27.5
eslint-plugin-jsx-a11y: 6.7.1 => 6.7.1
eslint-plugin-react: 7.32.2 => 7.32.2
eslint-plugin-react-hooks: 4.6.0 => 4.6.0
express: ~4.18.1 => 4.18.2
express-mongo-sanitize: ^2.2.0 => 2.2.0
express-rate-limit: ^7.1.3 => 7.1.3
helmet: ^7.0.0 => 7.0.0
hpp: ^0.2.3 => 0.2.3
html-react-parser: ^5.0.5 => 5.0.5
jest: ^29.4.1 => 29.7.0
jest-environment-jsdom: ^29.4.1 => 29.7.0
jest-environment-node: ^29.4.1 => 29.7.0
jose: ^5.0.2 => 5.0.2
lucide-react: ^0.290.0 => 0.290.0
mongoose: ^7.6.3 => 7.6.3
morgan: ^1.10.0 => 1.10.0
multer: ^1.4.5-lts.1 => 1.4.5-lts.1
nanoid: ^5.0.2 => 5.0.2
next: ^14.0.1 => 14.0.1
next-auth: ^5.0.0-beta.3 => 5.0.0-beta.3
next-share: ^0.27.0 => 0.27.0
nodemailer: ^6.9.7 => 6.9.7
nx: 17.0.3 => 17.0.3
postcss: ^8.4.31 => 8.4.31
prettier: ^2.6.2 => 2.8.8
react: 18.2.0 => 18.2.0
react-dom: 18.2.0 => 18.2.0
react-editor-js: ^2.1.0 => 2.1.0
react-hook-form: ^7.47.0 => 7.47.0
react-select: ^5.7.7 => 5.7.7
react-toastify: ^9.1.3 => 9.1.3
sharp: ^0.32.6 => 0.32.6
tailwind-merge: ^2.0.0 => 2.0.0
tailwindcss: ^3.3.5 => 3.3.5
theme-change: ^2.5.0 => 2.5.0
ts-jest: ^29.1.0 => 29.1.1
ts-node: 10.9.1 => 10.9.1
tslib: ^2.3.0 => 2.6.2
typescript: ~5.1.3 => 5.1.6
validator: ^13.11.0 => 13.11.0
xss-clean: ^0.1.4 => 0.1.4
zod: ^3.22.4 => 3.22.4

Reproduction URL

https://github.com/nextauthjs/next-auth-example

Describe the issue

image
image

How can I access login related api error messages like password or email is invalid or etc, in next-auth v4 it works perfectly but in v5 does not work

How to reproduce

After login and If I do input some wrong credentials

Expected behavior

Login related error should be pass to the frontend so user can see the login error

@amit548 amit548 added bug Something isn't working triage Unseen or unconfirmed by a maintainer yet. Provide extra information in the meantime. labels Nov 9, 2023
@shgrth
Copy link

shgrth commented Nov 10, 2023

I've been having this exact problem since migrating to v5. #8999

For some reason, next-auth decided to gatekeep this feature as a "security measure". It makes no sense. I have opened a discussion about this exact issue and it has zero responses. Maybe it's not important to them?

Another thread mentioning the same issue was closed with no resolution.

My company's web portal uses next-auth as auth. We can no longer prompt custom error messages to signIn calls for example when a user account is suspended or requires MFA.

@amit548
Copy link
Author

amit548 commented Nov 10, 2023

I've been having this exact problem since migrating to v5. #8999

For some reason, next-auth decided to gatekeep this feature as a "security measure". It makes no sense. I have opened a discussion about this exact issue and it has zero responses. Maybe it's not important to them?

Another thread mentioning the same issue was closed with no resolution.

My company's web portal uses next-auth as auth. We can no longer prompt custom error messages to signIn calls for example when a user account is suspended or requires MFA.

IDK, what kind of security measure they calculated but this is very important feature, it is required.

@shgrth
Copy link

shgrth commented Nov 10, 2023

I've been having this exact problem since migrating to v5. #8999
For some reason, next-auth decided to gatekeep this feature as a "security measure". It makes no sense. I have opened a discussion about this exact issue and it has zero responses. Maybe it's not important to them?
Another thread mentioning the same issue was closed with no resolution.
My company's web portal uses next-auth as auth. We can no longer prompt custom error messages to signIn calls for example when a user account is suspended or requires MFA.

IDK, what kind of security measure they calculated but this is very important feature, it is required.

This issue was raised back in January 2023 also. #6512
The following comment explains the reason and the commit.

(#6512 (comment))

I think it's a bad decision and a deal-breaker.

@amit548
Copy link
Author

amit548 commented Nov 10, 2023

I've been having this exact problem since migrating to v5. #8999
For some reason, next-auth decided to gatekeep this feature as a "security measure". It makes no sense. I have opened a discussion about this exact issue and it has zero responses. Maybe it's not important to them?
Another thread mentioning the same issue was closed with no resolution.
My company's web portal uses next-auth as auth. We can no longer prompt custom error messages to signIn calls for example when a user account is suspended or requires MFA.

IDK, what kind of security measure they calculated but this is very important feature, it is required.

This issue was raised back in January 2023 also. #6512 The following comment explains the reason and the commit.

(#6512 (comment))

I think it's a bad decision and a deal-breaker.

My mind though the same.

@gnllucena
Copy link

I've commented on #6512 on July still no response from next-auth team.

This library is amazing but having no communication between server side and client side with CredentialsProvider is really a deal breaker.

MFA authentication and First Time Login are really important features that are very hard (impossible AFAIK) to implement without the communication.

@balazsorban44 is there a way to implement this communication with the library? Will the library ever support this features again?

@shgrth
Copy link

shgrth commented Nov 23, 2023

Is there any way to get a response to this issue? Should this post be tagged with "providers"? I would like a confirmation from authjs team if this feature is deprecated forever or will be reverted back to how it was in V4, or a new secure way to throw custom errors to the front-end.

@amit548
Copy link
Author

amit548 commented Nov 23, 2023

Is there any way to get a response to this issue? Should this post be tagged with "providers"? I would like a confirmation from authjs team if this feature is deprecated forever or will be reverted back to how it was in V4, or a new secure way to throw custom errors to the front-end.

Not sure, I am still waiting for some kind of suggestions.

@AhmedBaset

This comment was marked as off-topic.

@amit548

This comment was marked as off-topic.

@balazsorban44 balazsorban44 added enhancement New feature or request and removed bug Something isn't working triage Unseen or unconfirmed by a maintainer yet. Provide extra information in the meantime. labels Jan 25, 2024
@balazsorban44 balazsorban44 changed the title Error messages are logged in the server allow custom errors to propagate to the client Jan 25, 2024
@balazsorban44 balazsorban44 changed the title allow custom errors to propagate to the client allow custom errors to propagate to the client from authorized callback Jan 25, 2024
@balazsorban44
Copy link
Member

balazsorban44 commented Jan 28, 2024

Wanted to let you know that I'm tracking this. I want to come up with a good solution here. Leave your comments! No need for "+1" or "need this" etc. comments, let's keep it effective and constructive. Come with suggestions! (You can just 👍 👎 for a simple opinion)

My initial reasoning was to nudge developers to not send back errors like "invalid password" or "invalid username", which generally is not recommended, as you could let someone enumerate your users, probably other problems with it too. So I just want to make doing this very explicit.

My proposal

Any generic error would still use the "catch-all" error type, but for custom ones that extend AuthorizeError, we would just re-throw.

We already do this actually, if you extend AuthError, but this was never documented:

if (e instanceof AuthError) throw e
const error = new CallbackRouteError(e as Error, { provider: provider.id })
logger.debug("callback route error details", { method, query, body })
throw error

Not all AuthError should end up on the client either though, to obfuscate server details as much as possible. I've been meaning to come back to this here:

// TODO: Filter out some error types from being sent to the client
const params = new URLSearchParams({ error: type })

So we need a specific error type, AuthorizeError, that you would use like so:

import { AuthorizeError } from "next-auth/errors"

class CustomAuthorizeError extends AuthorizeError {
  code = "custom"
}

// ...
authorize() {
  // ...
  throw new CustomAuthorizeError("foo")
}

Then, you would have the following options when you invoke signIn:

Client Components/Pages Router:

  • get redirected to this error page /error?error=AuthorizeError&code=custom
  • if redirect: false was set on signIn, it returns the error and code properties.

Server Actions:

  • await signIn() would throw CustomAuthorizeError that you would need to handle in a catch block.

Let me know what you think about this?

@amit548
Copy link
Author

amit548 commented Feb 8, 2024

Wanted to let you know that I'm tracking this. I want to come up with a good solution here. Leave your comments! No need for "+1" or "need this" etc. comments, let's keep it effective and constructive. Come with suggestions! (You can just 👍 👎 for a simple opinion)

My initial reasoning was to nudge developers to not send back errors like "invalid password" or "invalid username", which generally is not recommended, as you could let someone enumerate your users, probably other problems with it too. So I just want to make doing this very explicit.

My proposal

Any generic error would still use the "catch-all" error type, but for custom ones that extend AuthorizeError, we would just re-throw.

We already do this actually, if you extend AuthError, but this was never documented:

if (e instanceof AuthError) throw e
const error = new CallbackRouteError(e as Error, { provider: provider.id })
logger.debug("callback route error details", { method, query, body })
throw error

Not all AuthError should end up on the client either though, to obfuscate server details as much as possible. I've been meaning to come back to this here:

// TODO: Filter out some error types from being sent to the client
const params = new URLSearchParams({ error: type })

So we need a specific error type, AuthorizeError, that you would use like so:

import { AuthorizeError } from "next-auth/errors"

class CustomAuthorizeError extends AuthorizeError {
  code = "custom"
}

// ...
authorize() {
  // ...
  throw new CustomAuthorizeError("foo")
}

Then, you would have the following options when you invoke signIn:

Client Components/Pages Router:

  • get redirected to this error page /error?error=AuthorizeError&code=custom
  • if redirect: false was set on signIn, it returns the error and code properties.

Server Actions:

  • await signIn() would throw CustomAuthorizeError that you would need to handle in a catch block.

Let me know what you think about this?

Thank you for this.

@amit548 amit548 closed this as completed Feb 8, 2024
@Ahmedelwaafy
Copy link

Wanted to let you know that I'm tracking this. I want to come up with a good solution here. Leave your comments! No need for "+1" or "need this" etc. comments, let's keep it effective and constructive. Come with suggestions! (You can just 👍 👎 for a simple opinion)

My initial reasoning was to nudge developers to not send back errors like "invalid password" or "invalid username", which generally is not recommended, as you could let someone enumerate your users, probably other problems with it too. So I just want to make doing this very explicit.

My proposal

Any generic error would still use the "catch-all" error type, but for custom ones that extend AuthorizeError, we would just re-throw.

We already do this actually, if you extend AuthError, but this was never documented:

if (e instanceof AuthError) throw e
const error = new CallbackRouteError(e as Error, { provider: provider.id })
logger.debug("callback route error details", { method, query, body })
throw error

Not all AuthError should end up on the client either though, to obfuscate server details as much as possible. I've been meaning to come back to this here:

// TODO: Filter out some error types from being sent to the client
const params = new URLSearchParams({ error: type })

So we need a specific error type, AuthorizeError, that you would use like so:

import { AuthorizeError } from "next-auth/errors"

class CustomAuthorizeError extends AuthorizeError {
  code = "custom"
}

// ...
authorize() {
  // ...
  throw new CustomAuthorizeError("foo")
}

Then, you would have the following options when you invoke signIn:

Client Components/Pages Router:

  • get redirected to this error page /error?error=AuthorizeError&code=custom
  • if redirect: false was set on signIn, it returns the error and code properties.

Server Actions:

  • await signIn() would throw CustomAuthorizeError that you would need to handle in a catch block.

Let me know what you think about this?

is this a solution ?
i am trying to do this importing but it results this error
Cannot find module 'next-auth/errors' or its corresponding type declarations.

@AhmedBaset
Copy link
Contributor

is this a solution ? i am trying to do this importing but it results this error Cannot find module 'next-auth/errors' or its corresponding type declarations.

Not released yet.

Follow #9871

@Ahmedelwaafy
Copy link

is this a solution ? i am trying to do this importing but it results this error Cannot find module 'next-auth/errors' or its corresponding type declarations.

Not released yet.

Follow #9871

ok, thank you

@Andrii-Antoniuk
Copy link

@balazsorban44 @A7med3bdulBaset
For error messages currently (v0.28.0) we have the following modification of the message:

const url = `https://errors.authjs.dev#${this.type.toLowerCase()}`;
this.message += `${this.message ? " ." : ""}Read more at ${url}`;

Will it be possible to remove this string from custom errors?

E.g. here I'd like to be able to get a message as it was passed in the constructor

/**
 * @description use this error only with messages that should be accessible on the client
 */
export class ClientMessageError extends AuthError {
  code = CLIENT_AWARE_ERROR_CODE;
}

@Jared-Dahlke
Copy link

yes we really need to be able to return custom error messages. I used authjs to allow users of my app to deploy the app using custom OIDC credentials so that they can use any OIDC compliant provider they want. I need to show the actual error messages they are getting so that they can more easily debug/figure out what scopes/credentias etc they may have wrong or be missing while setting up /deploying the app. Currently the only way to tell is in the dev tools select "preserve log", then view the network calls and look at the error there. I need to be able to pass that error to the UI for them to see.

@ndom91
Copy link
Member

ndom91 commented Apr 18, 2024

@Jared-Dahlke the following should be working in beta.16 now:

import { CredentialsSignin } from "next-auth" 

class InvalidLoginError extends CredentialsSignin {
    code = 'Invalid identifier or password'
}

export const { handlers, auth } = NextAuth({
    providers: [
        CredentialsProvider({
            ....
            async authorize(credentials) {
               throw new InvalidLoginError()
            }
        })
    ]
})

You'll get the custom error code as a query param on the failed signin page you're redirected back to, for example.

@Jared-Dahlke
Copy link

Jared-Dahlke commented Apr 25, 2024

ah thank you @ndom91. But how can I use this in my custom OIDC provider? I am not using CredentialsProvider

import NextAuth from "next-auth";
import type { NextAuthConfig } from "next-auth";
import { jwtDecode } from "jwt-decode";
import { UserSetupStatus, validateGeniusAndGetUserData } from "@/utils";
import type { OIDCConfig } from "@auth/core/providers";


export const config = {
  providers: [
    {
      id: "generic",
      name: "Generic Provider",
      type: "oidc",
      clientId: process.env.CLIENT_ID,
      clientSecret: process.env.CLIENT_SECRET,
      issuer: process.env.ISSUER_BASE_URL,
      checks: ["pkce", "state"], // had to add this for okta
      authorization: {
        params: {
          audience: process.env.AUDIENCE, // had to add this for Auth0
          prompt: "login",
          scope: process.env.AUTH_SCOPE || "openid profile email",
        },
      },
    } satisfies OIDCConfig<any>,
  ],
  trustHost: true,
  secret: process.env.AUTH_SECRET,
  pages: {
    signIn: "/",
  },
  callbacks: {
    async jwt({ token, account, user }) {
     REDACTED
    },
    async session({ session, token, user }) {
     REDACTED
      }
      return newSession;
    },
  },
} satisfies NextAuthConfig;

export const { handlers, auth, signIn, signOut } = NextAuth(config);

Update:
I updated to .16 but haven't made your other changes yet since i am still not sure where to make them. Here is a screenshot of what the UI shows and the server right now:
image

I need to bubble that "PKCE code challenge should be specified in the authorize request for code verification." error message up to the UI

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

9 participants