Since sometime I am using auth0 with express. But now I have one question.
This is how my code looks like:
var passport = require('passport');
var Auth0Strategy = require('passport-auth0');
var strategy = new Auth0Strategy({
domain: '',
clientID: '',
clientSecret: '',
callbackURL: '/loginapi/callback'
}, function (accessToken, refreshToken, extraParams, profile, done) {
// accessToken is the token to call Auth0 API (not needed in the most cases)
// extraParams.id_token has the JSON Web Token
// profile has all the information from the user
return done(null, profile);
});
passport.use(strategy);
// This is not a best practice, but we want to keep things simple for now
passport.serializeUser(function (user, done) {
done(null, user);
});
passport.deserializeUser(function (user, done) {
done(null, user);
});
module.exports = strategy;
But how can I access the accessToken in a express request like the user element. I really don't know how, but I already tried some things.
Nils
I got it guys!
var strategy = new Auth0Strategy({
domain: '',
clientID: '',
clientSecret: '',
callbackURL: '/loginapi/callback'
}, function (accessToken, refreshToken, extraParams, profile, done) {
// accessToken is the token to call Auth0 API (not needed in the most cases)
// extraParams.id_token has the JSON Web Token
// profile has all the information from the user
var info = {
"profile": profile,
"accessToken": accessToken,
"refreshToken": refreshToken,
"extraParams": extraParams
};
return done(null, info);
});
Now I can simply access the accessToken with the req.user object.
Related
I'm working in an application which uses a REST api using the MEAN stack and Passport JS to manage the authentication.
The authentication, we use JTW tokens for the communication between the backend and frontend. The token is generated based on local username and passwords.
Now I want to 'add' (authorize) the user's google account to the profile to use with google calendar API. (using this-> https://github.com/wanasit/google-calendar)
I've already have managed to send the user to the google authorization page, and get the token back from it. The problem is that when the user gets redirected to the page, it looses the JWT token where I check the user for the request.
Is there any other way to get the current logged in user, or to pass some custom callback authorization header/param when calling the authorize method?
auth.js:
var googleParams = {
clientID: config.auth.google.clientID,
clientSecret: config.auth.google.clientSecret,
callbackURL: config.auth.google.callbackURL
}
var googleStrategy = new GoogleStrategy(googleParams, function (token, refreshToken, profile, done) {
profile.token = token;
return done(null, profile);
});
routes:
rotas.get(
'/google',
auth.authenticate(), // will check the current user
auth.isLoggedIn, // make sure the user is really logged in
auth.authorize('google', { scope: googleScope, passReqToCallback: true }) // redirects to google to get the token
);
rotas.get('/callback/google',
auth.authorize('google', { scope: googleScope, passReqToCallback: true })
auth.authRedirect()
);
the auth.authRedirect() function above is the closest solution I've found. It's a Express middleware wich redirects the user to a known route in the frontend where the user IS authenticated... but then I would not be able to fetch all his Google profile and information i need...
You have to be sure the app.use(session) its been called before any route.
...
app.use(session({
secret: 'secret'
}))
app.use(passport.initialize())
app.use(passport.session())
...
rotas.get(
'/google',
auth.authenticate(), // will check the current user
auth.isLoggedIn, // make sure the user is really logged in
auth.authorize('google', { scope: googleScope, passReqToCallback: true }) // redirects to google to get the token
);
rotas.get('/callback/google',
auth.authorize('google', { scope: googleScope, passReqToCallback: true })
auth.authRedirect()
);
Your req.user won't be undefined in this case.
If it doen't work right way, I can put my whole code that I've created here.
Hope it help you! :)
So what I ended up doing was:
Authenticate the user making the request via JWT access_token
Get the user's ID and set it to the state option's property
The user is redirected to the google authorization page and choose the account (s)he wants to connect
(S)He gets redirected to my callback url with the state query param having the user's id
Now I just have to get that id, search the user in the database, and set the data I need from req.account which contains the user's openid profile.
var googleScope = ['openid', 'email', 'https://www.googleapis.com/auth/calendar'];
routes.get(
'/google',
auth.authenticate(),
auth.isLoggedIn,
function (req, res, next) {
var _id = '' + req.user._id; // convert to String... _id is an mongoose object
return auth.authorize('google', { session: false, scope: googleScope, passReqToCallback: true, state: _id })(req, res, next)
}
);
routes.get('/callback/google',
function (req, res, next) {
auth.authorize('google', { session: false, scope: googleScope, passReqToCallback: true })(req, res, next);
},
auth.saveUserData()
);
saveUserData= function () {
return function (req, res, next) {
if (req.query.state) {
var _id = req.query.state;
User.findOne({ _id, deleted: false, active: true })
.exec(function (err, user) {
if (err) {
res.send(err);
}
if (user) {
user.auth.google = {
id: req.account.id,
token: req.account.token,
email: (req.account.emails.length ? req.account.emails[0].value : null),
name: req.account.displayName
}
user.save(function (err, data) {
if (err) {
res.send(err);
} else {
res.redirect('/')
}
})
} else {
res.sendStatus(401);
}
})
} else {
res.sendStatus(400)
}
}
I'm using express and passport to log users in, in one of my controllers, I want to access the current logged in user, but I'm confused about how to access them. req.user does not seem to exist
You must authenticate each time a user every request and set req.user with strategy. I use a strategy with the token.
Token is sent in the headers.
Instead of the token may be cookies or other strategy.
Below incomplete code, but can you help:
var bodyParser = require('body-parser');
var express = require('express');
var jwt = require('jsonwebtoken');
var LocalStrategy = require('passport-local').Strategy;
var BearerStrategy = require('passport-http-bearer').Strategy;
var app = express();
app.use(bodyParser.json());
//Local Strategy to login user with email and password
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'password'
},
function(email, password, done) {
usersRepository
.getUserByEmail(email)
.then(function(user) {
if (!!user && passwordHelper.verify(password, user.password, user.salt)) {
done(null, user);
} else {
done(null, false);
}
});
}));
//Bearer Strategy to auth user with token - run with every request
passport.use(new BearerStrategy(function(token, done) {
jwt.verify(token, 'secret', function(err, decoded) {
if (!err && decoded) {
done(null, decoded); // !!! here is set req.user - decode is my user from token
} else {
done(null, false);
}
});
}));
app.use(passport.initialize());
var bearerAuth = passport.authenticate('bearer', {
session: false
});
bearerAuth.unless = require('express-unless');
//Adding Bearer Strategy to all routing unless login
app.use(bearerAuth.unless({
path: [
'/login'
]
}));
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
//Login - use Local Strategy
app.post('/login', passport.authenticate('local'), function(req, res) {
var accessToken = jwt.sign(req.user, 'secret', {
expiresIn: '7d'
});
res.send({
id: req.user.id,
accessToken: accessToken,
email: req.user.email,
isAdmin: req.user.is_admin
});
});
I am not seeing any effect on the req.user or req.session.passport.user objects as a result of deserializeUser().
My understanding is that successful completion of deserializeUser() should result in my User object which I retrieve from a DB should be set to the req.user property. Is this correct?
Furthermore, it seems that the purpose of this callback is to allow me to serialize a small object (e.g.: {username: 'me', email: 'me#me.com'}) in the cookie itself, but then add more user info retrieved from the database to use downstream.
Instead, I see the following:
passport.serializeUser(function(user, done) {
// user: 'stan#stadelman.com'
fetchUser(user, function(u) {
// u: { email: 'stan#stadelman.com', name: 'Stan Stadelman' }
done(null, u);
});
});
passport.deserializeUser(function(id, done) {
// id: { email: 'stan#stadelman.com', name: 'Stan Stadelman' }
fetchUser(id.email, function(user) {
// user: { email: 'stan#stadelman.com', name: 'Stan Stadelman', local_props: 'from_db'}
done(null, user);
});
});
req.user = 'stan#stadelman.com'
req.session = { cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true },
passport: { user: { email: 'stan#stadelman.com', name: 'Stan Stadelman' } } }
My expectation was that the req.session.passport.user and/or req.user objects should contain the local_props property value. Am I reading the documentation & tutorials incorrectly, or is there an issue? I've cross-posted in passport.js github here.
My express & passport setup is as follows:
// express configs
app.use(cookieParser(expressSecret));
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.use(expressSession({
secret: expressSecret,
resave: true,
saveUninitialized: true }
));
app.use(passport.initialize());
app.use(passport.session());
// passport implementation
passport.use(new BasicStrategy(/...));
You should pass user id to done callback in serialize
passport.serializeUser(function(user, done) {
done(null, user.id);
});
And then use that id to fetch user in deserialize
passport.deserializeUser(function(id, done) {
fetchUser(id, function(user) {
done(null, user);
});
});
I run into a similar problem but caused by something else. Passport.js deserializeUser() does its job setting user object to req.user:
passport.deserializeUser(async function(id, done) {
try {
const account = await Account.findById(id);
done(null, account);
} catch (error) {
done(error, null)
}
});
However, in order to check whether a user is logged in, I have a route on the server /account/me that takes req.user and returns it as json:
router.route('/account/me').get(mwAuthentication, (req, res) => {
res.json(req.user);
});
To filter sensitive properties, I have a method toJSON() defined in Mongoose schema:
AccountSchema.methods.toJSON = function() {
let account = this;
let accountObject = account.toObject();
return _.pick(accountObject, ['_id', 'fullName', 'email', 'facebookId', 'photoUrl']);
};
When you call res.json(req.user) it calls JSON.stringify(req.user) internally, which again calls my toJSON() method and limits the object to properties that I picked.
Hope this helps someone.
I'am a beginner in expressjs and passportjs.
I played with authentication via google using passport with GoogleStrategy. Using the code below i have req.user = { id: '123456' } in /users/hello route handler, but i want to get some like this without session support to send it as the answer to authenticated client. In other words i want to send some token to client if authentication is successful without cookie session start. I can't find the way how to forward user object to target route handler when i turn off sessions.
passport.use(new GoogleStrategy({
returnURL: 'http://localhost/auth/google/return',
realm: 'http://localhost/'
},
function(identifier, profile, done) {
done(null, {id: '123456'});
}
));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
done(null, {id: id});
});
app.use(session({ secret: 'keyboard cat' }));
app.use(passport.initialize());
app.use(passport.session());
app.get('/auth/google', passport.authenticate('google');
app.get('/auth/google/return',
passport.authenticate('google', {
successRedirect: '/users/hello',
failureRedirect: '/users/goodbye'
}));
To turn off sessions try changing this:
app.get('/auth/google/return',
passport.authenticate('google', {
successRedirect: '/users/hello',
failureRedirect: '/users/goodbye'
}));
to:
app.get('/auth/google/return',
passport.authenticate('google', {
session:false
}));
Goal
What I want to do:
Create a session for the user
Create a session for the socket (socket.io)
Use passport.js to authenticate the login and socket sessions.
Notes
I have installed MongoStore and passport.socket.io npm's. I can login and set the cookie of the user logged in (connect.sid)
QUESTION
How do I setup the system to store socket sessions and couple them with the session of the user?
Code
app.js
/* The usual express setup */
passport = require('passport'),
LocalStrategy = require('passport-local').Strategy,
User = require('./models/user.js'),
MongoStore = require('connect-mongo')(express);
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.session({
secret: 'chuck norris',
store: new MongoStore({db: User.name}, // the db's name
function(err) {
console.log(err || 'connect ok!');
})
}));
app.use(express.methodOverride());
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.js (the passport part)
passport.use(new LocalStrategy({
usernameField: 'username',
passwordField: 'password'
},
function(username, password, done) {
User.findOne({username: username}, function(err, user) {
if(!user) {
return done(null, false, {message: 'Incorrect Username!'});
}
if(!user.validPassword(password)) {
return done(null, false, {message: 'Incorrect Password!'});
}
return done(null, user);
});
}
));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
app.post('/',
passport.authenticate('local'),
function(req, res) {
res.redirect('/home/'+req.user.username);
});
app.js (socket.io part)
io.set('authorization', passportSocket.authorize({
key: 'connect.sid',
secret: 'chuck norris',
store: /* Not entirely sure what goes here */
fail : function(data, accept) { accept(null, false); },
success: function(data, accept) { accept(null, true); }
}));
io.sockets.on('connection', function(socket) {
console.log('User Connected: ' + socket.handshake.user.username);
});
You store your new memory story object instance into a variable and pass it in to both express and socket io like so.
(be aware that we are using different stores but in theory it should not matter what store you use as long as you pass off control the proper way)...
var ...
,MemoryStore = express.session.MemoryStore
,sessionStore = new MemoryStore();
then in app.configure you...
app.use(express.session({store:sessionStore,secret:'secret',key:'express.sid'}));
and finally in socket.io configure
io.configure(function (){
io.set("authorization", passportSocketIo.authorize({
key: 'express.sid', //the cookie where express (or connect) stores its session id.
secret: 'secret', //the session secret to parse the cookie
store: sessionStore, //the session store that express uses
fail: function(data, accept) {
// console.log("failed");
// console.log(data);// *optional* callbacks on success or fail
accept(null, false); // second param takes boolean on whether or not to allow handshake
},
success: function(data, accept) {
// console.log("success socket.io auth");
// console.log(data);
accept(null, true);
}
}));
If you have done this correctly and your user successfully authenticates you should then be able to access the session data on the handshake object.
console.log(socket.handshake.user.username);
//or sometimes it might be...
console.log(socket.handshake.user[0].username);
Hope that helps.