Reputation: 153
I am only using a single CredentialsProvider
in next-auth but I'm confused about how to handle async authorize()
with a custom user interface.
I defined the user interface in types/next-auth.d.ts
as follows:
import NextAuth from "next-auth"
declare module "next-auth" {
interface User {
id: string
address: string
name?: string
}
}
This is the provider definition in [...nextauth].ts
:
CredentialsProvider({
name: "Ethereum",
credentials: {
message: {
label: "Message",
type: "text",
},
signature: {
label: "Signature",
type: "text",
},
},
async authorize(credentials) {
try {
const nextAuthUrl = process.env.NEXTAUTH_URL
if (!nextAuthUrl) return null
if (!credentials) return null
// [verify the credential here]
// "message" contains the verified information
let user = await prisma.user.findUnique({
where: {
address: message.address,
},
})
if (!user) {
user = await prisma.user.create({
data: {
address: message.address,
},
})
}
return {
id: user.id,
address: user.address,
name: user.name
}
} catch (e) {
console.error(e)
return null
}
},
})
Now I see the typescript error in the async authorize(credentials)
Type '(credentials: Record<"message" | "signature", string> | undefined) => Promise<{ id: string; address: string; name: string | null; } | null>' is not assignable to type '(credentials: Record<"message" | "signature", string> | undefined, req: Pick<RequestInternal, "body" | "query" | "headers" | "method">) => Awaitable<...>'.
Type 'Promise<{ id: string; address: string; name: string | null; } | null>' is not assignable to type 'Awaitable<User | null>'.
Type 'Promise<{ id: string; address: string; name: string | null; } | null>' is not assignable to type 'PromiseLike<User | null>'.
Types of property 'then' are incompatible.
Type '<TResult1 = { id: string; address: string; name: string | null; } | null, TResult2 = never>(onfulfilled?: ((value: { id: string; address: string; name: string | null; } | null) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<...>) | ... 1 more ... | unde...' is not assignable to type '<TResult1 = User | null, TResult2 = never>(onfulfilled?: ((value: User | null) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<...>) | null | undefined) => PromiseLike<...>'.
Types of parameters 'onfulfilled' and 'onfulfilled' are incompatible.
Types of parameters 'value' and 'value' are incompatible.
Type '{ id: string; address: string; name: string | null; } | null' is not assignable to type 'User | null'.
Type '{ id: string; address: string; name: string | null; }' is not assignable to type 'User'.
Types of property 'name' are incompatible.
Type 'string | null' is not assignable to type 'string | undefined'.
Type 'null' is not assignable to type 'string | undefined'.
NextAuth with typescript/extend interface
Upvotes: 13
Views: 13870
Reputation: 29
If you are using external API you can modify it like this
Credentials({
authorize: async (credentials) => {
// console.log({ credentials });
// Validated the fields if correct or not
const validateFields = LoginSchema.safeParse(credentials);
if (validateFields.success) {
// If Success check if user valid
const user = await fetch("http://localhost:3000/auth",{
method:"POST",
body:JSON.stringify({
email:validateFields.data.email,
password:validateFields.data.password
}),
headers:{
"Content-type":"application/json"
}
})
// const user = { name: "jay", password: "dave" };
// If user valid return user object
if(user) return user.json()
}
// If not just return null
return null;
},
Just convert user doc to json it will fix type error
Upvotes: 0
Reputation: 789
TL;DR
just adding id field to the returned user solved problem for me
example
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 id is a compulsory field.
Upvotes: 1
Reputation: 89
I managed to fix the issue by typing the return type of the function as Promise
async authorize(credentials, req): Promise<any> {
//code here
}
Upvotes: 7
Reputation: 1077
Got the same issue. Since I didn't want to turn of strict mode in .tsconfig, I simply went with parsing the returned object of authorize()
to any...
async authorize(credentials) {
// ...
return {
// ...
} as any. // <-- This here
}
I don't like it, but I think it's better than turning off strict mode.
Also it doesn't destroy type-safety in the follow up since the next step using the returned type would be in the jwt({user})
callback, and the typing still works there just fine.
Upvotes: 16
Reputation: 302
As stated on GitHub TypeScript error for the Credentials provider #2701 the current fix for this issue is to set strict to false ("strict": false
) in the tsconfig.json file. The reason for this is that NextAuth.js was developed using "strict": false
but they are currently working on it to become compatible with strict set to true.
Upvotes: 7