When I am trying to secure the users API; I am getting always 401 unauthorized. I have tried different variations to define strategy; but no luck. I have been using JWTStrategy and using jwtwebtoken while signing the token with the Secret and RS256 Algorithm
Passport.js
// import * as module from 'module';
const
User = require('../models/user'),
JwtStrategy = require('passport-jwt').Strategy,
ExtractJwt = require('passport-jwt').ExtractJwt,
config = require('./appconfig');
// Setting JWT strategy options
const jwtOptions = {
// Telling Passport to check authorization headers for JWT
jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme('Bearer'),
// Telling Passport where to find the secret
secretOrKey: config.jwtSecret,
algorithms:['RS256']
// TO-DO: Add issuer and audience checks
};
console.log(config.jwtSecret);
module.exports = function(passport) {
passport.use(new JwtStrategy(jwtOptions, function(jwt_payload, done) {
console.log(jwt_payload);
User.findOne({id: jwt_payload.sub}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
// or you could create a new account
}
});
}));
};
Index.Route.js
const express = require('express');
const userRoutes = require('./user.route');
const authRoutes = require('./auth.route');
// const postRoutes = require('./post.route');
const passport = require('passport');
passport.initialize();
var jwt = require('../config/passport')(passport);
const router = express.Router(); // eslint-disable-line new-cap
/** GET /health-check - Check service health */
router.get('/health-check', (req, res) =>
res.send('OK')
);
// mount user routes at /users
router.use('/users', passport.authenticate('jwt', { session: false }), userRoutes);
// mount auth routes at /auth
router.use('/auth', authRoutes);
// router.use('/posts', postRoutes);
module.exports = router;
Using Postman:
Header:
Authentication: JWT Token
localhost:4040/api/users
Did you configure postman in header section? Can you show JwtStrategy code.
Related
I'm trying to make a very simple authentication scenario with passport-jwt. When I access a protected path, it keeps returning 'unauthorized'. I've tried almost everything suggested on similar threads but it doesn't work. All I want to do for now is have a call to 'authenticate' path in order the JWT token to be generated and then call the 'protected' path and allow access. For simplicity, I have all the code in server.js.
Here is the code:
const express = require("express");
const passport = require('passport');
const jwt = require('jsonwebtoken');
const JWTStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const secret = "s0m3$3Cret$h0lyC0d3&$";
const options = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken,
secretOrKey: secret,
algorithms: ['RS256']
};
passport.use(new JWTStrategy(options, function(jwt_payload, done){
return done(null, {id: 'FAKE_ID'});
}
));
const app = new express();
app.get('/authenticate', (req, res, next) => {
const token = jwt.sign({sub:'FAKE_ID'},secret,{expiresIn: 604800});
res.status(200).json({ success: true, message: 'Logged in successfully!', token: 'Bearer ' + token});
})
app.get('/protected', passport.authenticate('jwt', { session: false }), (req, res, next) => {
res.status(200).json({ success: true, msg: "You are successfully authenticated to this route!"});
});
app.listen(3000, () => {
console.log('App running at 3000')
})
Thank you in advance
So I am trying to build two JWT authentication systems for one app. One for the users and one for businesses. The authentication uses passport (local and JWT). I started working on the user aspect of the authentication and I was able to make it work as I wanted. I thought it would be the same for business. All I did was simply copy paste and change few variable. Normally the business endpoints should work but it does not. So I tried to debug what was going on and I am still trying to figure out what it is. My guess is that when I load the strategies in my app.js, only the passport strategies for users get called. If I permute positions between passport and business-passport, everything breaks.
Here is my code.
Passport Middleware For User passport.js
const passport = require('passport');
const JwtStrategy = require('passport-jwt').Strategy;
const {ExtractJwt} = require('passport-jwt');
const LocalStrategy = require('passport-local').Strategy;
const { JWT_SECRET } = require('./config');
const User = require('./models/user');
// const Business = require('./models/business');
passport.use(new JwtStrategy({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: JWT_SECRET,
passReqToCallback: true
}, async (req, payload, done) => {
console.log('payload', payload)
try {
// Find the user specified in token
const user = await User.findById(payload.sub);
// Handle if User dont exist
if (!user) {
return done(null, false);
}
// Otherwise, return the user
req.user = user;
done(null, user);
} catch (error) {
done(error, false);
}
}));
// Local Strategy
passport.use(new LocalStrategy({
usernameField: 'email'
}, async (email, password, done) => {
try {
// Find the user given the email
const user = await User.findOne({email});
// If not, handle it
if (!user) {
return done(null, false);
}
// check if password is correct
const isMatch = await user.isValidPassword(password);
// if not, handle it
if (!isMatch) {
return done(null, false);
}
// Otherwise, return the user
done(null, user);
} catch (error) {
done(error, false);
}
}));
Passport Middleware For Business passport-business.js
Just change User for Business
Controllers for Users controllers/user.js
const fs = require('fs');
const mongoose = require('mongoose');
const JWT = require('jsonwebtoken');
const { JWT_SECRET } = require('../config');
const User = require('../models/user');
signToken = user => {
// Respond with token
return JWT.sign({
iss: 'My Business, Inc.',
userType: 'users',
sub: user._id, /* You can also use newUser.id */
iat: new Date().getTime(), /* Current Time */
exp: new Date().setDate(new Date().getDate() + 1), /* Current Time + 1 day ahead */
}, JWT_SECRET);
};
module.exports = {
signup: async (req, res, next) => {
console.log('req.value.body', req.value.body);
const {email, password} = req.value.body;
// Check if User already exist by email address
const foundUser = await User.findOne({email});
if(foundUser) {
return res.status(403).json({error: 'Email is already in use'});
}
// Create a new User
const newUser = new User({email, password});
await newUser.save();
// Generate the token
const token = signToken(newUser);
// Respond with token
res.status(200).json({token});
},
signin: async (req, res, next) => {
// Generate token
const token = signToken(req.user);
res.status(200).json({token});
// console.log('signin');
},
};
The reason why I have req.value... is because of a middleware that I used to validate my body and url params.
Controllers for Business controllers/business.js .
Everything is the same, just change User with Business.
I should also point out that whenever I use jwt.io to see if my token signature is valid, it always says invalid signature, but everything works on my application as expected. Wondering why it is saying invalid signature.
Routes for Users routes/user.js . Please remember that it is the same for Business.
const express = require('express');
// this router deals better with "try{} catch{}" situations"
const router = require('express-promise-router')();
const passport = require('passport');
const UserController = require('../controllers/user');
require('../passport');
const { validateBody, schemas, validateParam } = require('../helpers/route-helpers');
const StrategyLocal = passport.authenticate('local', {session: false});
const StrategyJWT = passport.authenticate('jwt', {session: false});
router.get('/', (req, res) => {
res.json({message: "Welcome to our User's API Endpoint!"});
});
router.route('/signup')
// .get(UserController.index)
.post([validateBody(schemas.authSchema)], UserController.signup);
router.route('/signin')
// Makes sense to implement localstrat here because jWt will always work because the user was issued a token at signup
// So both solutions will work here as a strategy
.post(validateBody(schemas.authSchema), StrategyLocal, UserController.signin);
module.exports = router;
And finally, the file that I think is causing the problem... Introducing...
app.js
const express = require('express');
const logger = require('morgan');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const passport = require('passport');
const path = require('path');
const cors = require('cors');
const { dbName } = require('./config');
mongoose.Promise = global.Promise;
mongoose.connect('mongodb://localhost/' + dbName, {
useNewUrlParser:true,
useUnifiedTopology:true,
useCreateIndex: true,
useFindAndModify: false
});
const app = express();
// 👇👇👇👇 This is where the problem is happening in my opinion
require('./passport-business')
require('./passport')
const user = require('./routes/user');
const business = require('./routes/business');
// Middlewares
// set the static files location /public/img will be /img for users
app.use(logger('dev'));
app.use(cors());
app.use(bodyParser.urlencoded({ extended: false}));
app.use(bodyParser.json());
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization, noauth");
if (req.method === 'OPTIONS'){
res.header("Access-Control-Allow-Methods", "PUT, POST, PATCH, DELETE, OPTIONS");
res.setHeader('Access-Control-Allow-Credentials', false);
return res.status(200).json({});
}
next();
});
app.use('/api/users', user);
app.use('/api/businesses', business);
// Start the server
const port = app.get('port') || 2019;
app.listen(port, () => console.log(`Server is listening on port ${port}`));
If I keep everything as is, all User endpoints work, signup works for Business, but I get 401 when I try to signin (Business). But if I do this...
require('./passport')
require('./passport-business')
I get 401 when I try to signin (User) and I get 500 when I try to signin (Business).
Can someone tell me what I am doing wrong here? Maybe it is the order in which I load these files in app.js. Maybe it is the fact that I am creating two separate passport files for User and Business. I would love to know how to combine those two into one. .
So in passport.js you do this:
try {
// Find the user specified in token
const user = await User.findById(payload.sub);
// Handle if User dont exist
if (!user) {
return done(null, false);
}
// Otherwise, return the user
req.user = user;
done(null, user);
} catch (error) {
done(error, false);
}
Instead of cloning the file, couldn't you get userType that you're already signing into the JWT and make a conditional?
ie:
try {
// Find the user specified in token
let user = null;
if (payload.userType === 'user') {
user = await User.findById(payload.sub);
}
if (payload.userType === 'business') {
user = await Business.findById(payload.sub);
}
// Handle if User dont exist
if (!user) {
return done(null, false);
}
// Otherwise, return the user
req.user = user;
done(null, user);
} catch (error) {
done(error, false);
}
Then were you require two passport files, you'd require just the one
Hello I have a question about JWT, auth:
I created my auth class:
const passport = require('passport');
const Strategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
module.exports = (app) => {
const jwtConfig = app.config.jwt;
const Users = app.datasource.models.tb_users;
const options = {};
options.secretOrKey = jwtConfig.secret;
options.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
const strategy = new Strategy(options,(payload, done) => {
Users
.findOne({where: payload.id})
.then(user => {
if(user){
return done(null,{
id: user.id,
login: user.login
});
}
return done(null,false);
})
.catch(error => done(error,null));
});
passport.use(strategy);
return {
initialize: () => passport.initialize(),
authenticate: () => passport.authenticate('jwt', jwtConfig.session)
};
}
this is my app.js:
const express = require('express');
const bodyParser = require('body-parser');
const indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');
const authRouter = require('./routes/auth');
const authorization = require('./auth');
const config = require('./config/config');
const datasource = require('./config/datasource');
const Email = require('./utils/email');
const app = express();
const port = 3000;
app.set('port',port);
app.config = config;
app.email = new Email(app.config);
app.datasource = datasource(app);
console.log(app.config);
app.use(bodyParser.json({
limit: '5mb'
}));
const auth = authorization(app);
app.use(auth.initialize());
app.auth = auth;
indexRouter(app);
usersRouter(app);
authRouter(app);
module.exports = app;
And then I have my token login and validation method
app.route('/login')
.post(async (req,res)=>{
try {
const response = await usersControllers.signin(req.body);
const login = response.login;
console.log(login);
if(login.id && login.isValid){
const payload = {id: login.id};
res.json({
token: jwt.sign({data:payload}, app.config.jwt.secret,{expiresIn: '1h'})
});
}else{
console.log('entrou here');
res.sendStatus(HttpStatus.UNAUTHORIZED);
}
} catch (error) {
console.log('entrou here');
console.error(error.message);
res.sendStatus(HttpStatus.UNAUTHORIZED);
}
})
and in my method of searching all users I call this authorization:
app.route('/users')
.all(app.auth.authenticate())
.get((req,res)=>{
usersController
.getAll()
.then(data => {
res.json(data);
})
.catch(error=>{
console.log(error);
res.status(400);
});
})
here: .all(app.auth.authenticate())
At this point I start to get confused
in insomnia he if I go in the login route and then in the get all users route he gives unauthorized even though being authorized by the login route
I would like to know what I could do so I could use my get route with my token
Is this front end work?
Basically I would like to know how I would do to access other routes with my token other than insomnia
And what I could improve on my code is missing something to achieve this?
Not exactly clear what is being asked and you are referencing "insomnia" which is not represented in the code you have shared. You have asked about the token work on the frontend and will share some thoughts.
You need to save the token on the client when it is returned from POST to /login. As you have specified the that passport should attempt to extract the token from the request headers: options.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken(); you will need to send the token within your headers when you are attempting to access secured routes. This is added as key/value to "Authorization" in the format "Bearer " + token.
For example using chaihttp test runner to post data to a secured route:
chai.request(server)
.post(`/books`)
.set('Authorization', `Bearer ${token.token}`) // set the auth header with the token
.send(data)
...
I've been trying to test the protected routes by using passport and passport-jwt.
I've got it to the point where the token is being generated when a user tries to log in and tested it in Postman.
I've created a route and passed in as an argument passport.authenticate with the jwt strategy and am getting errors all over the place.
In my main server.js, I require passport:
passport = require('passport');
app.use(passport.initialize());
// passport config
require('./config/passport')(passport);
My folder structure is this:
in my passport config file, i have this:
const jwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const mongoose = require('mongoose');
const User = mongoose.model('users')
const keys = require('../config/keys');
const opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = keys.secretOrKey;
module.export = passport => {
passport.use(
new jwtStrategy(opts, (jwt_payload, done) => {
console.log(jwt_payload);
}));
};
And my route is this:
// #route get to /users/current
// #desc: return the current user (who the jwt token belongs to)
// #access: should be public
router.get('/current',
passport.authenticate('jwt', { session: false }),
(req, res) => {
res.json({msg: "Success"})
}
);
The first error I can't seem to get passed is this in the console:
require('./config/passport')(passport);
^
TypeError: require(...) is not a function
In postman, when I try to go to /users/current and pass in a confirmed bearer token, I get this:
Error: Unknown authentication strategy "jwt" at attempt
in passport config file
you have typo module.export actualy its module.exports
thats why after require it does not recongnizing it as function
change the module.export to
module.exports = passport => {
passport.use(
new jwtStrategy(opts, (jwt_payload, done) => {
console.log(jwt_payload);
}));
};
its module.exports and not module.export.
The module.exports property can be assigned a new value (such as a function or object).
module.exports = class Square {
constructor(width) {
this.width = width;
}
area() {
return this.width ** 2;
}
};
nodejs modules documentation reference
I'm having trouble authenticating with passportjs and jwt. I'm sending an authorization header in a get request and reciveve an Unauthorized response. My first thought was that I'm probably messing up something with the token, so I'm trying to log the jwt_payload.
It seems to me like passport.authenticate() does not even run any of the code in my passport.js file, as console.log(jwt_payload) below never logs anything
/config/passport.js
module.exports = function(passport) {
let opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = config.secret;
passport.use(new JwtStrategy(opts, (jwt_payload, done) => {
console.log(jwt_payload);
User.getUserById(jwt_payload._id, (err, user) => {
if(err){
return done(err,false);
}
if(user){
return done(null,user);
} else {
return done(null, false);
}
});
}));
}
The passport.use(...) clause above must however be doing something, if i remove it i get an error of Unknown authentication strategy "jwt"
Below is the code for my passport setup and passport.authenticate() call.
/routes/users.js
const passport = require('passport');
const jwt = require('jsonwebtoken');
const config = require('../config/database');
router.get('/profile', passport.authenticate('jwt', {session:false}), (req, res, next) => {
res.json({user: req.user});
});
app.js
const passport = require('passport');
app.use(passport.initialize());
app.use(passport.session());
require('./config/passport')(passport);