I'm using passport-jwt package for simple authentication and the token is generated by jsonwebtoken.
But the problem is that verify callback is never called.
Here my passport.js code.
var JwtStrategy = require('passport-jwt').Strategy;
var User = require('../app/models/user');
var config = require('../config/database');
var opts = {};
opts.jwtFromRequest = function(req) {
var token = null;
if (req && req.headers) {
token = req.headers.authorization;
}
return token;
};
opts.secretOrKey = config.secret;
console.log(opts);
module.exports = function(passport) {
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findOne({_id: jwt_payload._doc._id}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
done(null, user);
} else {
done(null, false);
}
});
}));
};
Hope to hear from you.
Thanks
The problem is that you should add 'JWT '(JWT and a space ahead of the original jwt signed). Please check this tutorial http://blog.slatepeak.com/building-a-basic-restful-api-for-a-chat-system/
by Joshua for assistance.
By the way, make sure that if you need a 'where' inside your findOne or not.
Related
I am writing a application that uses JWTs for user authentication. I am trying to allow the user to submit or edit resources associated ONLY to there ID which is generated when they create their account in MongoDB only after the user has logged in. My instructor suggested to store the user._id within a cookie but not familiar with that process. Any tips on how I could accomplish this?
Here is an example of how my users are registered within the server
app.get('/register', function (req, res) {
res.sendFile(__dirname + '/public/register.html');
});
app.post('/register', async function (req,res) {
try {
req.body.password = await bcrypt.hash(req.body.password, 10);
var newUser = new User(req.body);
newUser.save(function(error,user){
res.redirect('/login');
});
} catch{
res.redirect('/register');
}
// console.log(newUser);
});
Here is the auth process
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const bcrypt = require('bcrypt');
const opts = {}
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = process.env.secret;
module.exports = new JwtStrategy(opts, async (jwt_payload,done)=> {
const user = await (req.body.email);
if( jwt_payload.email === req.body.email){
return done(null, true)
}
try {
if (await bcrypt.compare(password, user.password)){
return done(null, user);
} else {
return done(null, false, { message: "Password is incorrect"});
}
} catch (e) {
return done(e);
}
})
Here is logging in
app.post('/login', async (req, res) => {
const { username, password } = req.body
const user = await User.findOne({ username }).lean()
if (!user) {
return res.sendFile(__dirname + '/public/401.html');
}
if (await bcrypt.compare(password, user.password)) {
// the username, password combination is successful
const token = jwt.sign(
{
id: user._id,
username: user.username
},
process.env.secret
)
console.log(user._id);
//I need to find a way to send over the user._id to the client side so it can be stored later to allow the user to only update their resources and to view their resources
return res.redirect('/welcome.html');
}
res.sendFile(__dirname + '/public/401.html');
})
EDIT: I am not not wanting to store the token unless that is the only way to pass the user._id to the client side due to security reasoning.
Technologies I am using are, Node.js, Express.js, Mongoose, MongoDB, JWT, passport, jwt-simple, bcrypt, methodOverride, and secrets for resetting the users password and creating the user are stored in an .env.
I have a stand alone oauth2 identity provider that is working.
Now I'm developing a consumer that will authenticate users with this stand alone provider.
I'm following this tutorial about passport and Google Auth:
I'm trying to use this information to use passport-oauth2 to work as a client. I have made some changes in the code provided in the tutorial above by following the official documentation on passoprt-oauth2.
I think that I have some problem in the callback function where expressjs receive the confirmation of authentication and info about the user. I don't understand how to use this information.
Here is the code of my app.js
const express = require('express');
const app = express();
const passport = require('passport');
const OAuth2Strategy = require('passport-oauth2');
const cookieSession = require('cookie-session');
// cookieSession config
app.use(cookieSession({
maxAge:24*60*60*1000,
keys: ['secret-personalize']
}));
app.use(passport.initialize());
app.use(passport.session());
//Strategy config
passport.use(new OAuth2Strategy({
authorizationURL: 'http://localhost:3000/dialog/authorize',
tokenURL: 'http://localhost:3000/oauth/token',
clientID: 'xyz123',
clientSecret: 'ssh-password',
callbackURL: "/auth/oauth2/callback"
},
(accessToken, refreshToken, profile, done) => {
console.log(profile);
done(null, profile);
}
));
// Used to decode the received cookie and persist session
passport.deserializeUser((user, done) => {
done(null, user);
});
// Middleware to check if the User is authenticated
app.get('/auth/oauth2',
passport.authenticate('oauth2'));
function isUserAuthenticated(req, res, next){
if (req.user){
next();
} else {
res.send('you must login!');
}
}
// Routes
app.get('/', (req, res) => {
res.render('index.ejs');
});
// The middleware receives the data from AuthPRovider and runs the function on Strategy config
app.get('/auth/oauth2/callback', passport.authenticate('oauth2'), (req,res) => {
res.redirect('/secret');
});
// secret route
app.get('/secret', isUserAuthenticated, (req, res) =>{
res.send('You have reached the secret route');
});
// Logout route
app.get('/logout',(req, res) => {
req.logout();
res.redirect('/');
});
app.listen(8000, () => {
console.log('Server Started 8000');
});
and this is for views/index.ejs
<ul>
<li>Login</li>
<li>Secret</li>
<li>Logout</li></ul>
I got this error:
Error: Failed to serialize user into session
at pass (/home/user/job/NodeJS/test-consumer/second/node_modules/passport/lib/authenticator.js:281:19)
at Authenticator.serializeUser (/home/user/job/NodeJS/test-consumer/second/node_modules/passport/lib/authenticator.js:299:5)
at SessionManager.logIn (/home/user/job/NodeJS/test-consumer/second/node_modules/passport/lib/sessionmanager.js:14:8)
at IncomingMessage.req.login.req.logIn (/home/user/job/NodeJS/test-consumer/second/node_modules/passport/lib/http/request.js:50:33)
at OAuth2Strategy.strategy.success (/home/user/job/NodeJS/test-consumer/second/node_modules/passport/lib/middleware/authenticate.js:248:13)
at verified (/home/user/job/NodeJS/test-consumer/second/node_modules/passport-oauth2/lib/strategy.js:177:20)
at OAuth2Strategy.passport.use.OAuth2Strategy [as _verify] (/home/user/job/NodeJS/test-consumer/second/app.js:31:5)
at /home/user/job/NodeJS/test-consumer/second/node_modules/passport-oauth2/lib/strategy.js:193:24
at OAuth2Strategy.userProfile (/home/user/job/NodeJS/test-consumer/second/node_modules/passport-oauth2/lib/strategy.js:275:10)
at loadIt (/home/user/job/NodeJS/test-consumer/second/node_modules/passport-oauth2/lib/strategy.js:345:17)
Every help is welcome.
Thanks you
You need to add serializer:
passport.serializeUser(function(user, done) {
done(null, user);
});
I'm using this module now, but the profile always returns empty.
First you need to override the userProfile
This is the source code
const passport = require('passport')
// const { Strategy: GoogleStrategy } = require('passport-google-oauth20')
const { Strategy: GithubStrategy } = require('passport-github')
const { Strategy: OAuth2Strategy } = require('passport-oauth2')
const { GITHUB_CONFIG, OAUTH2_CONFIG} = require('../config')
const Profile = require('./profile')
module.exports = () => {
// Allow passport to serialize and deserialize users into sessions
passport.serializeUser((user, cb) => cb(null, user))
passport.deserializeUser((obj, cb) => cb(null, obj))
// The callback that is invoked when an OAuth provider sends back user
// information. Normally, you would save the user to the database
// in this callback and it would be customized for each provider
const callback = (accessToken, refreshToken, params, profile, cb) => {
console.log('access-token',accessToken)
console.log('refresh-token',refreshToken)
console.log('profile',profile)
console.log('params',params)
return cb(null, profile)
}
// Adding each OAuth provider's startegy to passport
// passport.use(new GoogleStrategy(GOOGLE_CONFIG, callback))
passport.use(new GithubStrategy(GITHUB_CONFIG, callback))
const DjangoStrategy = new OAuth2Strategy(OAUTH2_CONFIG, callback)
DjangoStrategy.userProfile = function(accessToken, done) {
var self = this;
this._userProfileURL = 'http://localhost:8001/accounts/profile/';
this._oauth2.get(this._userProfileURL, accessToken, function (err, body, res) {
var json;
if (err) {
if (err.data) {
try {
json = JSON.parse(err.data);
} catch (_) {}
}
if (json && json.message) {
return done(new APIError(json.message));
}
return done(new InternalOAuthError('Failed to fetch user profile', err));
}
try {
json = JSON.parse(body);
} catch (ex) {
return done(new Error('Failed to parse user profile'));
}
console.log('json', json)
var profile = Profile.parse(json);
profile.provider = 'oauth2';
profile._raw = body;
profile._json = json;
done(null, profile);
});
}
passport.use(DjangoStrategy)
}
Create a profile
profile.js
exports.parse = function(json) {
if ('string' == typeof json) {
json = JSON.parse(json);
}
var profile = {};
profile.id = String(json.id);
profile.displayName = json.name;
profile.username = json.username;
profile.email = json.email;
return profile;
};
You can also check clone my source code
https://github.com/faisallarai/nodejs-oauth-server.git
I am using express & jwt-simple to handle login/register & authenticated requests as a middleware api. I'm trying to create a .well-known endpoint so other api's can authenticate request based on token send in.
Here's my strategy:
module.exports = function() {
const opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeader();
opts.secretOrKey = securityConfig.jwtSecret;
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
// User.where('id', jwt_payload.id).fetch({withRelated: 'roles'})
console.log('jwt_payload', jwt_payload)
User.where('id', jwt_payload.id).fetch()
.then(user => user ? done(null, user) : done(null, false))
.catch(err => done(err, false));
}));
};
Here's my login route:
router.post('/login', function(req, res) {
const {username, password} = req.body;
Promise.coroutine(function* () {
const user = yield User.where('username', username).fetch();
if(user) {
const isValidPassword = yield user.validPassword(password);
if (isValidPassword) {
let expires = (Date.now() / 1000) + 60 * 30
let nbf = Date.now() / 1000
const validatedUser = user.omit('password');
// TODO: Verify that the encoding is legit..
// const token = jwt.encode(user.omit('password'), securityConfig.jwtSecret);
const token = jwt.encode({ nbf: nbf, exp: expires, id: validatedUser.id, orgId: validatedUser.orgId }, securityConfig.jwtSecret)
res.json({success: true, token: `JWT ${token}`, expires_in: expires});
} else {
res.status(401);
res.json({success: false, msg: 'Authentication failed'});
}
} else {
res.status(401);
res.json({success: false, msg: 'Authentication failed'});
}
})().catch(err => console.log(err));
});
Here's my .well-known route:
router.get('/.well-known', jwtAuth, function(req, res) {
// TODO: look over res.req.user. Don't seem to be the way to get those parameters.
// We dont take those parameters from the decrypted JWT, we seem to grab it from the user in DB.
const { id, orgId } = res.req.user.attributes;
console.log("DEBUG: userId", id)
console.log("DEBUG: USER", res.req.user)
res.json({
success: true,
userId: id,
orgId
});
});
here's my jwtAuth() function:
const passport = require('passport');
module.exports = passport.authenticate('jwt', { session: false });
How would I actually get the token in the route function & decrypt it? All this does right now which works is that it authenticates if true however I need to be able to decrypt the token to send back the stored values. I'm not sure what res.req.user.attributes comes from, is this the token?
Take a look at passport-jwt and in your passport-config (or wherever you initialize passport) setup JWT Strategy:
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const jwtAuth = (payload, done) => {
const user = //....find User in DB, fetch roles, additional data or whatever
// do whatever with decoded payload and call done
// if everything is OK, call
done(null, user);
//whatever you pass back as "user" object will be available in route handler as req.user
//if your user does not authenticate or anything call
done(null, false);
}
const apiJwtOptions: any = {};
apiJwtOptions.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
apiJwtOptions.algorithms = [your.jwt.alg];
apiJwtOptions.secretOrKey = your.jwt.secret;
//apiJwtOptions.issuer = ???;
//apiJwtOptions.audience = ???;
passport.use('jwt-api', new JwtStrategy(apiJwtOptions, jwtAuth));
If you want just decoded token, call done(null, payload) in jwtAuth.
Then in your route files when you want to protect endpoints and have info about user, use as:
const router = express.Router();
router.use(passport.authenticate('jwt-api', {session: false}));
And in handler you should have req.user available. It is configurable to what property of req you store data from auth, req.user is just default.
I am new to nodejs. I need to create and authentication using nodejs. Here I found few answers but they also used old version. Here I am using these versions
I used this code to create the passport.js file in my node application.
const JwtStrategy = require('passport-jwt').Strategy,
ExtractJwt = require('passport-jwt').ExtractJwt;
const config = require('./db');
const User = require('./models/user');
const opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = config.secret;
module.exports = (passport)=>{
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findUserbyId({_id: jwt_payload._doc._id}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
}
});
}));
};
And I used this routes method to create a route with the Authentication.
router.post('/profile',
passport.authenticate('jwt'),{ session: false },
(req, res)=> {
res.json({user:req.user});
});
I used postman to create a token and check the authentication.
I cannot understand the why it wrong. findUserById method is follow
module.exports.findUserbyId = (id, callback)=>{
User.findOne(id, callback);
}
What's the problem in my code? Why it every time gives
unauthorized
in my postman application.
I want the following flow in my app
User selects authenticate with google
I redirect to google and get the callback (This happens using Passport)
I now want to return an auth token that is specific to my application in the form of response to user.
var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
passport.use(new GoogleStrategy({
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
callbackURL: "http://127.0.0.1:3000/auth/google/callback"
},
function(accessToken, refreshToken, profile, done) {
// RETURN ACCESS TOKEN IN THE RESPONSE
}
));
I do get the success callback, but I don know how to return a custom access token/response from the callback function.
I guess the answer related to using of done(), or serialization or deserialization which I am not clear from the docs or any examples. Can you please also explain how done(), or serialization or deserialization are used.
You can do this in serialization and deserialization. See this passport example
userSchema.methods.generateRandomToken = function () {
var user = this,
chars = "_!abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890",
token = new Date().getTime() + '_';
for ( var x = 0; x < 16; x++ ) {
var i = Math.floor( Math.random() * 62 );
token += chars.charAt( i );
}
return token;
};
var User = mongoose.model('User', userSchema);
//serialize
passport.serializeUser(function (user, done){
var createAccessToken = function() {
var token = user.generateRandomToken()
User.findOne({
accessToken: token
}, function(err, existingUser){
if (err) { return done(err) }
if (existingUser) {
createAccessToken()
} else {
user.set('accessToken', token)
user.save(function(err){
if (err) { return done(err) }
return done(null, user.get('accessToken'))
})
}
})
}
if (user._id) {
createAccessToken()
}
})
//deserialize
passport.deserializeUser(function(token, done){
User.findOne({
accessToken : token
}, function(err, user){
done(err, user)
})
})