const express = require('express'); const bodyParser = require('body-parser'); const jwt = require('jsonwebtoken'); const randtoken = require('rand-token'); const passport = require('passport'); const { Strategy: JwtStrategy, ExtractJwt } = require('passport-jwt'); const refreshTokens = {}; const SECRET = "sauce"; const options = { jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), secretOrKey: SECRET }; passport.use(new JwtStrategy(options, (jwtPayload, done) => { const expirationDate = new Date(jwtPayload.exp * 1000); if (new Date() >= expirationDate) { return done(null, false); } const user = jwtPayload; done(null, user); })); const app = express(); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({extended: true})); app.use(passport.initialize()); app.post('/login', (req, res, next) => { const { username, password } = req.body; const accessToken = generateAccessToken(username, getRole(username)); const refreshToken = randtoken.uid(42); refreshTokens[refreshToken] = username; res.json({accessToken, refreshToken}); }); function generateAccessToken(username, role, expiresInSeconds = 60) { const user = { username, role }; const accessToken = jwt.sign(user, SECRET, { expiresIn: expiresInSeconds }); return accessToken; } function getRole(username) { switch (username) { case 'linus': return 'admin'; default: return 'user'; } } app.post('/token', (req, res) => { const { username, refreshToken } = req.body; if (refreshToken in refreshTokens && refreshTokens[refreshToken] === username) { const accessToken = generateAccessToken(username, getRole(username)); res.json({accessToken}); } else { res.sendStatus(401); } }); app.delete('/token/:refreshToken', (req, res, next) => { const { refreshToken } = req.params; if (refreshToken in refreshTokens) { delete refreshTokens[refreshToken]; } res.send(204); }); app.post('/restaurant-reservation', passport.authenticate('jwt', {session: false}), (req, res) => { const { user } = req; const { guestsCount } = req.body; res.json({user, guestsCount}); }); app.get('/user-accessible', authorize(), (req, res) => { res.json({message: 'for all users', user: req.user}); }); app.get('/admin-accessible', authorize('admin'), (req,res) => { res.json({message: 'for admins only', user: req.user}); }); function authorize(roles = []) { if (typeof roles === 'string') { roles = [roles]; } return [ passport.authenticate('jwt', {session: false}), (req, res, next) => { if (roles.length > 0 && !roles.includes(req.user.role)) { return res.status(403).json({message: 'No access'}); }; return next(); } ]; } app.listen(8080);
Test login for non-admin:
$ curl -i -H "Content-Type: application/json" --request POST --data '{"username": "richard", "password": "stallman"}' http://localhost:8080/login
Output:
HTTP/1.1 200 OK X-Powered-By: Express Content-Type: application/json; charset=utf-8 Content-Length: 253 ETag: W/"fd-eFe0WyxYJMwm+IYU2RknNmwLN7c" Date: Thu, 04 Jul 2019 11:18:34 GMT Connection: keep-alive {"accessToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InJpY2hhcmQiLCJyb2xlIjoidXNlciIsImlhdCI6MTU2MjIzOTExNCwiZXhwIjoxNTYyMjM5MTc0fQ.eVMIVLoq66wwnvX7R7_YE_Va5uUIupcWqZFJIql2VOo","refreshToken":"ZExtyNqF19XwCF8htNABs9rzwDV5lltlEQxGAGVaeV"}
Test non-admin role against user-accessible resource:
$ curl -i -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InJpY2hhcmQiLCJyb2xlIjoidXNlciIsImlhdCI6MTU2MjIzOTExNCwiZXhwIjoxNTYyMjM5MTc0fQ.eVMIVLoq66wwnvX7R7_YE_Va5uUIupcWqZFJIql2VOo" --request GET http://localhost:8080/user-accessible
Output:
HTTP/1.1 200 OK X-Powered-By: Express Content-Type: application/json; charset=utf-8 Content-Length: 103 ETag: W/"67-cGYUdSCA9Hfxbr3kmo6EfcwJGFk" Date: Thu, 04 Jul 2019 11:19:09 GMT Connection: keep-alive {"message":"for all users","user":{"username":"richard","role":"user","iat":1562239114,"exp":1562239174}}
Test non-admin role against admin-accessible resource:
$ curl -i -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InJpY2hhcmQiLCJyb2xlIjoidXNlciIsImlhdCI6MTU2MjIzOTExNCwiZXhwIjoxNTYyMjM5MTc0fQ.eVMIVLoq66wwnvX7R7_YE_Va5uUIupcWqZFJIql2VOo" --request GET http://localhost:8080/admin-accessible
Output:
HTTP/1.1 403 Forbidden X-Powered-By: Express Content-Type: application/json; charset=utf-8 Content-Length: 23 ETag: W/"17-wr3eIgD4+9Bp6mVHZCD0DXWfISk" Date: Thu, 04 Jul 2019 11:19:16 GMT Connection: keep-alive {"message":"No access"}
Test login for admin:
$ curl -i -H "Content-Type: application/json" --request POST --data '{"username": "linus", "password": "torvalds"}' http://localhost:8080/login
Output:
HTTP/1.1 200 OK X-Powered-By: Express Content-Type: application/json; charset=utf-8 Content-Length: 251 ETag: W/"fb-ffoTYONCm1BVpI+gaFqCXe7AX2g" Date: Thu, 04 Jul 2019 11:23:05 GMT Connection: keep-alive {"accessToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImxpbnVzIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNTYyMjM5Mzg1LCJleHAiOjE1NjIyMzk0NDV9.hU09-ESu5VADYgC12R-CBtLga4lmGnpGC1AAwxg7t_Y","refreshToken":"8RItXk4v6kV8W68paZk3av34vj3oV5z1vnQdSLAZG7"}
Test admin role against admin-accessible resource:
$ curl -i -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImxpbnVzIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNTYyMjM5Mzg1LCJleHAiOjE1NjIyMzk0NDV9.hU09-ESu5VADYgC12R-CBtLga4lmGnpGC1AAwxg7t_Y" --request GET http://localhost:8080/admin-accessible
Output:
HTTP/1.1 200 OK X-Powered-By: Express Content-Type: application/json; charset=utf-8 Content-Length: 104 ETag: W/"68-MXNiAugqialVCopW9uv3AMKWHjU" Date: Thu, 04 Jul 2019 11:23:42 GMT Connection: keep-alive {"message":"for admins only","user":{"username":"linus","role":"admin","iat":1562239385,"exp":1562239445}}
And of course user-accessible resource is available to admin role too:
$ curl -i -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImxpbnVzIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNTYyMjM5Mzg1LCJleHAiOjE1NjIyMzk0NDV9.hU09-ESu5VADYgC12R-CBtLga4lmGnpGC1AAwxg7t_Y" --request GET http://localhost:8080/user-accessible
Output:
HTTP/1.1 200 OK X-Powered-By: Express Content-Type: application/json; charset=utf-8 Content-Length: 102 ETag: W/"66-fLBX3f6AfyVZNVcfBNUakwJko5w" Date: Thu, 04 Jul 2019 11:23:51 GMT Connection: keep-alive {"message":"for all users","user":{"username":"linus","role":"admin","iat":1562239385,"exp":1562239445}}
No comments:
Post a Comment