I am getting this error.
https://next-auth.js.org/errors#oauth_callback_error expected 200 OK, got: 400 Bad Request
Please help me solve it.
THis is my next auth config
const authHandler: NextApiHandler = (req, res) => NextAuth(req, res, options)
export default authHandler
const options: NextAuthOptions = {
providers: [
FacebookProvider({
clientId: process.env.FACEBOOK_CLIENT_ID,
clientSecret: process.env.FACEBOOK_CLIENT_SECRET,
}),
],
callbacks: {
async session({ session, token }) {
if (session?.user) {
session.user.id = token.id
session.user.createdAt = token.createdAt
session.user.role = token.role
}
return session
},
async jwt({ user, token }) {
if (user) {
token.id = user.id
// #ts-ignore
token.createdAt = user.createdAt
// #ts-ignore
token.role = user.role
}
return token
},
},
session: {
strategy: 'jwt',
},
adapter: PrismaAdapter(prisma),
secret: process.env.NEXTAUTH_SECRET,
}
]1
Related
I am using Django and Next.js (Version 13 with the app dir enabled). Now I have two questions:
What is the best practice to deal with the access token I receive after I do the authorize call to the django backend? Is it correct how I put it into the callbacks?
export const authOptions = {
secret: process.env.NEXTAUTH_SECRET,
providers: [
CredentialsProvider({
name: 'Django',
credentials: {
username: { label: "Username", type: "text", placeholder: "mail#domain.com" },
password: { label: "Password", type: "password" }
},
async authorize(credentials, req) {
// Do access call
const resToken = await fetch(process.env.AUTH_ENDPOINT, {
method: 'POST',
body: JSON.stringify(credentials),
headers: { "Content-Type": "application/json" }
})
const jwt_token = await resToken.json()
// fetching user data
const resUser = await fetch(`${process.env.BACKEND_URL}/auth/users/me/`, {
method: 'GET',
headers: { "Content-Type": "application/json",
"Authorization": `JWT ${jwt_token.access}` }
})
const user = await resUser.json()
if (resUser.ok && jwt_token.access) {
user.access_token = jwt_token.access
user.refresh_token = jwt_token.refresh
return user
}
// Return null if user data could not be retrieved
return null
}
})
],
session: {
strategy: "jwt",
},
jwt: { encryption: true, },
callbacks: {
async jwt({ token, user }) {
if (user) {
token.access_token = user.access_token
token.refresh_token = user.refresh_token
console.log("if executed")
}
return token
},
async session({ session, token, user }) {
if (!session) {
session.access_token = user.access_token
session.refresh_token = user.refresh_token
session.user = user
}return session;
},
}
}
export default NextAuth(authOptions)
I have the provider wrapped in the provider.js file as shown below. Now I was wondering if I need to passt the session as <SessionProvider session={session}> in the code below? And if yes - could you tell me how?
'use client'
import { SessionProvider } from 'next-auth/react'
export function Providers({ children }) {
return (
<SessionProvider>
{children}
</SessionProvider>
);
}
Thank you!
I deployed a Nextjs(v13) app with AWS Amplify and using NextAuth(v4.17.0). I'm using CredentialsProvider with a custom server. All works great in development environment, but in production the session callback doesn't fire and the session is empty, even if the token gets created in the database
/page/api/auth/[...nextauth].tsx disregard the console logs lol
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import jwt_decode from "jwt-decode";
import { TokenInfo } from "../../../components/types/auth_types";
async function refreshAccessToken(token) {
try {
console.log("BUT WHY?");
const res = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/token/refresh/`,
{
method: "POST",
body: JSON.stringify({refresh: token.refreshToken}),
headers: {"Content-Type": "application/json"},
}
);
if (!res.ok) throw "refreshError";
const responseJson = await res.json();
return {
...token,
accessToken: responseJson.access,
}
} catch(error) {
return {
...token,
error: "RefreshAccessTokenError",
}
}
}
export const authOptions = {
providers: [
CredentialsProvider({
id: "credentials",
name: "Credentials",
credentials: {
email: { label: "Username", type: "text", placeholder: "" },
password: { label: "Password", type: "password" }
},
async authorize(credentials, req) {
const userCredentials = {
email: credentials.email, password: credentials.password
};
try {
const res = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/token/`,
{
method: "POST",
body: JSON.stringify(userCredentials),
headers: {"Content-Type": "application/json"},
credentials: "include",
}
);
console.log("res", res);
if (res.ok) {
const responseJson = await res.json();
console.log("resJson", responseJson);
const tokenInfo: TokenInfo = jwt_decode(responseJson.access);
console.log("tokenInfo", tokenInfo);
return {
id: tokenInfo.user_id.toString(),
email: tokenInfo.email,
firstName: tokenInfo.first_name,
lastName: tokenInfo.last_name,
isStaff: tokenInfo.is_staff,
accessToken: responseJson.access,
refreshToken: responseJson.refresh,
};
}
return null;
} catch(e) {
return null;
}
}
})
],
callbacks: {
async jwt({ token, account, user }) {
if (account && user) {
console.log("got into token", user);
token.firstName = user.firstName;
token.lastName = user.lastName;
token.refreshToken = user.refreshToken;
token.accessToken = user.accessToken;
}
if (token.accessToken) {
console.log("got in this if instead")
const decodedToken: TokenInfo = jwt_decode(token.accessToken);
if (Date.now() < decodedToken.exp * 1000) {
console.log("got here, returned properly");
return token;
}
}
console.log("got here, not properly, why?");
return await refreshAccessToken(token);
},
async session({ session, token }) {
console.log("getting session");
session.user.firstName = token.firstName;
session.user.lastName = token.lastName;
session.accessToken = token.accessToken;
console.log("sess", session);
return session;
}
},
secret: process.env.NEXT_AUTH_SECRET,
session: {
maxAge: 2 * 24 * 60 * 60, // two days
}
};
export default NextAuth(authOptions);
I have searched as best as I could and couldn't find anything that I didn't do already.
My understanding is I don't need to set session: { strategy: "jwt"} since that's the default.
I have NEXT_AUTH_SECRET="mysecret", NEXT_PUBLIC_API_URL="https://www.backend_domain.com" and NEXTAUTH_URL="https://www.frontend_domain.com" set properly in .env.production and the API calls succeed, as well as the NextAuth calls return 200 status code, no errors in Amplify logs
Edit 1:
If I navigate to /api/auth/signin/credentials and use the default UI for login, the session gets created successfully
i have big problem with next-auth credential-provider when i build my next.js project i get error [next-auth][error][client_fetch_error]
https://next-auth.js.org/errors#client_fetch_error session FetchError: request to http://localhost:3000/api/auth/session failed, reason: connect ECONNREFUSED 127.0.0.1:3000
**** i want to deploy my project local
that is my code
//env.local
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_URL_INTERNAL=http://localhost:3000
SECRET=SYoUMVkWrAVwv+qDJ+E43kmS7uMIJY3R7AS53tLNclk=
MONGODB_URI=mongodb://localhost:27017/healthycrm
//[...nextauth].js
import NextAuth from "next-auth";
import Providers from "next-auth/providers";
import User from "../../../models/mongoModels/user";
import connectDB from "../../../middleware/mondodb";
const handler = NextAuth({
providers: [
Providers.Credentials({
async authorize(credentials) {
const user = await User.findOne({
EmpUserName: credentials.userName,
}).exec();
if (!user) {
throw new Error("No user Found");
}
const cheakPassWord = user.EmpPassword === credentials.password;
if (!cheakPassWord) {
throw new Error("password is wrong");
}
return { name: user.EmpName, email: user.id, image: user.imgPath };
},
}),
],
secret: process.env.SECRET,
jwt: {
secret: process.env.SECRET,
encryption: true,
},
session: {
jwt: true,
maxAge: 1 * 24 * 60 * 60,
},
callbacks: {
async jwt({ token, user }) {
if (user) {
token.id = user.id;
}
return token;
},
async session({ session, token }) {
if (token) {
session.id = token.id;
}
return session;
},
session: async (session, user) => {
const data = await User.findById(user.email).exec();
const premissions = data.Premissions
? data.Premissions.map((x) => x.id)
: [];
const Emps = data.Premissions ? data.Emps.map((x) => x.id) : [];
session.user.jobID = data.EmpJobID;
session.user.MID = data._id;
session.user.List = data.EmpLists;
session.user.AreasQ = data.EmpAreasQ;
session.user.AreasG = data.EmpAreasG;
session.user.EmpJobName = data.EmpJobName;
session.user.EmpDepartID = data.EmpDepartID;
session.user.EmpDepartName = data.EmpDepartName;
session.user.EmpQtayID = data.EmpQtayID;
session.user.EmpGomla = data.EmpGomla;
session.user.directMangerId = data.directMangerId;
session.user.directMangerName = data.directMangerName;
session.user.EmpClassesQ = data.EmpClassesQ;
session.user.EmpClassesG = data.EmpClassesG;
session.user.Premissions = premissions;
session.user.Emps = Emps;
return Promise.resolve(session);
},
},
});
export default connectDB(handler);
I found the following solution: Run npm run dev during build
I'm using an IDP that requires a nonce
I have my nextauth like this (note that i passed my nonce in the authorization step) :
import NextAuth, { NextAuthOptions } from 'next-auth'
const randomString = (length: number) => {
let text = ''
let possible =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
for (let i = 0; i < length; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length))
}
return text
}
const nonce = `nonce${randomString(32)}`
const authOptions: NextAuthOptions = {
providers: [
{
issuer: 'https://fcp.integ01.dev-franceconnect.fr',
id: 'franceconnect',
clientSecret: process.env.FRANCE_CONNECT_SECRET || 'undefined',
clientId: process.env.FRANCE_CONNECT_ID || 'undefined',
name: 'FranceConnect',
type: 'oauth',
idToken: true,
client: {
authorization_signed_response_alg: 'HS256',
id_token_signed_response_alg: 'HS256'
},
authorization: {
url: 'https://fcp.integ01.dev-franceconnect.fr/api/v1/authorize',
params: {
scope: 'openid given_name gender',
nonce,
redirect_uri: `http://localhost:3000/api/auth/callback/franceconnect`,
},
},
token:`https://fcp.integ01.dev-franceconnect.fr/api/v1/token`,
userinfo:
'https://fcp.integ01.dev-franceconnect.fr/api/v1/userinfo',
profile(profile) {
console.log(profile)
return profile
},
},
],
debug: true,
secret: 'hdh-secret',
callbacks: {
async jwt({ token, account }) {
return token
},
async session({ session, token, user }) {
return session
},
},
}
export default NextAuth(authOptions)
I'm having this error :
[next-auth][error][CALLBACK_OAUTH_ERROR]
https://next-auth.js.org/errors#callback_oauth_error nonce mismatch, expected undefined, got: nonceZDBoVu2bD1rRESxh7y4kgZ76A6NiP22e RPError: nonce mismatch, expected undefined, got: nonceZDBoVu2bD1rRESxh7y4kgZ76A6NiP22e
at Client.validateIdToken (C:\Users\Shadow\Documents\Projets\HDH\front\node_modules\openid-client\lib\client.js:784:13)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async Client.callback (C:\Users\Shadow\Documents\Projets\HDH\front\node_modules\openid-client\lib\client.js:487:7)
at async oAuthCallback (C:\Users\Shadow\Documents\Projets\HDH\front\node_modules\next-auth\core\lib\oauth\callback.js:114:16)
at async Object.callback (C:\Users\Shadow\Documents\Projets\HDH\front\node_modules\next-auth\core\routes\callback.js:50:11)
at async NextAuthHandler (C:\Users\Shadow\Documents\Projets\HDH\front\node_modules\next-auth\core\index.js:186:28)
at async NextAuthNextHandler (C:\Users\Shadow\Documents\Projets\HDH\front\node_modules\next-auth\next\index.js:23:19)
at async C:\Users\Shadow\Documents\Projets\HDH\front\node_modules\next-auth\next\index.js:59:32
at async Object.apiResolver (C:\Users\Shadow\Documents\Projets\HDH\front\node_modules\next\dist\server\api-utils\node.js:179:9)
at async DevServer.runApi (C:\Users\Shadow\Documents\Projets\HDH\front\node_modules\next\dist\server\next-server.js:381:9) {
name: 'OAuthCallbackError',
code: undefined
}
If I remove the nonce I got this error from the IDP : {"status":"fail","message":"The following fields are missing or empty : nonce"}
How am I supposed to tell next auth to use a nonce ?
I manage to make it works by doing myself the token and userinfo requests (thanks to request method).
Here is the final code :
providers: [
{
issuer: 'https://fcp.integ01.dev-franceconnect.fr',
id: 'franceconnect',
clientSecret: process.env.FRANCE_CONNECT_SECRET || 'undefined',
clientId: process.env.FRANCE_CONNECT_ID || 'undefined',
name: 'FranceConnect',
type: 'oauth',
authorization: {
url: 'https://fcp.integ01.dev-franceconnect.fr/api/v1/authorize',
params: {
scope: 'openid profile email',
nonce,
redirect_uri: `${process.env.NEXTAUTH_URL}/api/auth/callback/franceconnect`,
},
},
token: {
async request(context) {
const body = {
grant_type: 'authorization_code',
redirect_uri: `${process.env.NEXTAUTH_URL}/api/auth/callback/franceconnect`,
client_id: process.env.FRANCE_CONNECT_ID || 'undefined',
client_secret:
process.env.FRANCE_CONNECT_SECRET || 'undefined',
code: context.params.code || 'undefined',
}
const data = new URLSearchParams(body).toString()
try {
const r = await axios({
method: 'POST',
headers: {
'content-type':
'application/x-www-form-urlencoded',
},
data,
url: `https://fcp.integ01.dev-franceconnect.fr/api/v1/token`,
})
return { tokens: r.data }
} catch (err: any) {
console.error(err)
throw new Error(err)
}
},
},
userinfo: {
url: 'https://fcp.integ01.dev-franceconnect.fr/api/v1/userinfo',
params: { schema: 'openid' },
async request(context) {
const r = await axios({
method: 'GET',
url: 'https://fcp.integ01.dev-franceconnect.fr/api/v1/userinfo?schema=openid',
headers: {
Authorization: `Bearer ${context.tokens.access_token}`,
},
})
return r.data
},
},
profile(profile) {
return {
...profile,
name: `${profile.given_name} ${profile.family_name}`,
id: profile.email,
}
},
},
],
The validate function for basic
await server.register(require('#hapi/basic'));
const validate = async (request, email, password, id_customer) => {
console.log(request)
if (!email || !password || !id_customer) {
return { credentials: null, isValid: false };
}
const results = await getHash(id_customer);
if (results.length == 0) {
return { credentials: null, isValid: false };
}
if (bcrypt.compareSync(password, results[0]['passwd'])) {
const credentials = { id: id_customer, email: email };
return { isValid: true, credentials };
}
return { credentials: null, isValid: false };
};
server.auth.strategy('simple', 'basic', { validate });
Route example :
{
method: 'POST',
path: '/home/getCategories',
config: {
auth: 'simple',
description: 'Get Home',
payload: {
multipart: true
},
handler: Home.getCategories
},
/* options: {
auth: 'simple'
},*/
//handler: Home.getCategories
},
Here is the axios call from the App :
axios.post('https://api.domain.com/home/getCategories', {
code: code
},
{
headers: {
'email': email,
'password': password,
'id_customer': id_customer
},
})
When I do the call I got a 401 unauthorized but I cant see the output of 'console.log(request)'
Any help ?
Have you tried the following? What version of Hapi.js are you using?
const categoryPostValidation = {
payload: Joi.object({
name: Joi.string().label("Name").min(1).max(30).error((errors) => new Error('Name is invalid, and must be 1 to 30 characters in length')).required(),
description: Joi.string().label("Description").min(1).max(255).error((errors) => new Error('Description is invalid, and must be 1 to 255 characters in length')).required()
}),
failAction: async (request, h, err) => {
throw err;
}
};
const categoryPostRouteOptions = {
description: "Posts one category.",
cors: true,
payload: {
output: 'data', // These are default options
parse: true // These are default options
},
auth: {
mode: 'required' // or 'try', etc
strategy: 'simple'
},
validate: categoryPostValidation,
handler: Home.getCategories
};
{
method: 'POST',
path: '/home/getCategories',
options: categoryPostRouteOptions
},