NextAuth authentication not working on Deployment on Vercel (Working on localhost) - authentication

Used credentials for authentication with nextauth. My code is not working on vercel deployment but working on localhost.
I used the basic cresdentials for authentication username and password but not able to get the session object after signing up from SignUpPage from the pages folder
[...nextauth].js
export default NextAuth({
session: {
jwt: true,
},
providers: [
Credentials({
async authorize(credentials) {
const client = await MongoClient.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const usersCollection = client.db().collection("users");
const user = await usersCollection.findOne({
username: credentials.username,
});
if (!user) {
client.close();
throw new Error("No user found!");
}
const isValid = await compare(credentials.password, user.password);
if (!isValid) {
client.close();
throw new Error("Could not log you in!");
}
client.close();
return { username: user.username };
},
}),
],
callbacks: {
jwt: async ({ token, user }) => {
if (user) {
token.user = user;
}
return token;
},
session: async ({ session, token }) => {
if (token) {
session.user = token.user;
}
return session;
},
},
secret: process.env.SECRET,
jwt: {
secret: process.env.SECRET,
encryption: true,
},
});
pages/api/auth/signup.js
async function handler(req, res) {
if (req.method !== "POST") {
return;
}
const data = req.body;
const { username, password } = data;
if (!username || !password || password.trim().length < 7) {
res.status(422).json({
message:
"Invalid input - password should also be at least 7 characters long.",
});
return;
}
const client = await MongoClient.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const db = client.db();
const existingUser = await db
.collection("users")
.findOne({ username: username });
if (existingUser) {
res.status(422).json({ message: "User exists already!" });
client.close();
return;
}
const hashedPassword = await hash(password, 12);
const result = await db.collection("users").insertOne({
username: username,
password: hashedPassword,
});
res.status(201).json({ message: "Created user!" });
client.close();
}
export default handler;
pages/SignUpPage.js
useEffect(() => {
getSession().then((session) => {
if (session) {
router.push("/");
} else {
setIsLoading(false);
}
});
}, [router]);
if (isLoading) {
return <p>Loading...</p>;
}
const createUser = async (username, password) => {
const response = await fetch("/api/auth/signup", {
method: "POST",
body: JSON.stringify({ username, password }),
headers: {
"Content-Type": "application/json",
},
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.message || "Something went wrong!");
}
return data;
};
const submitHandle = async (username, password) => {
if (login) {
const result = await signIn("credentials", {
redirect: false,
username: username,
password: password,
});
if (!result.error) {
router.push("/");
}
} else {
try {
const result = await createUser(username, password);
console.log("result", result);
router.push("/");
} catch (error) {
console.log(error);
}
}
};

Looks like you have used environment variables. Make sure you have updated them with relevant values.
for example,
NEXTAUTH_URL=http://localhost:3000
to
NEXTAUTH_URL=https://youdomain.com

Related

Password reset and sign up in vue js 3

I am trying to connect my vue js application to a msal b2c backend the login is done but now i need to make a button for password reset and sign up i cant find a sign up popup anywhere and i dont know how to set up a password reset popup.
When i click the password reset link in the login popup the popup closes and nothing happens after that
The way i currently have it set up is:
LoginView.vue
<template>
<div class="grid grid-cols-12">
<div class="default-container">
<p>u bent nog niet ingelogd log nu in</p>
<button type="button" #click="login">Login</button>
</div>
</div>
</template>
<script setup>
import useAuthStore from '../stores/AuthStore';
function login() {
useAuthStore().login();
}
</script>
Authstore.js
import { defineStore } from 'pinia';
import AuthService from '../services/AuthService';
const clientId = import.meta.env.VITE_APP_CLIENT_ID;
const authority = import.meta.env.VITE_APP_AUTHORITY;
const scopes = [import.meta.env.VITE_APP_SCOPE_READ];
const authService = new AuthService(clientId, authority, scopes);
const useAuthStore = defineStore('AuthStore', {
state: () => ({
isAuthenticated: !!localStorage.getItem('apiToken'),
apiToken: localStorage.getItem('apiToken'),
user: JSON.parse(localStorage.getItem('userDetails')),
error: null,
}),
getters: {
isLoggedIn: (state) => state.isAuthenticated,
currentApiToken: (state) => state.apiToken,
currentUser: (state) => state.user,
currentError: (state) => state.error,
},
actions: {
async login() {
try {
const token = await authService.login();
this.apiToken = token.apiToken;
localStorage.setItem('apiToken', token.apiToken);
this.isAuthenticated = true;
fetch(`${import.meta.env.VITE_APP_API_URL}/account/current`, {
headers: {
Authorization: `Bearer ${token.apiToken}`,
},
})
.then((response) => response.json())
.then((data) => {
localStorage.setItem('userDetails', JSON.stringify(data));
this.user = data;
});
} catch (error) {
this.error = error;
}
},
async logout() {
try {
await authService.logout();
this.user = null;
this.apiToken = null;
this.isAuthenticated = false;
localStorage.removeItem('apiToken');
localStorage.removeItem('userDetails');
} catch (error) {
this.error = error;
}
},
},
});
export default useAuthStore;
AuthService.js
import * as Msal from 'msal';
export default class AuthService {
constructor(clientId, authority, scopes) {
this.app = new Msal.UserAgentApplication({
auth: {
clientId,
authority,
postLogoutRedirectUri: window.location.origin,
redirectUri: window.location.origin,
validateAuthority: false,
},
cache: {
cacheLocation: 'localStorage',
},
});
this.scopes = scopes;
}
async login() {
const loginRequest = {
scopes: this.scopes,
prompt: 'select_account',
};
const accessTokenRequest = {
scopes: this.scopes,
};
let token = {};
try {
await this.app.loginPopup(loginRequest);
} catch (error) {
return undefined;
}
try {
const acquireTokenSilent = await this.app.acquireTokenSilent(accessTokenRequest);
token = {
apiToken: acquireTokenSilent.accessToken,
expiresOn: acquireTokenSilent.expiresOn,
};
} catch (error) {
try {
const acquireTokenPopup = await this.app.acquireTokenPopup(accessTokenRequest);
token = {
apiToken: acquireTokenPopup.accessToken,
expiresOn: acquireTokenPopup.expiresOn,
};
} catch (errorPopup) {
return undefined;
}
}
return token;
}
logout() {
this.app.logout();
}
}

No 'Access-Control-Allow-Origin' header is present on the requested resource (MERN)

This error is never ending, I keep getting it and it's been days I've been trying to find a solution for this annoying error.
Here is what happens when I try to log in.
My app works perfectly fine in localhost but there are alot of issue when I uploaded it to heroku and it is really annoying.
Im using
Axios.defaults.withCredentials = true;
code on my every front end.
My backend
const express = require("express");
const cors = require("cors");
const mongoose = require("mongoose")
const app = express();
const bcrypt = require("bcryptjs")
const saltRounds = 10;
const bodyParser = require("body-parser")
const cookieParser = require("cookie-parser")
const session = require("express-session")
const voterModel = require('./modules/voters.js')
const presidentModel = require('./modules/president.js')
const viceModel = require('./modules/vice.js')
const treasurerModel = require('./modules/treasurer.js')
var MongoDBStore = require('connect-mongodb-session')(session);
app.use(express.json());
const corsOptions = {
origin: 'https://incomparable-speculoos-abdd5f.netlify.app',
//update: or "origin: true," if you don't wanna add a specific one
credentials: true,
};
app.use(cors(corsOptions));
app.options('*', cors());
app.use(cookieParser());
app.use(bodyParser.urlencoded({ extended: true }))
mongoose.connect("hidden",
{
useNewUrlParser: true,
useUnifiedTopology: true
}
)
var store = new MongoDBStore({
uri: 'hidden',
collection: 'sessions'
});
// Catch errors
store.on('error', function(error) {
console.log(error);
});
app.use(session({
secret: "hidden",
resave: false,
store: store,
saveUninitialized: false,
cookie: {
maxAge: 1000 * 60 * 60 * 24
}
}))
app.post('/login', async (req, res) => {
const email = req.body.email;
const password = req.body.password;
voterModel.find({email: email}, {"email":1}, async (err, result) => {
if (err) {
console.log(err)
} else {
if(result.length > 0) {
const user = await voterModel.findOne({email: email})
const pass = await user.comparePassword(password)
if (pass) {
req.session.user = user
} else {
console.log("NOT LOGGED IN")
res.send({ message: 'Invalid email or password!'})
}
} else {
console.log("NOT LOGGED IN")
res.send({ message: 'Invalid email or password!'})
}
}
})
})
app.post('/register', async (req, res) => {
const username = req.body.username;
const email = req.body.email;
const password = req.body.password;
// HASING PASSWORD
bcrypt.hash(password, saltRounds, async (err, hash) => {
if (err) {
console.log(err)
}
// INSERTING VALUES
const voters = await voterModel({email: email, username: username, password: hash, status: false})
// CHECKS IF EMAIL IS IN USE
const isNewEmail = await voterModel.isThisEmailInUse(email)
if (!isNewEmail) return res.send({ message: 'This email is already taken!'})
// SAVES THE INSERT DATA FOR VOTERS
await voters.save()
res.send({success: true})
})
})
app.post('/voted', async (req, res) => {
// FOR UPDATING THE VOTING STATUS
const email = req.body.email
// VARIABLES FOR CHOSEN CANDIDATES OF USER
const president = req.body.president
const vice = req.body.vice
const treasurer = req.body.treasurer
// SETS THE STATUS OF VOTER TO TRUE SO HE/SHE CAN ONLY VOTE ONCE
voterModel.updateOne({email: email}, {$set : {status: true}}, (err, result) => {
if (err) {
console.log(err)
} else {
console.log(result)
}
})
// BELOW ARE THE COMMANDS FOR INCREMENTING THE VOTE COUNT OF SELECTED CANDIDATES OF THE VOTER
presidentModel.updateOne({nickname: president}, {$inc : {votes: 1}}, (err, result) => {
if (err) {
console.log(err)
} else {
console.log(result)
}
})
viceModel.updateOne({nickname: vice}, {$inc : {votes: 1}}, (err, result) => {
if (err) {
console.log(err)
} else {
console.log(result)
}
})
treasurerModel.updateOne({nickname: treasurer}, {$inc : {votes: 1}}, (err, result) => {
if (err) {
console.log(err)
} else {
console.log(result)
}
})
})
app.get('/login', (req, res) => {
if (req.session.user) {
res.send({loggedIn: true, user: req.session.user})
} else {
res.send({loggedIn: false})
}
})
app.post('/checkVote', (req, res) => {
const email = req.body.email
const num = true;
voterModel.find({ $and : [{email: email}, {status : num}]},(err, result) => {
if (err) {
console.log(err)
} else {
console.log(result)
if (result.length > 0) {
res.send( {voted: true } )
} else {
res.send( {voted: false } )
}
}
})
})
app.get("/logout", (req, res) => {
req.session.destroy(err => {
if (err) return next(err)
res.status(200).send('logged out')
})
res.status(200).send('User has been logged out');
});
const PORT = process.env.PORT || 3001
app.listen(PORT, () => {
console.log('running on port 3001')
})

Vue3 Errorhandling in Vuex , how to catch error, when you indirectly dispatch a naction?

auth(){} is to send request to the server. For, authentication ,login and signUp url is different. So I use two actions to assign the url by mode "login/signUp". then "login" and "signUp" will dispatch auth(){}
In the past, I directly put send request function fetch () in login() / signUp(). when error throw out ,I can catch it by use try {} catch{}.
Now, I want to reduct the duplicated code . The problem is , the error will be throw out by auth(), in Vue3 component, what I dispatch is not auth(), it is login() / signUp().
How can I get the error? will it be possible to pass the error through login()/signUp(),then I can get it ?
updated:
auth.vue
async setUser() {
if (this.mode === "sign up") {
if (this.emailIsValid === true && this.passwordsIsValid === true) {
const payload = {
email: this.email,
password: this.password,
};
await this.$store.dispatch("homepage/signUp", payload);
}
} else if (this.mode === "login") {
if (this.emailIsValid === true && this.passwordsIsValid === true) {
this.isLoading = true;
const payload = {
email: this.email,
password: this.password,
};
try {
await this.$store.dispatch("homepage/login", payload);
} catch (err) {
this.isLoading = false;
// console.log(err);
this.error = err.message || "something went wrong";
}
}
}
},
Vuex actions.js
async signUp (context, payload) {
context.dispatch('auth', {
...payload,
mode: 'sign up'
})
},
async login (context, payload) {
await context.dispatch('auth', {
...payload,
mode: 'login'
})
},
async auth (context, payload) {
let url = ''
if (payload.mode === 'sign up') {
url =
'https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=[api key]'
} else if (payload.mode === 'login') {
url =
'https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=[api key]'
}
const res = await fetch(url, {
method: 'POST',
body: JSON.stringify({
email: payload.email,
password: payload.password,
returnSecureToken: true
})
})
const resData = await res.json()
if (!res.ok) {
// console.log(resData)
const error = new Error(resData.error.message || 'Try again later ')
throw error
}
}
}
just pass error the bridge action .
add another try{} catch {}
vuex actions.js
async login (context, payload) {
try {
await context.dispatch('auth', {
...payload,
mode: 'login'
})
} catch (err) {
throw err
}
}

TypeError: User.comparePassword is not a function. Comes up with Postman

I'm trying to get my login component on the server side to work, but it keeps giving me this one error over and over. "TypeError: User.comparePassword is not a function" I had it working in postman before, but I can't figure out why it broke now. I'm making an ecommerce site, so I'm excluding code that has stuff to do with the rest of the site.
[user.js - my routing and where the error is being presented.]
const { User, validateUser } = require("../models/user.js");
const bcrypt = require("bcrypt");
const config = require("config");
const jwt = require("jsonwebtoken");
const auth = require("../middleware/auth");
const express = require("express");
const router = express.Router();
//get users
router.get("/", async (req, res) => {
try {
const users = await User.find();
return res.send(users);
} catch (ex) {
return res.status(500).send(`Internal server Error: ${ex}`);
}
});
//get a user
router.get("/:userId", async (req, res) => {
try {
const user = await User.findById(req.params.userId);
return res.send(user);
} catch (ex) {
return res.status(500).send(`Internal server Error: ${ex}`);
}
});
//new user
router.post("/register", async (req, res) => {
try {
const { error } = validateUser(req.body);
if (error) return res.status(500).send(error.details[0].message);
let user = await User.findOne({ email: req.body.email });
if (user) return res.status(400).send("User already registered.");
const salt = await bcrypt.genSalt(10);
user = new User({
name: req.body.name,
email: req.body.email,
password: await bcrypt.hash(req.body.password, salt),
});
await user.save();
const token = jwt.sign(
{ _id: user._id, name: user.name },
config.get("jwtSecret")
);
return res
.header("x-auth-token", token)
.header("access-control-expose-headers", "x-auth-token")
.send({ _id: user._id, name: user.name, email: user.email });
} catch (ex) {
return res.status(500).send(`InternalServerError:${ex}`);
}
});
router.get("/auth", auth, (req, res) => {
res.status(200).json({
_id: req.user._id,
isAdmin: req.user.role === 0 ? false : true,
isAuth: true,
email: req.user.email,
name: req.user.name,
role: req.user.role,
image: req.user.image,
cart: req.user.cart,
history: req.user.history,
});
});
router.post("/login", (req, res) => {
User.findOne({ email: req.body.email }, (err, user) => {
if (!User)
return res.json({
loginSuccess: false,
message: "Auth failed, email not found",
});
});
User.comparePassword(req.body.password, (err, isMatch) => {
if (!isMatch)
return res.json({ loginSuccess: false, message: "Wrong password" });
user.generateToken((err, user) => {
if (err) return res.status(400).send(err);
res.cookie("w_authExp", User.tokenExp);
res.cookie("w_auth", User.token).status(200).json({
loginSuccess: true,
userId: user._id,
});
});
});
router.get("/logout", auth, (req, res) => {
User.findOneAndUpdate(
{ _id: req.user._id },
{ token: "", tokenExp: "" },
(err, doc) => {
if (err) return res.json({ success: false, err });
return res.status(200).send({
success: true,
});
}
);
});
});
[User.js - Userschema]
const mongoose = require('mongoose');
const Joi = require('joi');
const cors = require('cors');
const config = require('config');
const jwt = require('jsonwebtoken');
const { productSchema } = require('./Product');
const { reviewSchema } = require('./review');
const userSchema = new mongoose.Schema({
name: { type: String, required: true, minlength: 5, maxlength: 50},
email: {type: String, unique: true, required: true, minlength: 5, maxlength: 255},
password: {type: String, required: true, maxlength: 1024, minlength: 5},
timestamp: { type: Date, default: Date.now() },
cart: {type: [productSchema], default: []},
newSalePost: {type: [productSchema], default: []},
review: {type: [reviewSchema], default: []},
image: {type: String, required: true}
});
const User = mongoose.model('User', userSchema);
userSchema.methods.generateAuthToken = function () {
return jwt.sign({_id: this._id, name: this.name, isAdmin: this.isAdmin}, config.get('jwtSecret'));
};
function validateUser(user){
const schema = Joi.object({
name: Joi.string().min(5).max(50).required(),
email: Joi.string().min(5).max(255).required().email(),
password: Joi.string().min(5).max(1024).required(),
});
return schema.validate(user);
}
userSchema.statics.findByToken = function (token, cb) {
var user = this;
jwt.verify(token, 'secret', function (err, decode) {
user.findOne({ "_id": decode, "token": token }, function (err, user) {
if (err) return cb(err);
cb(null, user);
})
})
}
exports.User = User;
exports.validateUser = validateUser;
exports.userSchema = userSchema;

How to properly logout with JWT using Passport Strategies?

I am new to JWT and Passport so I started following a MERN tutorial on Youtube by NoobCoder that deals with authentication and authorization using JWT. I reached the part where the route deals with '/logout' and I get Unauthorized as a reply from Postman. The code so far is exactly the same by the looks of it. Can someone help me understand what is wrong here?
I have attached the code in the bottom. Please let me know if more information is required.
Here is the code:
app.js
const express = require('express');
const app = express();
const cookieParser = require('cookie-parser');
const mongoose = require('mongoose');
app.use(cookieParser());
app.use(express.json());
mongoose.connect('mongodb://localhost:27017/mernauth', {useNewUrlParser: true, useUnifiedTopology: true}, () => {
console.log('Successfully connected to DB');
});
const userRouter = require('./routes/User');
app.use('/user', userRouter);
app.listen(5000, () => {
console.log('express server started');
});
passport.js
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const JwtStrategy = require('passport-jwt').Strategy;
const User = require('./models/User');
const cookieExtractor = req => {
let token = null;
if(req && req.cookies) {
token = req.cookies['access_token'];
}
return token;
}
// Authorization
passport.use(new JwtStrategy({
jwtFromRequest: cookieExtractor,
secretOrKey: 'NoobCoder'
}, (payload, done) => {
User.findById({_id: payload.sub}, (err, user) => {
if(err) {
return done(err, false);
}
if(user) {
return done(null, user);
}
else {
return done(null, false);
}
})
}));
// Authenticated local strategy using username and password
passport.use(new LocalStrategy((username, password, done) => {
User.findOne({username}, (err, user) => {
// Something went wrong with DB
if(err) {
return done(err);
}
// If no user exists; null = no error; false = user does not exist
if(!user) {
return done(null, false);
}
// Check if password is correct; callback cb = done
user.comparePassword(password, done);
});
}));
User.js (route)
const express = require('express');
const userRouter = express.Router();
const passport = require('passport');
const passportConfig = require('../passport');
const JWT = require('jsonwebtoken');
const User = require('../models/User');
const Todo = require('../models/Todo');
const signToken = userID => {
return JWT.sign({
iss: "NoobCoder",
sub: userID
}, "NoobCoder", {expiresIn: "1h"});
}
userRouter.post('/register', (req, res) => {
const {username, password, role} = req.body;
User.findOne({username}, (err, user) => {
if(err) {
res.status(500).json({message: {msgBody: "Error has occured", msgError: true}})
}
if(user) {
res.status(400).json({message: {msgBody: "Username is already taken", msgError: true}})
}
else {
const newUser = new User({username, password, role});
newUser.save(err => {
if(err) {
res.status(500).json({message: {msgBody: "Error has occured", msgError: true}})
}
else {
res.status(201).json({message: {msgBody: "Account Successfully Created", msgError: false}})
}
})
}
})
});
userRouter.post('/login', passport.authenticate('local', {session: false}), (req, res) => {
if(req.isAuthenticated()) {
const {_id, username, role} = req.user;
const token = signToken(_id);
res.cookie('access_token', token, {httpOnly: true, sameSite: true});
res.status(200).json({isAuthenticated: true, user: {username, role}})
}
});
userRouter.get('/logout', passport.authenticate('jwt', {session: false}), (req, res) => {
res.clearCookie('access_token');
res.json({user: {username: '', role: ''}, success: true});
});
module.exports = userRouter;
User.js (model)
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const UserSchema = new mongoose.Schema({
username: {
type: String,
required: true,
min: 6,
max: 15
},
password: {
type: String,
required: true,
},
role: {
type: String,
enum: ['user', 'admin'],
required: true
},
todos: [{type: mongoose.Schema.Types.ObjectId, ref: 'Todo'}]
});
UserSchema.pre('save', function(next) {
if(!this.isModified('password')) {
return next()
}
bcrypt.hash(this.password, 10, (err, passwordHash) => {
if(err) {
return next(err);
}
this.password = passwordHash;
next();
});
});
UserSchema.methods.comparePassword = function(password, cb) {
bcrypt.compare(password, this.password, (err, isMatch) => {
if(err) {
return cb(err);
}
else {
if(!isMatch) {
return cb(null, isMatch)
}
return cb(null, this);
}
})
};
module.exports = mongoose.model('User', UserSchema);
Perhaps the /logout route is unauthorised because the JWT token is not present?
JWT token presence can be verified by ensuring that the cookieExtractor function is returning a token
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
Source