I have used next-auth for Authentication in my next.js project and have deployed it on AWS server using DOcker. I am getting "http://localhost:3000/api/auth/error?error=Something%20went%20wrong!%20Please%20try%20again%20later." in responce of network everytime when I try to login or register and on all the pages where I have uset nextauth.
This is my nextauth code:
[...nextauth.js]
import NextAuth from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';
import { API_BASE_URL } from '../../../utils/constants';
export default NextAuth({
providers: [
CredentialsProvider({
id: 'credentials',
name: 'Busfabrik',
credentials: {
email_id: {
label: 'email',
type: 'email',
},
password: { label: 'Password', type: 'password' }
},
async authorize(credentials, req) {
const payload = {
email_id: credentials.email_id,
password: credentials.password,
isadmin: !!credentials.isadmin
};
const res = await fetch(API_BASE_URL + '/users/login', {
method: 'POST',
body: JSON.stringify(payload),
headers: {
'Content-Type': 'application/json'
},
});
const user = await res.json();
// console.log('user', user);
// If error and we don't have user data, return error
if (!res.ok) {
throw new Error(user.exception);
}
// If no error and we have user data, return it
if (res.ok && user.success) {
return user;
} else {
// Return error if user data could not be retrieved
throw new Error(user.msg);
}
},
}),
// ...add more providers here
],
secret: process.env.JWT_SECRET,
callbacks: {
async jwt({ token, user }) {
// console.log('jwt user', user)
if (user && user.success) {
return {
...token,
accessToken: user.payload.token,
userData: user.payload
};
}
return token;
},
async session({ session, token }) {
// console.log('session', session);
// console.log('session token', token);
session.accessToken = token.accessToken;
session.user = token.userData;
return session;
},
},
theme: {
colorScheme: 'auto', // "auto" | "dark" | "light"
brandColor: '', // Hex color code #33FF5D
logo: '/vercel.svg', // Absolute URL to image
},
// Enable debug messages in the console if you are having problems
debug: process.env.NODE_ENV === 'development',
});
Can someone suggest or have aced the same issue? If Yes, then please guide me on this. I an stuck here from a long time and not able to find the solution.
Related
I'm trying to set the cookie from my express backend to my nextjs front end using the response.setHeader, but it's not working, I get my json response but no cookies are set, I used postman to make some tests and in postman it did set the cookies as it should be.
NextJs version is 13.0.2, express version is 4.18.2 and cookie version is 0.5.0
My express server:
export const loginUser = async (req: Request, res: Response) => {
const { email, password } = req.body;
//limpa os cookies
res.clearCookie("accessToken", { httpOnly: true });
if (email !== "" && password !== "") {
try {
const foundUser: any = await prisma.users.findFirst({
where: {
email,
password,
},
});
try {
const accessToken = jwt.sign(
{ id: foundUser.id, email },
"dspaojdspoadsaodksa",
{
expiresIn: "10s",
}
);
const refreshToken = jwt.sign(
{ id: foundUser.id, email },
"dsaoindsadmnsaosda",
{
expiresIn: "50s",
}
);
const savedRefresh = await prisma.refresh_token.upsert({
where: {
users_id: foundUser.id,
},
update: {
access_token: accessToken,
refresh_tk: refreshToken,
users_id: foundUser.id,
},
create: {
access_expires_in: "",
refresh_expires_in: "",
access_token: accessToken,
refresh_tk: refreshToken,
users_id: foundUser.id,
},
});
res.setHeader(
"Set-Cookie",
cookie.serialize("accessToken", accessToken, {
maxAge: 1000 * 60 * 15, //15 minutes
httpOnly: true, // The cookie only accessible by the web server
})
);
res.json({ accessToken, user: { email }, ok: true });
} catch (error) {
console.log("refresh cant be created");
res.send({ message: "refresh cant be created" });
}
} catch (error) {
res.send({ message: "user not found" });
}
} else {
res.json({ message: "token creation failed" });
}
};
My nextJs front-end:
const handleLogin = async (event: React.SyntheticEvent) => {
event.preventDefault();
const email = (event?.target as any).email.value;
const password = (event?.target as any).password.value;
if (email === "" || password === "") {
setError(true);
return;
}
const data = {
email,
password,
};
const resp = await fetch("http://localhost:5000/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
const userData = await resp.json();
console.log(userData);
};
I am building a nodejs application
I want to save cookies using nodejs
it send the cookie to the browser
but it does not save on the browser storage
export const signin = async (req, res, next) => {
try {
const user = await User.findOne({
$or: [{ phone: req.body.phone }, { username: req.body.username }],
});
if (!user) return res.status(401).json({ msg: "Wrong Credentials" });
const isCorrect = bcrypt.compareSync(req.body.password, user.password); // true
if (!isCorrect) return res.status(401).json({ msg: "Wrong Credentials" });
const token = jwt.sign({ id: user._id, role: user.role }, process.env.JWT);
const { password, ...others } = user._doc;
res
.cookie("access_token", token, {
httpOnly: false,
secure: false,
maxAge: 60 * 60 * 24 * 7,
})
.status(200)
.json(others);
} catch (error) {
next(error);
}
};
Frontend
const get = path => {
const new_url = `${BASE_URL}${path}`;
return axios.get(new_url || {}, {
withCredentials: true,
credentials: "include",
});
};
I have frontend deployed on vercel and backend deployed on heroku. I have given all the required access headers and included withCredentials in frontend but even after that request headers doesn't contain the Cookie header in production even though the application tab of developer tools shows it there. Here is my code for reference.
const origin =
process.env.NODE_ENV === "development"
? "http://localhost:3000"
: "https://stagio.vercel.app";
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Credentials", true);
res.header("Access-Control-Allow-Origin", origin);
res.header(
"Access-Control-Allow-Methods",
"GET,PUT,POST,PATCH,DELETE,UPDATE,OPTIONS"
);
res.header(
"Access-Control-Allow-Headers",
"X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept"
);
res.header("Access-Control-Expose-Headers", "Set-Cookie");
next();
});
app.use(cors({ credentials: true, origin: origin }));
This is how I am setting my cookie
const sendToken = (user, status, res) => {
const token = user.getSignedToken();
const { password, resetExpire, resetToken, ...userObj } = user._doc;
res
.status(status)
.cookie("secureCookie", token, {
// httpOnly: true,
secure: true,
// domain: "herokuapp.com",
sameSite: "None",
// maxAge: 1000000,
})
.json({
success: true,
user: userObj,
});
return token;
};
And this is my frontend call
import { Event, Razorpay } from "#typings/event";
import axios from "axios";
// axios.defaults.withCredentials = true;
export const createEvent = async (
title: string,
description: string,
date: Date,
time: Date,
price: number,
owner: string,
poster: string,
banner?: string,
event_id?: string
): Promise<{ success: string; event: Event }> => {
const res = await axios(`${process.env.BASE_URL}api/event/create`, {
withCredentials: true,
method: event_id ? "PATCH" : "POST",
headers: {
Accept: "application/json",
"Access-Control-Allow-Credentials": "true",
},
data: {
title,
description,
date,
time,
price,
owner,
poster,
banner,
event_id,
},
});
return res.data;
};
I am using next auth for credentials so this is how I am setting the header cookie
import NextAuth, {
NextAuthOptions,
Session,
SessionStrategy,
User,
} from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { login } from "#actions/auth";
import { toast } from "react-toastify";
import { JWT } from "next-auth/jwt";
import { NextApiRequest, NextApiResponse } from "next";
import { SessionToken } from "next-auth/core/lib/cookie";
// For more information on each option (and a full list of options) go to
// https://next-auth.js.org/configuration/options
const nextAuthOptions = (req: NextApiRequest, res: NextApiResponse) => {
return {
providers: [
CredentialsProvider({
name: "Credentials",
credentials: {
email: { label: "Email", type: "text" },
password: { label: "Password", type: "password" },
},
async authorize(
credentials: Record<"email" | "password", string> | undefined,
req
): Promise<Omit<User, "id"> | { id?: string | undefined } | null> {
// Add logic here to look up the user from the credentials supplied
const response = await login(
credentials?.email!,
credentials?.password!
);
const cookies = response.headers["set-cookie"];
console.log(cookies);
res.setHeader("Set-Cookie", cookies);
if (response) {
var user = { ...response.data.user };
return user;
} else {
return null;
}
},
}),
],
refetchInterval: 1 * 24 * 60 * 60,
secret: process.env.NEXTAUTH_SECRET,
debug: true,
session: {
strategy: "jwt" as SessionStrategy,
maxAge: 3 * 24 * 60 * 60,
},
jwt: {
maxAge: 3 * 24 * 60 * 60,
},
callbacks: {
jwt: async ({ token, user }: { token: JWT; user?: User }) => {
user &&
(token.user = {
username: user.username,
email: user.email,
image: user.image,
});
return token;
},
session: async ({ session, token }: { session: Session; token: JWT }) => {
session.user = token.user;
session.token = token.token;
return session;
},
},
};
};
export default (req: NextApiRequest, res: NextApiResponse) => {
return NextAuth(req, res, nextAuthOptions(req, res));
};
Kindly help me figure out my mistake. I have been stuck on it for a month now.
I am trying to implement NextAuth with Credentials (email and password).
I have set up my mongodb for this. I also set up /api/proile/ route to post login credentials and tested it out with Postman, returns user correctly.
But the problem starts with after logging in. when I log in, credentials return in the vscode terminal (I console log in /api/auth/[...nextauth].js with console.log(credentials) but in the browser it authorizes the user, i can access and view protected routes and stuff, but when I log the session in front-end, it displays as null for user information as you can see in the picture below;
here is my /api/auth/[...nextauth].js code;
import NextAuth from 'next-auth';
import Providers from 'next-auth/providers';
import { connectToDatabase } from '../../../util/mongodb';
const options = {
providers: [
Providers.Credentials({
name: 'Credentials',
credentials: {
email: { label: 'Email', type: 'text' },
password: { label: 'Password', type: 'password' },
},
async authorize(credentials, req) {
console.log(credentials);
const res = await fetch('http://localhost:3000/api/profile/', {
method: 'POST',
body: JSON.stringify(credentials),
headers: {
'Content-Type': 'application/json',
},
});
const user = await res.json();
// const user = { id: '1', name: 'Suat Bayrak', email: 'test#test.com2' };
if (user) {
return user;
} else {
return null;
}
},
}),
],
pages: {
signIn: '/signin',
},
session: {
jwt: true,
maxAge: 30 * 24 * 60 * 60,
updateAge: 24 * 60 * 60,
},
database: `mongodb+srv://${process.env.MONGO_DB_USERNAME}:${process.env.MONGO_DB_PASSWORD}#nextjs-academia- sb.ki5vd.mongodb.net/test`,
};
export default (req, res) => NextAuth(req, res, options);
by the way, when I use static user credentials like i commented in the code above, it works perfectly fine and returns session info correctly...
Also here is my /pages/signin.js
import { signIn } from 'next-auth/client';
import { useState } from 'react';
import { useRouter } from 'next/router';
export default function SignIn() {
const router = useRouter();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [loginError, setLoginError] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
console.log('submitted!');
signIn('credentials', {
email: email,
password: password,
// email: 'test#test.com',
// password: '1234',
callbackUrl: 'http://localhost:3000/about',
redirect: false,
}).then(function (result) {
console.log(result);
if (result.error !== null) {
if (result.status === 401) {
setLoginError(
'Your username/password combination was incorrect. Please try again'
);
} else {
setLoginError(result.error);
}
} else {
console.log(result);
router.push(result.url);
}
});
};
return (
<form onSubmit={handleSubmit}>
<label>
Email
<input
name='email'
type='text'
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</label>
<label>
Password
<input
name='password'
type='password'
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</label>
<button type='submit'>Sign in</button>
</form>
);
}
also here is my github repo for this whole code;
GitHub Repo
UPDATE: At first login, user info displays on the terminal but after I refresh the page, it says undefined
import NextAuth from 'next-auth';
import Providers from 'next-auth/providers';
const options = {
providers: [
Providers.Credentials({
name: 'Credentials',
credentials: {
email: { label: 'Email', type: 'text' },
password: { label: 'Password', type: 'password' },
},
async authorize(credentials, req) {
console.log(credentials);
let user;
const res = await fetch('http://localhost:3000/api/profile/', {
method: 'POST',
body: JSON.stringify(credentials),
headers: {
'Content-Type': 'application/json',
},
});
const arrayToJson = await res.json();
user = arrayToJson[0];
if (user) {
return user;
} else {
return null;
}
},
}),
],
pages: {
signIn: '/signin',
},
session: {
jwt: true,
maxAge: 30 * 24 * 60 * 60,
updateAge: 24 * 60 * 60,
},
callbacks: {
async signIn(user) {
return user.userId && user.isActive === '1';
},
async session(session, token) {
session.user = token.user;
return session;
},
async jwt(token, user) {
if (user) token.user = user;
return token;
},
},
database: `mongodb+srv://${process.env.MONGO_DB_USERNAME}:${process.env.MONGO_DB_PASSWORD}#nextjs-academia-sb.ki5vd.mongodb.net/test`,
};
export default (req, res) => NextAuth(req, res, options);
so the problem was at callbacks section of the code. Now its working fine
this my routes code
const Joi = require('joi')
const handler = require('../handler/get-request')
const route = {
method: 'POST',
path: '/IB',
options: {
tags: ['api', 'IB', 'request'],
description: 'Request New User IB',
notes: 'It will return new user IB data',
validate: {
payload: {
data: Joi.object().keys({
cifNo: Joi.string().min(5).max(45).required(),
userId: Joi.string().min(5).max(45).required(),
deviceInfo: Joi.string().min(3).max(45).required()
})
}
},
plugins: {
'hapi-swagger': {
responses: {
'200': {
description: 'Success'
}
}
}
},
auth: 'ib'
},
handler
}
module.exports = route
and this is my code where i registered the plugin hapi-auth-jwt
await server.register(require('hapi-auth-jwt2'))
server.auth.strategy('ib', 'jwt', {
key: config.internalServiceAccessToken,
validate: async function (decoded, request) {
// See https://github.com/dwyl/hapi-auth-jwt2
// provides checking for invalidated token after user logout
request.auth.decoded = decoded
const user = await request.server.methods.services.ib.auth.findUser(decoded.username)
return { isValid: user, credentials: decoded }
},
verifyOptions: { algorithms: ['RS256'] }
})
when i hit my routes, i got this error message, "Error: Missing Authentication".
Can someone explain me why? and help me to fix this error. Big Thanks!