I am successfully authenticating and logging in with the google OAuth API.
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
const passportInit = require('./app/routes/auth')
const session = require('express-session');
module.exports = (app, db, passport) => {
app.use(session({secret: "ahskfjdhkjshadkjfhlajsdhlfkj"}));
passportInit(passport)
app.use(passport.initialize());
app.use(passport.session())
app.get('/', (req, res) => {
if (req.session.token) {
res.cookie('token', req.session.token);
res.json({
status: 'session cookie set'
});
console.log(req.session.token);
console.log(JSON.stringify(req.user))
} else {
res.cookie('token', '')
res.json({
status: 'session cookie not set'
});
}
});
app.get('/auth/google', passport.authenticate('google', {
scope: ['https://www.googleapis.com/auth/userinfo.profile', 'https://www.googleapis.com/auth/youtube']
}));
app.get('/auth/google/callback',
passport.authenticate('google', {
failureRedirect: '/' // Fail: To err Page
}),
(req, res) => {
req.session.token = req.user.token;
userString = JSON.stringify(req.user);
userObjectValue = JSON.parse(userString);
userId = userObjectValue['profile'].id;
userName = userObjectValue['profile'].name;
userGivenName = userName.givenName;
const details = {'userId': userId};
db.collection('users').findOne(details, (err, item) => {
if (item == null) {
res.redirect('http://localhost:80/Register.html');
} else {
if(item['rolle'] == 'yt') {
res.redirect('http://localhost:80/YT_Welcome.html');
} else {
res.redirect('http://localhost:80/WT_Welcome.html');
}
}
});
}
);
app.get('/logout', (req, res) => {
req.logout();
req.session.token = null;
res.redirect('/');
});
}
Now I want to make a POST request from my frontend to my NodeJS backend.
Frontend-Request:
function registerWT() {
console.log('registerWT started...')
var rolle = 'wt';
var json = {"rolle": rolle};
$.ajax({
url: 'http://localhost:8000/user',
type: 'POST',
data: JSON.stringify(json),
contentType: 'application/json; charset=utf-8',
dataType: 'JSON',
async: false,
success: function (msg) {
var js = JSON.stringify(msg);
var state = msg['state'];
if (state == true) {
console.log('successfully created new user')
} else {
console.log('failed to create new user')
}
}
});
Backend-API:
var ObjectID = require('mongodb').ObjectID;
const passport = require('passport');
const passportInit = require('./auth');
module.exports = (app, db) => {
app.post('/user', (req, res) => {
console.log("POST USER REACHED!"); // This is printed out...
console.log(req.body.rolle); //is printed out correctly
console.log(req.user); // Is undefined...
if (!req.isAuthenticated()) { return res.send({'error':'unauthorized'}) } //Authentication fails...
console.log(req.user.userId);
console.log(req.userGivenName);
console.log(req.body.rolle);
userId = req.user.userId;
userGivenName = req.user.userGivenName;
userRolle= req.body.rolle;
const details = { userId: userId, userGivenName: userGivenName, rolle: userRolle };
db.collection('users').insert(details, (err, result) => {
if (err) {
res.send({ 'error': 'An error has occurred' });
} else {
res.send(result.ops[0]);
}
});
});
}
From my understanding the user authentication data should be send automaticly with every request I am doing from my frontend to my backend, since I logged in via google before. Is this correct or do I miss to include something in my frontend JS request code?
What is interessting is that after I logged in, I have to trouble with navigating to /user. So there is no problem with manualy doing a get request to this API, where I am also checking for authentication.
app.get('/user', (req, res) => {
if (!req.isAuthenticated()) { return res.send({'error':'unauthorized'}) }
db.collection('users').find({}).toArray((err, item) => {
if (err) {
res.send({'error':'An error has occurred'});
} else {
res.json(item)
}
});
});
But when I am making a Get request with JavaScript, the authentication fails again...
JavaScript get request:
function getTest() {
console.log('YT');
$.ajax({
url: 'http://localhost:8000/user',
type: 'GET',
async: false,
success: function (msg) {
var state = msg['state']; //nehmen den Wert von state aus der JSON
if (state == true) {
console.log('successfully created new user')
} else {
console.log('failed to create new user')
}
}
});
}
Does someone know what I am doing wrong here?
Edit:
My passportInit:
const GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
const keys = require('../../config/keys')
module.exports = (passport) => {
passport.serializeUser((user, done) => {
done(null, user);
});
passport.deserializeUser((user, done) => {
done(null, user);
});
passport.use(new GoogleStrategy({
clientID: keys.google.clientID,
clientSecret: keys.google.cientSecret,
callbackURL: keys.google.callback
},
(token, refreshToken, profile, done) => {
return done(null, {
profile: profile,
token: token
});
}));
};
Edit2: Added cors package:
const MongoClient = require('mongodb').MongoClient;
const bodyParser = require('body-parser');
const db = require('./config/db');
const keys = require('./config/keys')
const passport = require('passport');
const express = require('express');
const app = express();
const GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
const cors = require('cors')
const port = 8000;
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cors({ origin: 'http://localhost' })); //configuring cors
MongoClient.connect(db.url, (err, database) => {
if (err) return console.log(err)
app.options('*', cors()) // enable pre-flight across-the-board
require('./authenticate')(app, database,passport);
require('./app/routes')(app, database, passport);
app.listen(port, () => {
console.log('We are live on ' + port);
});
});
I finally was able to solve the problem.
No, Browser Plug-in or something like this is needed!
Please see my Code below.
Frontend-Request:
function register() {
var role = 'wt';
var json = {"role": role};
$.ajax({
url: 'http://localhost:8000/user',
type: 'POST',
data: JSON.stringify(json),
contentType: 'application/json; charset=utf-8',
dataType: 'JSON',
xhrFields: {
withCredentials: true
},
crossDomain: true,
async: false,
success: function (msg) {
var state = msg['state'];
if (state == true) {
console.log('successfully created new WT')
location.href = 'http://localhost:80/WT_Welcome.html'
} else {
console.log('failed to create new WT')
location.href = 'http://localhost:80/index.html'
}
}
});
}
Backend Server.js
const MongoClient = require('mongodb').MongoClient;
const bodyParser = require('body-parser');
const db = require('./config/db');
const keys = require('./config/keys')
const passport = require('passport');
const express = require('express');
const app = express();
const GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
const port = 8000;
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
MongoClient.connect(db.url, (err, database) => {
if (err) return console.log(err)
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', "http://localhost");
res.header('Access-Control-Allow-Methods','GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Credentials', true)
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
})
require('./authenticate')(app, database,passport);
require('./app/routes')(app, database, passport);
app.listen(port, () => {
console.log('We are live on ' + port);
});
});
Backend API:
app.post('/user', (req, res) => {
if (!req.isAuthenticated()) { return res.send({'error':'unauthorized'}) } //Authentication fails...
userString = JSON.stringify(req.user);
userObject = JSON.parse(userString)
userId = userObjectValue['profile'].id;
userName = userObjectValue['profile'].name; //not used
userGivenName = userName.givenName;
userRolle= req.body.rolle;
const details = { userId: userId, userGivenName: userGivenName, rolle: userRolle };
console.log(details);
db.collection('users').insert(details, (err, result) => {
if (err) {
res.send({ 'state': false });
} else {
res.send({'state' : true});
}
});
});
Related
I am trying to use express session with react/express frontend and backend. For the express server, I saved the user information in express session in success callback, then I set the credential in the express option and cors to true. However, when I use axios post request from the React frontend with withCredential set to true, req.session.user still shows up as undefined. Does anyone know how to resolve this?
I tried to use middleware, but they were not helpful.
In server.mjs
import express from 'express';
import session from 'express-session';
import mongoose from 'mongoose';
import cors from 'cors';
import User from './db.mjs';
import path from 'path';
import url from 'url';
import bcrypt from 'bcryptjs';
import bodyParser from 'body-parser';
import router from './routes/user.routes.mjs';
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(session({
secret: 'keyboard cat',
cookie: {
maxAge: 600000,
secure: true
},
resave: false,
saveUninitialized: true,
}))
app.use(
cors({
allowedHeaders: ["authorization", "Content-Type"], // you can change the headers
exposedHeaders: ["authorization", 'set-cookie'], // you can change the headers
origin: "*",
credentials: true,
origin: true,
methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
preflightContinue: false
})
);
app.options({
origin: true,
credentials: true,
})
app.use('/public', express.static('public'));
app.use('/api', router)
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: false
}));
// make {{user}} variable available for all paths
app.use((req, res, next) => {
res.locals.user = req.session.user;
next();
});
// logging
app.use((req, res, next) => {
console.log(req.method, req.path, req.body);
next();
});
const login = (email, password, successCallback, errorCallback) => {
// TODO: implement login
const user = User.findOne({'email': email}, function (err, user) {
console.log(err,user)
if (err) {
console.log(err);
errorCallback('USER SEARCH FAILED')
}
else {
if (user === null) {
errorCallback('USER NOT FOUND');
}
else {
bcrypt.compare(password, user.password, (err, passwordMatch) => {
if (err) {
console.log(err);
errorCallback("PASSWORD CHECK FAILED")
}
else {
if (passwordMatch) {
successCallback(user);
}
else {
errorCallback("PASSWORDS DO NOT MATCH");
}
}
});
}
}
});
};
const startAuthenticatedSession = (req, user, cb) => {
// TODO: implement startAuthenticatedSession
console.log('starting new authenticated session', user);
req.session.regenerate(function(err) {
if (!err) {
// set a property on req.session that represents the user
req.session.user = user;
cb(err);
} else {
// call callback with error
cb(err);
}
})
};
const endAuthenticatedSession = (req, cb) => {
// TODO: implement endAuthenticatedSession
req.session.destroy((err) => { cb(err); });
};
const signup = (email, password, errorCallback, successCallback) => {
// TODO: implement register
if (password.length < 8) {
errorCallback('PASSWORD TOO SHORT');
}
User.findOne({'email': email}, function(err, result) {
console.log(result);
if (result === null) {
const hash = bcrypt.hash(password, 10, function(err, hash) {
// do more stuff here!
if (err) {
console.log(err);
errorCallback({message: 'PASSWORD HASH FAILED'})
}
const newUser = new User({
email: email,
password: hash
});
newUser.save(function(err, user, count) {
if (err) {
errorCallback('DOCUMENT SAVE ERROR')
}
successCallback(user);
console.log(req.session.user);
});
});
}
else {
errorCallback('EMAIL ALREADY EXISTS');
}
})
User.findOne({'email': email});
};
app.get("/api", (req, res) => {
res.json({"user": ["userOne", "userTwo", "userThree"]})
})
app.post("/api/login/", (req, res) => {
const email = req.body.email;
const password = req.body.password;
console.log(req.body)// assumes that User was registered in `./db.mjs`
function success(newUser) {
startAuthenticatedSession(req, newUser, (err) => {
if (!err) {
req.session.user = newUser;
res.send({key:'success'});
} else {
res.render('error', {message: 'err authing???'});
}
});
}
login(email, password, success, (e)=>console.log(e));
})
In react frontend component
import '../../App.css';
import React from 'react';
import axios from 'axios';
import icon from '../../img/upload.svg';
axios.defaults.withCredentials = true;
function handleFile(files) {
alert("Number of files: " + files.length);
console.log(files);
const profileImg = 'profileImg';
console.log(files[0].name);
const formData = new FormData();
formData.append(profileImg, files[0]);
axios.post("http://localhost:8000/api/user-profile", formData, {
headers: {
'Content-Type': 'multipart/form-data'
},
withCredentials: true
}).then(res => {
console.log(res);
});
}
console.log for undefined in post request
router.post('/user-profile', upload.single('profileImg'), (req, res, next) => {
const url = req.protocol + '://' + req.get('host')
console.log(req.file);
console.log(req.session.user);
Edit: the console log for req.session:
Session {
cookie: {
path: '/',
_expires: 2022-12-05T04:56:43.020Z,
originalMaxAge: 600000,
httpOnly: true,
secure: true
}
}
I'm trying to create an application which uses JWKS with RSA to sign cookies. I have some issues with the passport strategy I defined, when I try using it, the following error appears:
done(null, jwks.keys[0])
^
TypeError: Cannot read properties of undefined (reading '0')
My code for the passport Strategy I created is the following:
passport.use('jwt-jku', new JwtStrategy({
jwtFromRequest: req => { return (req && req.cookies) ? req.cookies.jwtCookie : null },
secretOrKeyProvider: (req, rawJwtToken, done) => {
const decoded = jwt.decode(rawJwtToken, { complete: true }) // Tiene .header y .payload
if (decoded.header.jku /* && decoded.payload.role === 'admin' */) {
axios({
method: 'get',
url: decoded.header.jku,
responseType: 'json'
}).then(function (jwks) {
console.log(jwks.toString())
done(null, jwks.keys[0])
})
} /* else if(decoded.payload.role !== 'admin') { ESTO NO VA porq no hay 'res'
req.send("You can't access the fortune teller endpoint! Only admin has access to it!")
} */
}
}, (jwtPayload, done) => { return done(null, jwtPayload ?? false) }
))
I use a file called jwks.json that contains:
{"keys":[{"kty":"RSA","kid":"tbrTvJIqhJ7jnYPdQL-LpkPUwqFltyQnWC5QGGUY8JM","use":"sig","alg":"RS256","e":"AQAB","n":"sFTW93A8v-oDFIS7oB2kzZq0RG15Oim9-FyeiTg6QBHcRb4kbTfXf0bQ3dTe-y0mVKUdZUtFyk71DNmI1INpO9y-QU2b-03KIW56As-kR6cmZeDIFwqDjceoUJreLEdEnxkObxuD5d5Qb7Zt8TS1BiRkX5vpiV32VTWpPZs0TglBv-Jdm54r1bvmrwaVUUMMi0DUptKIKLbqysl1j4OIXQSUMjRJF7OFIDfaZqvhPBNXTW7dbvxbfOq46Gpz_EqD2VBlYXOOgfXh8OhhhPkgZnbkq2HSUO1TZoiGFlq6M-Rah64D_8ESsDG2Bvk1Tx7hUCUmiba4AG2am2Gr8qcMmQ","d":"N0NHogHzkCQrtfUJhptzyJpZL-7tZOyrJJkGeLP-EZFnaxY1_WLYb4PGtxddkPPsBPBAn7vxEgAf7diJeSz8QK4klHvZzG9rT4H7zEC-WFSLBLfnjc0WQMuXtTdsNQ1a9M_aBblmY5cCt5hBqaxepZ-nM42-KfagWsE4Gt43DNN0vO7ztotY5TzKGv2R8TIz8BJIYVLVlUwvFheTDNJ97kz6EMR7keTzRcpg6pj-21l0NCLIySkRv4ZEQirW12kek7hgtI0A-mCiU61-GzX0S_MFEGi16Clk0kIQ6Ld2stT26xT01x0-htubgp-KEgW64GXKetOCzXrjZVivSHywPw","p":"vHSS2gMw56CgPyQmrLQNwR4cwL6QC77Zk5IE2QZlRWBDKUwOYioruqoc8t3Nop5xMcFjbNrtEb2RlMDB-lfyfJtcIWo9xh67AMcZ5Zq-FGnJBTjCry8Vf08r9jAfXzvr5dGbFnp4xV2gpuATQ3apBLy9eCkQKOVshdTbqtbKnCM","q":"74fZ4QSOQKqroCqqGnJ-MyyzTL2mSnthDzo7AmwSIZlzkgaqXO4o27uwRJVW2dxEK0XmDKNd94XNflADv1SDhu3mWcDVjkOjnc-zrVgEZjUWG3sABtwUWlSijOkmZG5jD9KPvb1ksvN0K3Xn0wcu8z1XJ9xSm4gw3NXHM2eBEhM","dp":"Zp64BL1uGQNPn3JpGOwm_T0PfSmHuM2hHwmJWEByTvffOpKu1meOTmQpxVpgiqfqjaR4JqOEKn_m0XCxcEgJxC65WXzHzKWZQRAqGuxLi0QbcZt57_kcpKmRD8semjY_-3JLGE9yik60l4o3Eyp_WRQJvG_n280qbin9wCacR68","dq":"skdAqTQE1KiyuLFIjgqtV1Vr1jBJdqJB6V2D0hexnwRhekwvvJ8eFf2qAkHMtLY2nqaVxM5LABG8ksjnIR68Dk8-5Yqa6Bn7y1dt-hTKoW_dlnKD4JcpJtpbadmWU6W9YWdsyOIP8wk0a6yD8RMlpLEBD4_yhARnqdYLP31IGbU","qi":"iIjOxBfcAwzikLRjMFQRfoEcW2RXDzi0xs2c9keut4j1DVgacVlS5J7sDHcJDsJJToHmaasix7-nl7nA5IYn6UiayzU7BqOc0D-orn8HecWWeYQ6hMZ81U80jRHmSN2cp-g4X2NtduxlGw8yoSlAKxQyEpgb5SqXYcDr5hQNgy4"}]}
I don't know what else to do, following I leave the whole code, maybe its easier to understand or something... Thank you!
const express = require('express')
const logger = require('morgan')
const passport = require('passport')
const LocalStrategy = require('passport-local').Strategy
const JwtStrategy = require('passport-jwt').Strategy
const fortune = require('fortune-teller')
const axios = require('axios')
const cookieParser = require('cookie-parser')
const jwt = require('jsonwebtoken')
const fs = require('fs')
const path = require('path')
const jose = require('node-jose') // probar jose
const keyStore = jose.JWK.createKeyStore()
keyStore.generate('RSA', 2048, { alg: 'RS256', use: 'sig' })
.then(result => {
fs.writeFileSync(
path.join(__dirname, 'jwks.json'),
JSON.stringify(keyStore.toJSON(true))
)
// console.log(`Exported JWKSet: ${JSON.stringify(keyStore.toJSON(true), undefined, 2)}`)
})
const port = 3000
const app = express()
app.use(logger('dev'))
app.use(cookieParser())
/*
Configure the local strategy for using it in Passport.
The local strategy requires a `verify` function which receives the credentials
(`username` and `password`) submitted by the user. The function must verify
that the username and password are correct and then invoke `done` with a user
object, which will be set at `req.user` in route handlers after authentication.
*/
passport.use('local', new LocalStrategy(
{
usernameField: 'username', // it MUST match the name of the input field for the username in the login HTML formulary
passwordField: 'password', // it MUST match the name of the input field for the password in the login HTML formulary
session: false // we will store a JWT in the cookie with all the required session data. Our server does not need to keep a session, it's going to be stateless
},
function (username, password, done) {
if (username === 'walrus' && password === 'walrus') {
const user = {
username: 'walrus',
description: 'the only user that deserves to contact the fortune teller'
}
return done(null, user) // the first argument for done is the error, if any. In our case there is no error, and so we pass null. The object user will be added by the passport middleware to req.user and thus will be available there for the next middleware and/or the route handler
}
return done(null, false) // in passport returning false as the user object means that the authentication process failed.
}
))
passport.use('jwt-jku', new JwtStrategy({
jwtFromRequest: req => { return (req && req.cookies) ? req.cookies.jwtCookie : null },
secretOrKeyProvider: (req, rawJwtToken, done) => {
const decoded = jwt.decode(rawJwtToken, { complete: true }) // Tiene .header y .payload
if (decoded.header.jku /* && decoded.payload.role === 'admin' */) {
axios({
method: 'get',
url: decoded.header.jku,
responseType: 'json'
}).then(function (jwks) {
console.log(jwks.toString())
done(null, jwks.keys[0])
})
} /* else if(decoded.payload.role !== 'admin') { ESTO NO VA porq no hay 'res'
req.send("You can't access the fortune teller endpoint! Only admin has access to it!")
} */
}
}, (jwtPayload, done) => { return done(null, jwtPayload ?? false) }
))
app.use(express.urlencoded({ extended: true })) // needed to retrieve html form fields (it's a requirement of the local strategy)
app.use(passport.initialize()) // we load the passport auth middleware to our express application. It should be loaded before any route.
app.get('/', passport.authenticate('jwt-jku', { session: false, failureRedirect: '/login' }),
(req, res) => {
res.send(
"<a href='/'>Refresh</a> / <a href='/logout'>Logout</a><br><br>User: " +
req.user.sub + '<br><br>' + fortune.fortune()
)
}
)
app.get('/login',
(req, res) => {
res.sendFile('login.html', { root: __dirname })
}
)
app.post('/login',
passport.authenticate('local', { failureRedirect: '/login', session: false }),
async (req, res) => {
const [key] = keyStore.all({ use: 'sig' })
const opt = {
compact: true,
jwk: key,
fields: {
typ: 'jwt',
jku: 'http://localhost:3000/jwks.json' // IP server
}
}
const payload = JSON.stringify({
exp: Math.floor(Date.now() / 1000) + 604800, // 1 week (7×24×60×60=604800s) from now
iat: Math.floor(Date.now() / 1000),
role: 'user'
})
const token = await jose.JWS.createSign(opt, key).update(payload).final()
// console.log(`Token sent. Debug at https://jwt.io/?value=${token}`)
res.cookie('jwtCookie', token, { httpOnly: true, secure: false }) // Store the token into the cookie
res.redirect('/')
// res.send(token)
}
)
/* app.get('/user', (req, res) => {
const user = { // Create the json object user with a name and a description
name: 'walrus',
description: 'it is what it is'
}
res.json(user) // Send the json object 'user'
}) */
app.get('/jwks.json', async (req, res) => {
const ks = fs.readFileSync('./jwks.json')
const keyStore = await jose.JWK.asKeyStore(ks.toString())
res.send(keyStore.toJSON())
})
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
Trying to create a login authentication system as an entry into web development but the fetch I have to access my login functionality doesn't work. Morgan shows "POST -- ms --". (Works through Postman). As far as I can see my cors system is set up as expected. The API will respond if the passport.authenticate('local') is removed.
authenticate.js
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var User = require('./models/user');
var JwtStrategy = require('passport-jwt').Strategy;
var ExtractJwt = require('passport-jwt').ExtractJwt;
var jwt = require('jsonwebtoken'); // used to create, sign, and verify tokens
var config = require('./config.js');
exports.local = passport.use(new LocalStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
exports.getToken = function(user) {
return jwt.sign(user, config.secretKey,
{expiresIn: 3600});
};
var opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = config.secretKey;
exports.jwtPassport = passport.use(new JwtStrategy(opts,
(jwt_payload, done) => {
console.log("JWT payload: ", jwt_payload);
User.findOne({_id: jwt_payload._id}, (err, user) => {
if (err) {
return done(err, false);
}
else if (user) {
return done(null, user);
}
else {
return done(null, false);
}
});
}));
routes
var express = require('express');
var UserRouter = express.Router();
var passport = require('passport');
var User = require('../models/user')
var authenticate = require('../authenticate');
const cors = require('./cors');
const bodyParser = require('body-parser');
UserRouter.use(bodyParser.json());
UserRouter.route('/login')
.options(cors.corsWithOptions, (req, res) => { res.sendStatus(200); })
.post(cors.cors, passport.authenticate('local'), (req, res) => {
console.log(req.body);
var token = authenticate.getToken({_id: req.user._id});
res.setHeader('Content-Type', 'application/json');
res.json({success: true, token: token, status: 'You are successfully logged in!'});
res.status(200).send()
});
module.exports = UserRouter;
mongoose schema file
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var passportLocalMongoose = require('passport-local-mongoose');
var User = new Schema ({
firstname : {
type: String,
default: ""
},
lastname: {
type: String,
default: ''
},
admin: {
type: Boolean,
default: false
}
});
User.plugin(passportLocalMongoose);
module.exports = mongoose.model('User', User);
cors file
const express = require('express');
const cors = require('cors');
var whitelist = ['http://localhost:3000', 'http://localhost:3443']
var corsOptionsDelegate = (req, callback) => {
var corsOptions;
console.log("Validating origin");
console.log(req.header('Origin'));
if(whitelist.indexOf(req.header('Origin')) !== -1) {
corsOptions = { origin: true };
}
else {
corsOptions = { origin: false };
}
callback(null, corsOptions);
};
exports.cors = cors();
exports.corsWithOptions = cors(corsOptionsDelegate);
Front end API call
export const loginUser = (creds) => (dispatch) => {
console.log("test")
dispatch(requestLogin(creds))
console.log("attempting login")
return fetch('http://localhost:3443/users/login', {
method: 'POST',
headers: {
'Content-Type':'application/json',
},
body: JSON.stringify(creds)
})
.then(response => {
console.log("got response 1");
if(response.ok) {
return response
} else {
console.log("response errored");
var error = new Error('Error ' + response.status + ': ' + response.statusText);
error.response = response;
throw error;
}
},
error => {
console.log("errored")
throw error;
})
.then(response => response.json())
.then(response => {
console.log("got response 2");
if(response.success) {
// Successful login
localStorage.setItem('token', response.token);
localStorage.setItem('creds', JSON.stringify(creds));
// TODO dispatch success
dispatch(receiveLogin(response));
} else {
var error = new Error('Error ' + response.status);
error.response = response;
throw error;
}
})
.catch(error => dispatch(loginError(error.message)))
}
Does anyone know where I'm going wrong with this? I'm not really getting any useful error messages from my front-end so haven't included.
Terminal output upon login attempt
OPTIONS /users/login 204 3.287 ms - 0
POST /users/login - - ms - -
I am using passport.js specifically the local-strategy for authentication with my next.js application.
Making requests to my data store is fine, and auth, authorization works as well. But I'd like access to the req.user for another route so I can have access to that users._id.
As the passport docs indicate:
By default, if authentication fails, Passport will respond with a 401
Unauthorized status, and any additional route handlers will not be
invoked. If authentication succeeds, the next handler will be invoked
and the req.user property will be set to the authenticated user.
This is my Next.js app's server.js file:
var express = require('express');
require('dotenv').config();
var morgan = require('morgan');
var cookieParser = require('cookie-parser');
var cors = require('cors');
var nextJS = require('next');
var session = require('express-session');
var mongoose = require('mongoose');
var MongoStore = require('connect-mongo')(session);
var path = require('path');
var bodyParser = require('body-parser');
var auth = require('./lib/auth');
var HttpStatus = require('http-status-codes');
var compression = require('compression');
var helmet = require('helmet');
var PORT = process.env.PORT || 8016;
var { isBlockedPage, isInternalUrl } = require('next-server/dist/server/utils');
function NODE_ENVSetter(ENV) {
var environment,
environments = {
production: () => {
environment = process.env.MONGODB_URI;
console.log(`We are currently in the production environment: ${environment}`);
return environment;
},
test: () => {
environment = process.env.TEST_DB_DSN;
console.log(`We are currently in the test environment: ${environment}`);
return environment;
},
default: () => {
environment = process.env.DEVELOPMENT_DB_DSN;
console.log(`We are currently in the development environment: ${environment}`);
return environment;
}
};
(environments[ENV] || environments['default'])();
return environment;
}
var db = NODE_ENVSetter('development');
function start() {
const dev = process.env.NODE_ENV !== 'production';
const app = nextJS({ dev });
const server = express();
// const proxy = createProxyMiddleware(options);
app
.prepare()
.then(() => {
mongoose.connect(db, {
useNewUrlParser: true,
useUnifiedTopology: true
});
mongoose.Promise = global.Promise;
mongoose.connection
.on('connected', () => {
console.log(`Mongoose connection open on ${db}`);
})
.on('error', err => {
console.log(`Connection error: ${err.message}`);
});
})
.catch(err => {
console.error(err);
});
server.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept'
);
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Methods', '*'); // enables all the methods to take place
return next();
});
server.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Credentials', true);
res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept'
);
next();
});
server.use(morgan('dev'));
server.set('view engine', 'html');
server.use(express.static(path.join(__dirname + 'uploads')));
server.use('/uploads', express.static('uploads'));
server.use(cors());
server.use(cookieParser());
server.use(bodyParser.json());
server.use(
session({
secret: 'very secret 12345',
resave: true,
saveUninitialized: false,
store: new MongoStore({ mongooseConnection: mongoose.connection })
})
);
server.use(bodyParser.urlencoded({ limit: '50mb', extended: false }));
server.use(auth.initialize);
server.use(auth.session);
server.use(compression());
server.use(helmet());
server.use('/users', require('./users'));
Here I've included a log to check the req.user object
server.use((req, res, next) => {
console.log('req.user', req.user);
next();
});
// Redirect all requests to main entrypoint pages/index.js
server.get('/*', async (req, res, next) => {
try {
// #NOTE code duplication from here
// https://github.com/zeit/next.js/blob/cc6fe5fdf92c9c618a739128fbd5192a6d397afa/packages/next-server/server/next-server.ts#L405
const pathName = req.originalUrl;
if (isInternalUrl(req.url)) {
return app.handleRequest(req, res, req.originalUrl);
}
if (isBlockedPage(pathName)) {
return app.render404(req, res, req.originalUrl);
}
// Provide react-router static router with a context object
// https://reacttraining.com/react-router/web/guides/server-rendering
req.locals = {};
req.locals.context = {};
const html = await app.renderToHTML(req, res, '/', {});
// Handle client redirects
const context = req.locals.context;
if (context.url) {
return res.redirect(context.url);
}
// Handle client response statuses
if (context.status) {
return res.status(context.status).send();
}
// Request was ended by the user
if (html === null) {
return;
}
app.sendHTML(req, res, html);
} catch (e) {
next(e);
}
});
// eslint-disable-next-line func-names
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
server.all('*', (req, res, next) => {
next(new AppError(`Can't find ${req.originalUrl} on this server!`, 404));
});
if (process.env.NODE_ENV === 'production') {
server.use(express.static('.next/static'));
server.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, '.next/static', 'index.html'));
});
server.listen(PORT, err => {
if (err) throw err;
console.log(
`> Ready and listening on PORT:${PORT} in the ${process.env.NODE_ENV} environment`
);
});
} else {
server.listen(PORT, err => {
if (err) throw err;
console.log(`> Ready and listening on http://localhost:${PORT}`);
});
}
}
start();
And this is my auth file:
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var UserModel = require('../models/UserModel');
passport.use(
new LocalStrategy(
{ usernameField: 'username', passwordField: 'password' },
async (username, password, done) => {
try {
const user = await UserModel.findOne({ username: username }).exec();
if (!user) {
return done(null, false, { message: 'Invalid username!' });
}
const passwordOk = await user.comparePassword(password);
if (!passwordOk) {
return done(null, false, {
message: 'Invalid password!'
});
}
return done(null, user);
} catch (err) {
return done(err);
}
}
)
);
// eslint-disable-next-line no-underscore-dangle
passport.serializeUser((user, done) => done(null, user._id));
passport.deserializeUser(async (id, done) => {
try {
const user = await UserModel.findById(id).exec(); //exec is used to get a real Promise
console.log('user deserialUser', user);
return done(null, user);
} catch (err) {
return done(err);
}
});
module.exports = {
initialize: passport.initialize(),
session: passport.session()
};
And it my /login route:
router.route('/login').post((req, res, next) => {
passport.authenticate('local', (err, user) => {
console.log('user 60', req.user);
console.log('user ', user);
// console.log('res.locals.user ', res.locals.user);
if (!user) {
return res.status(404).send({
msg: [
`We were unable to find this user.`,
`This email and/or password combo may be incorrect.
Please confirm with the "Forgot password" link above or the "Register" link below!`
]
});
}
if (user.isVerified === false) {
return res.status(401).send({
msg: [
'Your username has not been verified!',
'Check your email for a confirmation link.'
]
});
} else {
I made a log to see if user would be on the req object
console.log('req.user in success login route', req.user);
return res.status(200).send({
msg: [`Your have successfully logged in;`, `Welcome to Hillfinders!`]
});
}
})(req, res, next);
});
It appears what fixed my problem was the way I was declaring my routes.
This works:
router.post('/login', passport.authenticate('local'), function(req, res) {
var user = req.user;
if (!user) {
return res.status(404).send({
msg: [
`We were unable to find this user.`,
`This email and/or password combo may be incorrect.
Please confirm with the "Forgot password" link above or the "Register" link below!`
]
});
}
if (user.isVerified === false) {
return res.status(401).send({
msg: [
'Your username has not been verified!',
'Check your email for a confirmation link.'
]
});
} else {
return res.status(200).send({
msg: [`Your have successfully logged in;`, `Welcome to Hillfinders!`]
});
}
});
I was declaring it like this:
router.route('/login').post((req, res, next) => {
passport.authenticate('local', (err, user) => {
console.log('user ', user);
// console.log('res.locals.user ', res.locals.user);
if (!user) {
res.status(404).send({
msg: [
`We were unable to find this user.`,
`This email and/or password combo may be incorrect.
Please confirm with the "Forgot password" link above or the "Register" link below!`
]
});
return;
}
if (user.isVerified === false) {
res.status(401).send({
msg: [
'Your username has not been verified!',
'Check your email for a confirmation link.'
]
});
return;
} else {
res.status(200).send({
msg: [`Your have successfully logged in;`, `Welcome to Hillfinder!`]
});
return;
}
})(req, res, next);
});
Think I saw this syntax in the Express docs and just thought it was cool because of the chaining:
router.route('/login').post((req, res, next)=>{})
There must be some reason to declare it like this instead of the normal way...
Anyway thats' what eventually got me the user from passport to be on the req object \o/, This might also help other libraries that add objects of value to the req object too...
i'm new to nodejs, i'm using express-session for my project
I can't retrieve session values nowhere than my login route
I see many people have the same problems
Any recommend or help would be awesome ! you all have a nice day !
Here's my login route
route.post('/verify', (req, res) => {
const email = req.body.email;
const pass = req.body.password;
userModel.findOne({ email: email }, (err, data) => {
if (err) {
console.log(err);
res.status(500).json({
success: false,
message: error.message
});
}
else {
if (data !== null) {
if (!bcryptjs.compareSync(pass, data.password)
) {
res.status(400).json({
success: false,
message: "Wrong Password"
})
}
else {
req.session.currentUser = {
email: data.email,
};
console.log(req.session);
res.status(200).json({
success: true,
message: "Login Success",
data: {
email: data.email,
name: data.name,
id:data.id,
}
})
}
}
else {
res.status(404).json({
success: false,
message: "Email doesn't exist"
})
}
}
})
})
Here's my server.js setup:
const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const cors = require('cors');
const session = require('express-session');
const bcryptjs = require('bcryptjs');
const passport = require('passport');
var Schema = mongoose.Schema;
require('./passport/facebook-auth')(passport);
require('dotenv').config();
const passportSetup = require('./passport/google-auth');
const authRoutes = require('./routes/auth-routes');
const userRoutes = require('./user/user.routes')
const userModel = require('./user/user.schema');
// connect to mongodb
mongoose.connect('mongodb://' + process.env.USER + ':' + process.env.PASS + '#localhost:27017/' + process.env.DATABASE + '?authSource=admin', { useNewUrlParser: true, useUnifiedTopology: true }, (e) => {
//FIXME: tim cach viet khac
if (e)
throw e;
else {
console.log("MongoDB Connected...");
// basic init
const server = express();
server.use(session({
secret: 'keyboard cat',
resave: true,
saveUninitialized: false,
}));
server.use(express.static('public'));
// set up cors to allow us to accept requests from our client
server.use(cors({
origin: "http://localhost:3000", // allow to server to accept request from different origin
methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
credentials: true // allow session cookie from browser to pass through
})
);
server.use(bodyParser.json());
// set up session cookies
// initialize passport
server.use(passport.initialize());
server.use(passport.session());
// set up route
server.use('/auth', authRoutes);
server.use('/users', userRoutes);
server.listen(process.env.PORT || 5000, (err) => {
if (err)
throw err;
else
console.log("Server listening on port 5000...");
console.log('hadm x tanhng...');
console.log('ununneee here we come');
});
}
})
after logging in , the session is destroyed automatically
thank you once again