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}`)
})
Related
I'm trying to implement JWT authentication, and I can't figure out why in Postman protected-route is available, while in my browser protected-route is Unauthorized.
I'm a beginner and its first time implementing jwt authentication in my project following this tutorial https://www.youtube.com/playlist?list=PLYQSCk-qyTW2ewJ05f_GKHtTIzjynDgjK
Problem
/login and /register work fine and I can see from the log they issue a JWT token in the req.headers
like 'Authorization':'Bearer <token>', however when I try to access GET /protected-route in my browser, it returns Unauthorized, and I can see no logging from JWTStrategy.
I think req.headers.Authorization is not set to all HTTP requests in the app, but only to POST /login, /register routes So my questions are:
Is this req.headers["Authorization"] = 'Bearer <toekn>'; correct way to set req headers to all GET and POST request in the app?
Does the jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken() checks for Authorization property in req.headers or res.headers?
I have provided the relevant code snippets please have a look!
Code:
app.js
//...
require('./config/database'); // require the db
// ...
const passport = require('passport');
const strategy = require('./config/passport').strategy;
// ...
app.use(passport.initialize());
passport.use(strategy);
// ...
module.exports = app
router/index.js
const router = express.Router();
//...
const User = require('../config/database').User;
const passport = require('passport');
const utils = require('../lib/utils');
// register route
router.post('/register', function(req, res, next){
const saltHash = utils.genPassword(req.body.password);
console.log("req.body.password is: " + req.body.password)
const salt = saltHash.salt;
const hash = saltHash.hash;
const newUser = new User({
username: req.body.username,
hash: hash,
salt: salt
})
newUser.save()
.then((user) => {
const jwt = utils.issueJWT(user);
if (jwt){
req.headers["Authorization"] = jwt.token;
}
res.redirect('/login')
})
.catch(err => next(err))
});
// login route
router.post('/login', function(req, res, next){
User.findOne({username: req.body.username})
.then((user) => {
if(!user) {
res.status(401).json({success: false, msg: "Could not find user "})
}
// validate the user
const isValid = utils.validPassword(req.body.password, user.hash, user.salt)
if(isValid) {
// issue a JWT
const jwt = utils.issueJWT(user);
if (jwt){
req.headers["Authorization"] = jwt.token;
}
res.redirect("/")
} else {
res.status(401).json({success: false, msg: "you entered the wrong password"})
}
})
.catch(err => next(err))
});
// GET protected route
router.get("/protectd-route", passport.authenticate('jwt', {session: false}), async (req, res, next) => {
res.render("user/getuser.pug")
next()
})
passport.js
const User = require('../config/database').User;
// ...
const Strategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
/// PUB_KEY
const pathToKey = path.join(__dirname, '..', 'id_rsa_pub.pem');
const PUB_KEY = fs.readFileSync(pathToKey, 'utf8');
// options
const options = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: PUB_KEY,
algorithms: ['RS256']
};
// strategy
const strategy = new Strategy(options, (payload, done) => {
User.findOne({_id: payload.sub})
.then((user) => {
if(user) {
return done(null, user)
} else {
return done(null, false)
}
})
.catch((err) => done(err, null))
});
module.exports.strategy = strategy;
utils.js
genPassword() - Creating a salt and hash out of it to store in db
validPassword() - re-hashing user salt and hash to verify
issueJWT() - signing user with jsonwebtoken
const crypto = require('crypto');
const jsonwebtoken = require('jsonwebtoken');
const User = require('../config/database').User;
//...
const pathToKey = path.join(__dirname, '..', 'id_rsa_priv.pem');
const PRIV_KEY = fs.readFileSync(pathToKey, 'utf8');
// validate in /login
function validPassword(password, hash, salt) {
var hashVerify = crypto.pbkdf2Sync(password, salt, 10000, 64, 'sha512').toString('hex');
return hash === hashVerify;
}
// generate in /register
function genPassword(password) {
var salt = crypto.randomBytes(32).toString('hex');
var genHash = crypto.pbkdf2Sync(password, salt, 10000, 64, 'sha512').toString('hex');
return {
salt: salt,
hash: genHash
};
}
// sign
function issueJWT(user) {
const _id = user._id;
const expiresIn = '86400';
const payload = {
sub: _id,
iat: Date.now()
};
const signedToken = jsonwebtoken.sign(payload, PRIV_KEY, { expiresIn: expiresIn, algorithm: 'RS256' });
return {
token: "Bearer " + signedToken,
expires: expiresIn
}
}
module.exports.validPassword = validPassword;
module.exports.genPassword = genPassword;
module.exports.issueJWT = issueJWT;
Postman
In Postman, the protected route is showing successfully, with Headers set as above.
The browser network is showing this, there is no Authorization property both in response and request headers
Clearly, I'm missing something, but I can't figure it out. Any help is appreciated
here is also my db
database.js
const uri = process.env.MONGO_URI
const connection = mongoose.connection;
mongoose.connect(uri, {
useUnifiedTopology: true,
serverSelectionTimeoutMS: 5000
})
connection.on('error', console.error.bind(console, "connection error"))
connection.once('open', () => {
console.log('MongoDB database connection has been established successfully')
})
const userSchema = new mongoose.Schema({
username: String,
hash: String,
salt: String
});
const User = mongoose.model('User', userSchema )
module.exports.User = User;
example of app and database after logging in with existing user
example of app and database after registering a brand new user
It seems session data only stores the ' _id ' field after a user registers, but not after a user logs in. This means I cannot access the ' _id ' field (or the email field for that matter).
Here is the console.log outputs (from the userController functions)
User data after register:
{ username: 'aaa',
email: 'aaa#aaa.com',
password:
'$2a$10$Yl8E3iFNBw7VPwjsi.8ade6peTDN6Ui/3Mgzu.YrnoaeJFqt35aRy',
_id: 6009f707ba65290d9e8d1070 }
User data after login:
{ username: 'aaa', email: '', password: 'aaa' }
Here is my userController.js showing login and register functions:
const User = require('../models/User')
exports.login = function(req, res) {
let user = new User(req.body)
user.login().then(() => {
//can create new properties unique per browser visitor
console.log("User data after login: ")
console.log(user.data)
req.session.user = {username: user.data.username, _id: user.data._id}
req.session.save(function() {
res.redirect('/')
})
}).catch((e) => {
req.flash('errors', e)
req.session.save(function() {
res.redirect('/')
})
})
}
exports.register = function(req, res) {
let user = new User(req.body)
user.register().then(() => {
console.log("User data after register: ")
console.log(user.data)
req.session.user = {username: user.data.username, _id: user.data._id}
req.session.save(function() {
res.redirect('/')
})
}).catch((regErrors) => {
regErrors.forEach(function(err) {
req.flash('regErrors', err)
})
req.session.save(function() {
res.redirect('/')
})
})
Here is my User.js model:
const validator = require('validator')
const bcrypt = require('bcryptjs')
const usersCollection = require('../db').db().collection('users')
let User = function(data) {
this.data = data,
this.errors = []
}
User.prototype.login = function() {
return new Promise((resolve, reject) => {
this.cleanup()
usersCollection.findOne({username: this.data.username}).then((attemptedUser) => {
if(attemptedUser && bcrypt.compareSync(this.data.password, attemptedUser.password)) {
resolve()
} else {
reject("Invalid Username / Password")
}
}).catch(function() {
reject("Try again later")
})
})
}
User.prototype.register = function() {
return new Promise(async (resolve, reject) => {
this.cleanup()
await this.validate()
if(!this.errors.length) {
let salt = bcrypt.genSaltSync(10)
this.data.password = bcrypt.hashSync(this.data.password, salt)
await usersCollection.insertOne(this.data)
resolve()
} else {
reject(this.errors)
}
})
}
module.exports = User
Edited: Added app.js file
const express = require('express')
const session = require('express-session')
const MongoStore = require('connect-mongo')(session)
const flash = require('connect-flash')
const app = express()
let sessionOptions = session({
secret: "shh secret dfdfdfdf",
store: new MongoStore({client: require('./db')}),
resave: false,
saveUninitialized: false,
cookie: {maxAge: 1000*60*60*24, httpOnly: true},
})
app.use(sessionOptions)
app.use(flash())
//run this function before every request, then runs router
app.use(function(req, res, next) {
res.locals.user = req.session.user
next()
})
const router = require('./router')
const e = require('express')
app.use(express.urlencoded({extended: false}))
app.use(express.json())
app.use(express.static('public'))
app.set('views', 'views')
app.set('view engine', 'ejs')
app.use('/', router)
module.exports = app
The problem is that you are making a query for the user but you never actually return the user. Instead you use old data that you provided when you created user object.
usersCollection.findOne({username: this.data.username}).then((attemptedUser) => {
if(attemptedUser && bcrypt.compareSync(this.data.password, attemptedUser.password)) {
this.data = attemptedUser;
resolve()
} else {
reject("Invalid Username / Password")
}
}).catch(function() {
reject("Try again later")
})
In order to update user data property, before resolving the Promise you can assign to the data the user that you found in database.
I may suggest you not to declare service logic in your models but directly in handlers or service object.
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
I use express-sessions and passports. I store token data based on the sessionID stored in cookies which will later be retrieved in the mysql database.
So far I can store shopping cart data using this sessionID as a token
that is stored in cookies and will later be retrieved in the database
every request made.
but I got a problem when the user logged in using a passport, which is where the user data is also stored in the session and makes the sessionID change when the user successfully logs in.
So after successfully logging in the shopping cart data disappears because the sessionID is different
Did I make mistake?
to be honest, I was really confused about the use of this express session
I have tried deleting this : app.use(passport.session()); and still the sessionID is changed
Server.js
const app = express();
app.use(session({
genid: function (req) {
return uuidv4() // use UUIDs for session IDs
},
name:keys.session.name,
secret: keys.session.secret,
resave: false,
saveUninitialized: true,
rolling:true,
cookie: {
secure: false,
httpOnly: true,
maxAge:keys.session.maxAge, // satu hari,
sameSite:true,
}
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(cookieParser());
app.use(csrf({ cookie: true }));
app.disable('x-powered-by');
app.use((req,res,next)=>{
res.header('X-XSS-Protection', '1; mode=block');
res.header('X-Frame-Options','deny');
res.header('X-Content-Type-Options','nosniff');
res.header("Access-Control-Allow-Origin", "http://localhost:3000");
res.cookie('hammerstout_t',req.csrfToken());
console.log(req.sessionID);
next();
})
app.use(cors({ origin: keys.origin.url,credentials:true}))
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.get('/', function (req, res, next) {
var ua = UAparser(req.headers['user-agent']);
res.end(JSON.stringify(ua, null, ' '));
})
app.use('/api/',[CategoryRoutes, ProductRoutes, CartRoutes, AuthRoutes,TrackRoutes]);
const port = process.env.PORT || 5000;
app.listen(port, (err) => {
if(err){
console.log(err);
}else{
console.log(`Server running on port ! ${port}`);
}
});
passport-setup-js
import passport from 'passport';
import GoogleStrategy from 'passport-google-oauth20';
import keys from './keys';
import db from './conn';
passport.serializeUser((user, done) => {
let tokenValue={
providerId:user.providerId,
token:user.token,
user_id:user.id
}
done(null, tokenValue)
})
passport.deserializeUser((data, done) => {
let querySelect = `SELECT us.id,us.displayName,us.email,up.providerId,up.token,up.provider from user as us
left join user_provider as up on us.id = up.user_id
where us.id = ? and up.provider = 'google' and up.providerId = ? and up.token = ?`;
db.query(querySelect,[data.user_id,data.providerId,data.token], (err, ress) => {
if(ress.length > 0){
done(null, ress[0])
}
})
})
passport.use(
new GoogleStrategy({
//options for the google strategy
callbackURL: '/api/auth/google/redirect',
clientID: keys.google.clientID,
clientSecret: keys.google.clientSecret
}, (accessToken, refreshToken, profile, done) => {
// console.log(profile);
// TODO : HARUS VALIDASI DULU
let queryInsert = `INSERT INTO user set ?; INSERT INTO user_provider set user_id = (SELECT u.id from user as u order by id desc limit 1), ?;`;
let queryFind = `SELECT us.id from user as us
left join user_provider as up on us.id = up.user_id
where up.providerId = ${profile.id}
and up.token = '${accessToken}' group by us.id`;
let querySelect = `SELECT us.id,us.displayName,us.email,up.providerId,up.token,up.provider from user as us
left join user_provider as up on us.id = up.user_id
where us.id = ? and up.provider = ? and up.providerId = ? and up.token = ?`;
let user = {
displayName: profile.displayName,
email: profile.emails[0].value,
}
if(profile.gender){
user.gender = profile.gender
}
let user_provider={
provider:profile.provider,
providerId:profile.id,
token:accessToken
}
db.query(queryFind, (error, result, fields) => {
if(error) return done(error);
if (result.length > 0) {
return done(null, result[0]);
}else{
db.query(queryInsert, [user, user_provider], (err, ress, fields) => {
if (err) return done(err);
if (ress) {
db.query(querySelect, [ress[0].insertId, profile.provider, profile.id, accessToken],(err,ress)=>{
if (err) return done(err);
if(ress.length > 0){
return done(null, ress[0]);
}
})
}
})
}
})
})
)
Controller
export const loginGoogleRedirect = (req,res)=>{
let payload ={
id:req.user.id,
displayName:req.user.displayName,
email:req.user.email,
providerId:req.user.providerId,
token:req.user.token,
provider:req.user.provider
};
jwt.sign(
payload,
keys.jwt.secretOrPrivateKey,
{
expiresIn: keys.jwt.expiresIn
}, (err, token) => {
res.redirect(keys.origin.url + "?token=" + token);
});
}
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});
}
});
});