I'm trying to set up role based authentication in next.js using next-auth and prisma but when I try to call signIn()
function I get a weird error
error - TypeError: "ikm"" must be an instance of Uint8Array or a string
.
The signIn()
function invokes the authorize process, when I enter invalid credentials I get unauthorized
response. But this error occurs only when the credentials are true.
This is next-auth handler using the credentials provider:
// /api/auth/[...nextauth].ts
export default (req: NextApiRequest, res: NextApiResponse) =>
NextAuth(req, res, {
secret: process.env.NEXTAUTH_SECRET,
adapter: PrismaAdapter(prisma),
providers: [
CredentialsProvider({
id: 'admin-login',
name: 'Admin login',
credentials: {
email: {
label: 'Email',
type: 'email',
placeholder: 'test@test.com',
},
password: { label: 'Mot de passe', type: 'password' },
},
authorize: async (credentials, request) => {
const { email, password } = registerSchema.parse(credentials);
const user = await prisma.user.findFirst({
where: {
email,
},
select: {
id: true,
email: true,
password: true,
name: true,
role: true,
},
});
if (!user) throw new Error('Unauthorized.');
if (user.role !== 'ADMIN') throw new Error('Unauthorized.');
const isValid = await verify(user.password, password);
if (!isValid) throw new Error('Invalid Credentials');
return {
id: user.id,
email: user.email,
name: user.name,
role: user.role,
};
},
}),
],
callbacks: {
async signIn({ user, account, profile, email, credentials }) {
return true;
},
async redirect({ url, baseUrl }) {
return url.startsWith(baseUrl)
? Promise.resolve(url)
: Promise.resolve(baseUrl);
},
async jwt({ token, user, account, profile, isNewUser }) {
if (user) {
token.id = user.id;
token.role = user.role;
}
return token;
},
async session({ session, token, user }) {
const sess: Session = {
...session,
user: {
...session.user,
id: token.id as string,
role: token.role as string,
},
};
return sess;
},
},
session: {
strategy: 'jwt',
},
jwt: {
secret: process.env.JWT_SECRET,
maxAge: 30 * 24 * 30 * 60, // 30 days
},
pages: {
signIn: '/auth/login',
signOut: '/api/auth/logout',
newUser: '/api/auth/register',
},
});
And here is the login page:
// /auth/login.tsx
const LoginPage: NextPage<Props> = ({ csrfToken }) => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<ISignUp & { csrfToken: string }>({ resolver: zodResolver(registerSchema) });
const onSubmit = async (values: ISignUp & { csrfToken: string }) => {
try {
const res = await signIn('admin-login', {
redirect: false,
email: values.email,
password: values.password,
});
console.log(res);
} catch (err) {
console.error(err);
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input
{...register('csrfToken')}
type='hidden'
defaultValue={csrfToken}
hidden
/>
<input className='border' {...register('email')} />
<p>{errors.email?.message}</p>
<input className='border' {...register('password')} />
<p>{errors.password?.message}</p>
<button type='submit'>
Submit
</button>
</form>
);
};