nextauthjs / next-auth

Authentication for the Web.
https://authjs.dev
ISC License
22.7k stars 3.03k forks source link

TypeScript error for the Credentials provider #2701

Closed dragoncorf closed 2 years ago

dragoncorf commented 2 years ago

Description 🐜

The changes to Providers in [...nextauth].ts in V4 appered a TypeScript error for the Credentials provider.

Type 'CredentialsConfig<{ username: { label: string; type: string; }; password: { label: string; type: string; }; }>' is not assignable to type 'Provider'.
  Type 'CredentialsConfig<{ username: { label: string; type: string; }; password: { label: string; type: string; }; }>' is not assignable to type 'CredentialsConfig<{}>'.
    Types of property 'authorize' are incompatible.
      Type '(credentials: Record<"username" | "password", string>, req: NextApiRequest) => Awaitable<Omit<User, "id"> | { id?: string | undefined; } | null>' is not assignable to type '(credentials: Record<never, string>, req: NextApiRequest) => Awaitable<Omit<User, "id"> | { id?: string | undefined; } | null>'.
        Types of parameters 'credentials' and 'credentials' are incompatible.
          Type 'Record<never, string>' is missing the following properties from type 'Record<"username" | "password", string>': username, password

Is this a bug in your own project?

No

How to reproduce β˜•οΈ

import bcrypt from 'bcryptjs';
import prisma from 'lib/prisma';
import NextAuth from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';

export default NextAuth({
  providers: [
    CredentialsProvider({
      name: 'Credentials',
      credentials: {
        username: { label: 'Username', type: 'text' },
        password: { label: 'Password', type: 'password' }
      },
      async authorize(credentials: any, req) {
        const user = await prisma.user.findUnique({
          where: {
            username: credentials.username
          }
        });

        if (!user) {
          return null;
        }

        const valid = await bcrypt.compare(credentials.password, user.password);

        if (!valid) {
          console.log(`Credentials not valid`);
          return null;
        }

        if (user) {
          return { ...user, email: user.username };
        }
        return null;
      }
    })
  ]
});

Screenshots / Logs πŸ“½

No response

Environment πŸ–₯

System: OS: Windows 10 10.0.19042 CPU: (8) x64 Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz Memory: 3.44 GB / 11.81 GB Binaries: Node: 14.15.4 - D:\Node\node.EXE npm: 6.14.10 - D:\Node\npm.CMD Browsers: Edge: Spartan (44.19041.1023.0), Chromium (93.0.961.38) Internet Explorer: 11.0.19041.906 npmPackages: next: ^10.2.3 => 10.2.3 next-auth: ^4.0.0-beta.2 => 4.0.0-beta.2 react: ^17.0.2 => 17.0.2

Contributing πŸ™ŒπŸ½

No, I am afraid I cannot help regarding this

balazsorban44 commented 2 years ago

This is a follow-up issue from #2677.

Unfortunately, I still cannot reproduce it, so it's probably a misconfiguration somewhere.

Make sure/double-check that your node_modules/next-auth/package.json contains at least version 4.0.0-beta.2.

Here is how it looks locally for me. Note, I also removed the any, as it wasn't needed:

(Small suggestion, if you don't use req, just don't declare it.)

You could try creating a CodeSandbox or a reproduction repo if you still think it is an error, but I have a strong feeling it is something with the version being incorrect in node_modeules.

I'll close this, for now, but if you can prove the error in a reproducible way, I will gladly reopen it.

Rhym commented 2 years ago

@balazsorban44 Can you please show how you're importing the provider?

balazsorban44 commented 2 years ago

I literally pasted the code from above. πŸ˜…

You ask me for a reproduction of a working example? πŸ˜„ I'll create one, fine.

Rhym commented 2 years ago

Haha, I swear I must be doing something dumb.

Screen Shot 2021-09-09 at 09 44 18

balazsorban44 commented 2 years ago

Here it is: https://stackblitz.com/edit/nextjs-hdxwhs?file=pages/api/auth/[...nextauth].ts

Make sure you actually use beta.2!

Rhym commented 2 years ago

Here's a reproducible issue: https://codesandbox.io/s/charming-feynman-0p5gf?file=/src/pages/api/auth/%5B...nextauth%5D.ts

balazsorban44 commented 2 years ago

Now that just doesn't make sense to my brain... πŸ‘€

Rhym commented 2 years ago

Me too man, me too πŸ˜‚

balazsorban44 commented 2 years ago

Maybe a difference in the tsconfig.json causing this?

I have to go now, but please keep digging :pray:

Update: it's the strict flag. next-auth is being developed with strict: false.

If I turn it to true, other stuff might break too... (counted 62 errors when I ran tsc) This might be a bigger issue then. For now, you could set strict: false, and the error will go away. I'll have to investigate this further and decide what we should do. :thinking:

Rhym commented 2 years ago

The issue is when the tsconfig.json has strict mode turned on: "strict":true,

Rhym commented 2 years ago

Sweet man, thank you for all your time working with me on this. For now I'll set strict to false, but would be awesome to support strict mode too :) But totally get if that would be a ballache.

balazsorban44 commented 2 years ago

I will close this in favour of #2709.

The current fix for this issue is to turn off strict mode in tsconfig.json.

copleykj commented 2 years ago

As an update to this just in case anyone else comes across the problem, upgrading from 4.0.0-beta.4 to 4.0.0-beta.7 fixed this issue. It actually created another though lol.

balazsorban44 commented 2 years ago

it did not, see my answer here https://github.com/nextauthjs/next-auth/issues/2709#issuecomment-973183324

r4881t commented 2 years ago

I'm facing something similar it seems. Asked here at SO https://stackoverflow.com/questions/70990262/how-to-write-authorize-function-correctly-in-typescript-for-nextauthjs

TimMTech commented 1 year ago

Nextjs with its 4,555,332,233 issue. Keep it up Vercel!

coycoylaniba commented 1 year ago

does anyone have a fix for this? im also getting this error.

gerardmarquinarubio commented 1 year ago

Bumping this, still an issue as of May 2023. My workaround was to simply type the return as any:

CredentialsProvider({
      name: "Credentials",
      credentials: { 
        email: { label: "Username", type: "text", placeholder: "johndoe@domain.com" },
        password: { label: "Password", type: "password", placeholder: "verysecurepassword" },
      },
      async authorize (credentials) {
        try {
          if (!credentials) throw new Error("no credentials to log in as");
          const { email, password } = credentials;
          const {
            salt,
            password: hashedPassword,
            ...user
          } = await client.user.findUniqueOrThrow({
            where: {
              email,
            },
          });
          const logged = await comparePassword(password, salt, hashedPassword);
          if (!logged) throw new Error("invalid credentials");
          return user as any; // fix for https://github.com/nextauthjs/next-auth/issues/2701
        } catch (ignored) {
          return null;
        }
      }
    })
aayushsingh7 commented 1 year ago

Can anyone tell me what's wrong with my code i have tried everything even the ( "strict":false ) but it still doesn't work

` CredentialsProvider({
        name: "Credentials",
        async authorize(credentials, req) {
          if (!credentials) {
            throw new Error("Credentials are required");
          }
          const isUserExists = await User.findOne({ email: credentials.email });
          if (!isUserExists) {
           throw new Error("No user found")
          }
          const checkPassword = await bcryptjs.compare(
            credentials.password,
            isUserExists.password
          );
          if (!checkPassword || isUserExists.email !== credentials.email) {
            throw new Error("Invalid credentials")
          }
          return isUserExists
        },
      })`
gerardmarquinarubio commented 1 year ago

Can anyone tell me what's wrong with my code i have tried everything even the ( "strict":false ) but it still doesn't work

` CredentialsProvider({
        name: "Credentials",
        async authorize(credentials, req) {
          if (!credentials) {
            throw new Error("Credentials are required");
          }
          const isUserExists = await User.findOne({ email: credentials.email });
          if (!isUserExists) {
           throw new Error("No user found")
          }
          const checkPassword = await bcryptjs.compare(
            credentials.password,
            isUserExists.password
          );
          if (!checkPassword || isUserExists.email !== credentials.email) {
            throw new Error("Invalid credentials")
          }
          return isUserExists
        },
      })`

For one you forgot the credentials field in the provider, in my comment that's just above yours you've got a working example with prisma too.

Issaminu commented 1 year ago

@balazsorban44 Any updates on this?

Meagle89 commented 1 year ago
interface Credentials extends Record<"username" | "password", string> {}

  async authorize(credentials?: Credentials): Promise<LoggedInUser | null> {
        if (!credentials) return null;

fixed it for me

michael-lloyd-morris commented 9 months ago

Instead of changing the strict setting in the tsconfig I highly recommend just using tsignore

  providers: [
    // @ts-ignore
    CredentialsProvider({
bs-spasici commented 8 months ago

can someone reopen this issue please? there are still a lot of people wanting to pass custom user data from the authorize method. also i got the same barrier when needing to pass the custom data to the session. why are we forced into this basic type again? export interface DefaultUser { id: string name?: string | null email?: string | null image?: string | null }

itihask56 commented 7 months ago

You can get rid of this kind of error in two ways: //1 in tsconfig.json file, add "noImplicitAny " :false,

{ "compilerOptions": { // ... other properties "noImplicitAny": false } }

//2

in tsconfig.json file, set "strict " :false,

{ "compilerOptions": { // ... other properties "strict": false } }

Yohannfra commented 6 months ago

Instead of changing the strict setting in the tsconfig I highly recommend just using tsignore

  providers: [
    // @ts-ignore
    CredentialsProvider({
      /* eslint-disable-next-line @typescript-eslint/ban-ts-comment*/
      // @ts-ignore
      async authorize(credentials, req) {
      ...

Works like a charm ! πŸ‘

AnimeAllstar commented 3 months ago

why has this been closed if the issue still exists when "strict": true?

cwiik commented 3 months ago

why has this been closed if the issue still exists when "strict": true?

thinking same..

zllWarMachinellz commented 3 months ago

The solution I provided for this issue encountered with Next.js was as follows:

async authorize(credentials): Promise<any> i used prisma and nextAuth "version": "4.24.5"

Magimart commented 3 months ago

This solution works without turning strict to false ==== strict: true; I have tested on Nextjs 13.5.4 and node 18x , the deployment node version18x at vercel.

simply combine the types

type CombineRequest = Request & NextApiRequest; type CombineResponse = Response & NextApiResponse; interface Credentials{ email: string; password: string; } async function auth(req: CombineRequest, res: CombineResponse){

return await NextAuth(req, res, {
    session: {
        strategy: "jwt",
    },
    providers: [
        CredentialProviders({
           // @ts-ignore
           async authorize(credentials: Credentials): Promise<any>{
             /** your logic */
           }
        })
    ]
})

};

JoseChavez98 commented 3 months ago

It is terrible that this is still happening. Does NextAuth have no support at all? I'm changing libraries just because of how bad this is for a Big company's product support. Switching to Lucia

Edit by maintainer bot: Comment was automatically minimized because it was considered unhelpful. (If you think this was by mistake, let us know). Please only comment if it adds context to the issue. If you want to express that you have the same problem, use the upvote πŸ‘ on the issue description or subscribe to the issue for updates. Thanks!

jaydave1412 commented 2 months ago

I have this config that is working but it should not

import nextAuth from "next-auth/next";
import { AuthOptions } from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";

export const authOptions: AuthOptions = {
  providers: [
    CredentialsProvider({
      credentials: {
        email: {},
        password: {},
      },
      async authorize(credentials) {
        const user = { id: "hello", name: "jay", password: "dave" };
        if (!user || !user.password) return null;

        const passwordsMatch = user.password === credentials?.password;

        if (passwordsMatch) return user;
        return null;
      },
    }),
  ],
};

export default nextAuth(authOptions);

how I decided to add id, I looked into the types,

this is how credentials config interface is defined

export interface CredentialsConfig<
  C extends Record<string, CredentialInput> = Record<string, CredentialInput>
> extends CommonProviderOptions {
  type: "credentials"
  credentials: C
  authorize: (
    credentials: Record<keyof C, string> | undefined,
    req: Pick<RequestInternal, "body" | "query" | "headers" | "method">
  ) => Awaitable<User | null>
}

as you can see it returns awaitable user and user is defined like this

export interface DefaultUser {
  id: string
  name?: string | null
  email?: string | null
  image?: string | null
}

/**
 * The shape of the returned object in the OAuth providers' `profile` callback,
 * available in the `jwt` and `session` callbacks,
 * or the second parameter of the `session` callback, when using a database.
 *
 * [`signIn` callback](https://next-auth.js.org/configuration/callbacks#sign-in-callback) |
 * [`session` callback](https://next-auth.js.org/configuration/callbacks#jwt-callback) |
 * [`jwt` callback](https://next-auth.js.org/configuration/callbacks#jwt-callback) |
 * [`profile` OAuth provider callback](https://next-auth.js.org/configuration/providers#using-a-custom-provider)
 */
export interface User extends DefaultUser {}

as you can see password is not part of User in the interface then why is it working

randhirsingh0578 commented 1 month ago

import NextAuth, { NextAuthOptions } from "next-auth"; import CredentialsProvider from "next-auth/providers/credentials";

const authOptions: NextAuthOptions = { providers: [ CredentialsProvider({ type: "credentials", credentials: {}, authorize(credentials:any, req:any) { const user = { id: "1", name: "J Smith", email: "jsmith@example.com" } if (user) { return user } else { return null } } }), ], };

export default NextAuth(authOptions);

randhirsingh0578 commented 1 month ago

please try my above code in for the [...nextauth].tsx this works