Stock with express passport LocalStrategy test, probably on mocking request.
test('should Test password Login auth', async (done) => {
const response = jest.fn((arg)=> console.log('args', arg));
const next = jest.fn();
let mockReq = {body: { username: "test#gmail.com", password: "tets"}}
let mockRes = {send: function(ats){console.log("mockResFunc", ats), response()}};
passport.authenticate('local', ()=> response())(mockReq, mockRes);
expect(next).not.toHaveBeenCalled();
expect(response).toHaveBeenCalled();
but callback is never called as well as i didn't found password and username goes to passport function. Does anyone ideas how to mock credentials using jest(i think here is clue)?
passport.use(new LocalStrategy(
async function(username, password, done) {
const existingUser = await User.findOne({ 'email' : username })
console.log('credits', username, password, existingUser.email)
if (existingUser) {
let validUsr = existingUser.validPassword(password);
if (existingUser && validUsr) {
console.log('passport',existingUser.email)
return done(null, existingUser);
}
}
return done(null, false, { message: 'Wrong credentials.' });
}
));
Dont write a mock function for something like local strategy, write an actual function
const request = require('supertest')
const app = require('../server/app')
describe('Login', () => {
it('should fail with incorrect credentials', async () => {
const res = await request(app)
.post('/auth/login')
.send({
email: 'dummy',
password: 'demo'
})
expect(res.statusCode).toEqual(401)
expect(res.body).toHaveProperty('message')
})
it('should succeed with correct credentials', async () => {
const res = await request(app)
.post('/auth/login')
.send({
email: 'demo',
password: 'demo'
})
expect(res.statusCode).toEqual(200)
expect(res.body).toEqual({ email: 'demo' })
})
})
describe('Logout', () => {
it('should logout successfully', async () => {
const res = await request(app).post('/auth/logout')
expect(res.statusCode).toEqual(200)
expect(res.body).toEqual({ ok: true })
})
})
Related
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')
})
Loginhandler is the function which evokes after clicking log in button. Below are the pieces of two files. LoginScreen is my login.js file where as Action is my action file. I have made reducer file too, but my focus is to get the username and the password from input field, send it to action file using loginhandler function and on success, opens up my Home Screen and on Error, the Alert pops up.
----------------Login Screen------------
useEffect(() => {
if (error) {
Alert.alert("An Error Occurred!", error, [{ text: "Okay" }]);
}
}, [error]);
const loginHandler = async () => {
let action = authActions.login(
formState.inputValues.username,
formState.inputValues.password
);
setError(null);
setIsLoading(true);
try {
await dispatch(action);
props.navigation.navigate("PostAuth");
} catch (err) {
setError(err);
setIsLoading(false);
}
};
-----------------ACTION FILE-------------------
const axios = require("axios");
export const LOGIN = "LOGIN";
export const login = (username, password) => {
const params = new URLSearchParams();
params.append("username", username);
params.append("password", password);
return async (dispatch) => {enter code here
axios
.post("xyz.com/testApp/api/login.php", params)
.then((response) => {
const res = response.data.response;
const resMsg = res.message;
let preDefinedErrMsg;
if (resMsg !== "success") {
preDefinedErrMsg = "Wrong Credentials";
throw new Error(preDefinedErrMsg);
}
dispatch({
type: LOGIN,
token: "resData.idToken",
userId: "resData.id",
errorMessage: "message",
});
console.log(response);
})
.catch((err) => {
//console.log(err);
});
};
};
Yes I got it solved, by handling error in my action file.
const axios = require("axios");
export const LOGIN = "LOGIN";
export const login = (username, password) => {
const params = new URLSearchParams();
params.append("username", username);
params.append("password", password);
return async (dispatch) => {
await axios
.post("xyz.com/api/login.php", params)
.then((response) => {
const res = response.data.response;
const resMsg = res.message;
let preDefinedMsg;
if (resMsg === "Error") {
preDefinedErrMsg = "Wrong Credentials";
throw new Error(preDefinedErrMsg);
} else if (resMsg === "success") {
preDefinedMsg = "success";
dispatch({
type: LOGIN,
token: "resData.idToken",
userId: "resData.id",
errorMessage: "message",
});
}
})
.catch((error) => {
if (error.message === preDefinedErrMsg) {
throw new Error(preDefinedErrMsg);
}
});
};
};
I want to mock Amplify Auth service in my test. There is no error, but test doesn't work because of my mock.
Here is the code I'm about to test:
signIn(): void {
if (!this.valid) return;
this.loading = 1;
this.$Auth
.signIn(this.email, this.password)
.then(() => this.$router.push({ name: "homeManagement" }))
.catch((err: any) => (this.errorMessage = err.message))
.finally(() => (this.loading = 0));
}
Here is the test:
const $t = jest.fn();
$t.mockReturnValue("");
const $Auth = jest.fn();
$Auth.mockReturnValue({
code: "UserNotFoundException",
name: "UserNotFoundException",
message: "User does not exist."
});
const factory = mountFactory(LoginForm, {
mount: {
mocks: {
$Auth
}
}
});
describe("LoginForm", () => {
it("User not found", async () => {
const wrapper = factory();
await wrapper.setData({
email: "david#gmail.com",
password: "Qwer321"
});
await wrapper.vm.signIn();
expect(wrapper.vm.$data.errorMessage.length).not.toEqual(0);
});
});
Figured out a solution, but maybe there is a better one flush-promises to mock Amplify call:
const $Auth = jest.fn();
$Auth.signIn = () => Promise.resolve();
describe("LoginForm", () => {
it("User does not exist", async () => {
const wrapper = factory();
await wrapper.setData({
email: "david#gmail.com",
password: "Qwer321",
valid: true
});
await wrapper.vm.signIn();
await flushPromises();
expect(wrapper.vm.$data.errorMessage.length).not.toEqual(0);
});
});
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
I am trying to create a post using an app built in react native but everytime I try creating it gives me a 401 error after I have already logged in. I assume it isn't getting a token from AsyncStorage. I need helping.
This is the ItemContext where the functionality for creating a post-
import createDataContext from "./createDataContext";
import sellerApi from "../api/seller";
import { navigate } from "../navigationRef";
const itemReducer = (state, action) => {
switch (action.type) {
case "fetch_items":
return action.payload;
case "create_item":
return { errorMessage: "", item: action.payload };
default:
return state;
}
};
const fetchItems = dispatch => async () => {
const response = await sellerApi.get("/api/items");
console.log(response.data);
dispatch({ type: "fetch_items", payload: response.data });
};
const createItem = dispatch => async (
title,
category,
detail,
condition,
price
) => {
try {
const response = await sellerApi.post("/api/items", {
title,
category,
detail,
condition,
price
});
//this is the other place the error might be happening i need this to save in the phone local storage
console.log(response.data);
dispatch({ type: "create_item", payload: response.data });
navigate("Home");
} catch (err) {
console.log(err);
}
};
export const { Provider, Context } = createDataContext(
itemReducer,
{ createItem, fetchItems },
[]
);
this is the AuthContext where the signin and signup functionality is located and the AsyncStorage is used. Let me know if you guys need to see the node function for Auth.
import createDataContext from "./createDataContext";
import sellerApi from "../api/seller";
import { navigate } from "../navigationRef";
const authReducer = (state, action) => {
switch (action.type) {
case "add_error":
return { ...state, errorMessage: action.payload };
case "signup":
return { errorMessage: "", token: action.payload };
case "signin":
return { errorMessage: "", token: action.payload };
case "fetch_user":
return action.payload;
case "clear_error_message":
return { ...state, errorMessage: "" };
case "signout":
return { token: null, errorMessage: "" };
default:
return state;
}
};
const tryLocalSignin = dispatch => async () => {
const token = await AsyncStorage.getItem("token");
if (token) {
dispatch({ type: "signin", payload: token });
navigate("Home");
} else {
navigate("loginFlow");
}
};
const clearErrorMessage = dispatch => {
dispatch({ type: "clear_error_message" });
};
const signup = dispatch => async ({ name, phone, email, password }) => {
try {
const response = await sellerApi.post("/api/users", {
name,
phone,
email,
password
});
//this is the other place the error might be happening i need this to save in the phone local storage
await AsyncStorage.setItem("token", response.data);
console.log(response.data);
dispatch({ type: "signup", payload: response.data.token });
navigate("Home");
} catch (err) {
dispatch({ type: "add_error", payload: "FAIL" });
}
};
const signin = dispatch => async ({ email, password }) => {
try {
const response = await sellerApi.post("/api/auth", {
email,
password
});
await AsyncStorage.setItem("token", response.data);
console.log(response.data);
dispatch({ type: "signin", payload: response.data.token });
navigate("Home");
} catch (err) {
dispatch({ type: "add_error", payload: "FAIL" });
}
};
// const fetchUser = dispatch => async () => {
// const response = await sellerApi.get("/auth");
// dispatch({ type: "fetch_user", payload: response.data });
// };
//need to get the users info to display it in the accountScreen
const signout = dispatch => async () => {
await AsyncStorage.removeItem("token");
dispatch({ type: "signout" });
navigate("loginFlow");
};
export const { Provider, Context } = createDataContext(
authReducer,
{ signup, signin, signout, tryLocalSignin },
{ token: null, errorMessage: "" }
);
This is the backend for the Auth function that makes sure the user is logged in before begin able to send a post request----
const jwt = require("jsonwebtoken");
const config = require("config");
module.exports = function (req, res, next) {
const token = req.header("x-auth-token");
if (!token) return res.status(401).send("Access denied");
try {
const decoded = jwt.verify(token, config.get("jwtPrivateKey"));
req.user = decoded;
next();
} catch (ex) {
res.status(400).send("Invalid token.");
}
}
this is where the post request for when you signup and login is pretty much similar-
router.post("/", async (req, res) => {
const { error } = validate(req.body);
if (error) return res.status(400).send(error.details[0].message);
let user = await User.findOne({ email: req.body.email });
if (user) return res.status(400).send("User already registered.");
user = new User(_.pick(req.body, "name", "phone", "email", "password"));
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(user.password, salt);
await user.save();
const token = user.generateAuthToken();
res.header("x-auth-token", token).send(token);
});
PLEASE HELP
Importing Async storage like this import {AsyncStorage} from 'react-native'; has been deprecated. You can check here async storage .
Thats why i suppose the AsyncStorage is not working, try downloading this rn-community-async-storage . package first and then import AsyncStorage like
import AsyncStorage from '#react-native-community/async-storage';
hope it helps. feel free for doubts