#Content table:
- Preface
- Installation
- Create the authentication router
- Implementation of a JWT protected router
- Test the JWT authentication and authorization
- Conclusions
- A few words about https and http
- all the code files /routers/auth.js, /routers/users.js, /app/passport.js and app.js
- Resources
#Preface
JWT is a json web token. It is used for authentication between server and client. So to implement it in a node.js we are going to use such modules:
- passport – to set up log in middleware
- passport-local – to create a user authentication router
- passport-jwt – to create a user authorization router
- jsonwebtoken – to generate a JWT itself
So let’s get started.
#Installation
we have to install four this module
1 |
npm i passport passport-local passport-jwt jsonwebtoken |
#Create the authentication router.
We need a router where a user will send username and password and we will check it and send back a JWT. To implement this we’ll need a passport and its passport.authentication middleware where we use a passport-local.
let’s create a file /routers/auth.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
const express = require('express') const router = express.Router() const jwt = require('jsonwebtoken') const passport = require('passport') /* POST login. */ router.post('/login', (req, res, next) => { passport.authenticate('local', {session: false}, (err, user, info) => { console.log('here we are2 user = ', user); if (err || !user) { return res.status(400).json({ message: 'Something is not right', user: user }) } req.login(user, {session: false}, (err) => { if (err) { res.send(err) } // generate a signed json web token with the contents of user object and return it in the response const token = jwt.sign({user}, 'your_jwt_secret') return res.json({ user, token }) }) })(req, res) }) module.exports = router |
and include somewhere in the app.js
1 2 3 |
... app.use('/auth', require('./routes/auth.js')) ... |
here is
1 2 3 |
... req.login(user, {session: false}, (err) => { ... |
req.login is a passport function which logs in the user it accepts callback function where we generate jwt by using a nodejs module jsonwebtoken
1 2 3 |
... const token = jwt.sign({user}, 'your_jwt_secret') ... |
we need a your_jwt_secret and this function jwt.sign accept a lot of parameters
1 2 3 |
... passport.authenticate('local', {session: false}, (err, user, info) => { ... |
here is a call passport.authenticate function where we apply a “local” as a first parameter that will invoke a passport-local strategy and its strategy will have a code of authentication users. So let’s implement this piece of code
Let’s create a file /app/passport.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
const LocalStrategy = require('passport-local').Strategy module.exports = (passport) => { passport.use(new LocalStrategy( (username, password, done) => { console.log('here we are'); // here should be a look up to the database for username and password and comparison with an accepted username and password user = "neo" return done(null, user, { message: 'Logged In Successfully' }) })) } |
passport.use is a function, which accepts strategies, in this case, LocalStrategy and a callback function where we pass username, password and a callback function done. Here should be a lookup to the database or whatever for username and password and compare with a passed username and password and if they equal we’ll return a callback done with a user.
Ok, so far so good! We have an authentication router which generates and return a JWT token. The next we need to create a protected router where users will send their requests with tokens and get resources
#Implementation of a JWT protected router
A router will have a url site.com/users/*. Everything being requested to site.com/users/* will require JWT authentication. To realize JWT authentication we’are going to use passport-jwt strategy. So let’s add the strategy to the /app/password.js file where is already a local strategy
a file /app/passport.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
... const passportJWT = require('passport-jwt') const JWTStrategy = passportJWT.Strategy const ExtractJWT = passportJWT.ExtractJwt module.exports = (passport) => { ... passport.use(new JWTStrategy({ jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(), secretOrKey: 'your_jwt_secret' }, (jwtPayload, done) => { // find the user in db if needed. This functionality may be omitted if you store everything you'll need in JWT payload. console.log('jwtPayload = ', jwtPayload) const user = "here I am" return done(null, user) })) } |
we need ExtractJWT which is a special object of a passport-jwt strategy
1 2 3 |
... const ExtractJWT = passportJWT.ExtractJwt ... |
which helps us extract a jwt token from the request
1 2 3 |
... jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(), ... |
The token uses a bearer header. It’s a special header which is used to pass a JWT from a client to a server, they are nothing special but text. As we can see there is a special function fromAuthHeaderAsBearerToken to get it.
1 2 3 |
... }, (jwtPayload, done) => { ... |
JWTStrategy accepts a unanimous callback function, which accepts two arguments jwtPayload and callback done to send the data to the user. Here we can do a lookup to the database to find additional user parameters or not if you enough from the payload.
So, the next left to create a router itself.
Let’s create a file /routers/users.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
const express = require('express') const router = express.Router() /* GET users listing. */ router.get('/', (req, res, next) => { res.send('respond with a resource') }) /* GET user profile */ router.get('/profile', (req, res, next) => { res.send(req.user) }) module.exports = router |
here is nothing special to explain.
Now, let’s include the file to the express middleware.
Somewhere in the app.js
1 2 3 4 5 |
... app.use('/users', passport.authenticate('jwt', {session: false}), require('./routes/users.js')) ... |
here is we add a passport.authenticate middleware function before including the router. As you can see it uses ‘jwt‘ authenticate strategy that was already be created in the /app/passport.js file.
That’s it!
#Test the JWT authentication and authorization
We’ll be using a postman to test, here is an animation how it is going on
#Conclusions
So now, everything should work. We created the authenticate login router and the protected router.
The authenticate login router uses passport-local strategy and the protected router uses passport-jwt strategy.
The authenticate login router generates a JWT token by using a nodejs module jsonwebtoken
Clients have to pass the token as a bearer header and it will be extracted in the protected router in the passport-jwt authentication callback function. So that’s it! A simple JWT + passport.js authentication is done.
#A few words about https and http
It is inappropriate to use JWT token over http. Why? Because somebody in the middle can get the token as http passes data as plain text. Using JWT token everybody can connect to the protected router. So we have to encrypt the passed data by using https. https encrypts everything even headers so that the JWT token is protected.
Always use https with JWT. Never pass data as plain text
#all the code files /routers/auth.js, /routers/users.js, /app/passport.js and app.js
/routers/auth.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
const express = require('express') const router = express.Router() const jwt = require('jsonwebtoken') const passport = require('passport') /* POST login. */ router.post('/login', (req, res, next) => { passport.authenticate('local', {session: false}, (err, user, info) => { console.log('here we are2 user = ', user); if (err || !user) { return res.status(400).json({ message: 'Something is not right', user: user }) } req.login(user, {session: false}, (err) => { if (err) { res.send(err) } // generate a signed json web token with the contents of user object and return it in the response const token = jwt.sign({user}, 'your_jwt_secret') return res.json({ user, token }) }) })(req, res) }) module.exports = router |
/routers/users.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
const express = require('express') const router = express.Router() /* GET users listing. */ router.get('/', (req, res, next) => { res.send('respond with a resource') }) /* GET user profile */ router.get('/profile', (req, res, next) => { res.send(req.user) }) module.exports = router |
/app/passport.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
const LocalStrategy = require('passport-local').Strategy const passportJWT = require('passport-jwt') const JWTStrategy = passportJWT.Strategy const ExtractJWT = passportJWT.ExtractJwt module.exports = (passport) => { passport.use(new LocalStrategy( (username, password, done) => { console.log('here we are'); user = "neo" return done(null, user, { message: 'Logged In Successfully' }) })) passport.use(new JWTStrategy({ jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(), secretOrKey: 'your_jwt_secret' }, (jwtPayload, cb) => { // find the user in db if needed. This functionality may be omitted if you store everything you'll need in JWT payload. console.log('jwtPayload = ', jwtPayload) const user = "here I am" return cb(null, user) })) } |
app.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
var createError = require('http-errors'); var express = require('express'); var path = require('path'); var cookieParser = require('cookie-parser'); var logger = require('morgan'); const passport = require('passport') var app = express(); // view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'pug'); app.use(logger('dev')); app.use(express.json()); app.use(express.urlencoded({ extended: false })); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); // we don't use this as we do with sessions // app.use(passport.initialize()) // app.use(passport.session()) app.use('/auth', require('./routes/auth.js')) app.use('/users', passport.authenticate('jwt', {session: false}), require('./routes/users.js')) // catch 404 and forward to error handler app.use(function(req, res, next) { next(createError(404)); }); // error handler app.use(function(err, req, res, next) { // set locals, only providing error in development res.locals.message = err.message; res.locals.error = req.app.get('env') === 'development' ? err : {}; // render the error page res.status(err.status || 500); res.render('error'); }); const server = require('http').Server(app) // adding passport's strategies local and jwt require('./app/passport.js')(passport) module.exports = { app, server }; |
#Resources:
the end