Skip to content

Instantly share code, notes, and snippets.

@AnsonT
Last active March 8, 2024 22:53
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AnsonT/9c9abe50566b56330ec784346b163d34 to your computer and use it in GitHub Desktop.
Save AnsonT/9c9abe50566b56330ec784346b163d34 to your computer and use it in GitHub Desktop.
Simplest Passport/JWT implementation with cookie and bearer authentication support
import express from 'express'
import bodyParser from 'body-parser'
import cookieParser from 'cookie-parser'
import passport from 'passport'
import passportJWT, { ExtractJwt, Strategy as JwtStrategy } from 'passport-jwt'
import bcrypt from 'bcrypt'
import jwt from 'jsonwebtoken'
const { fromExtractors, fromAuthHeaderWithScheme, fromAuthHeaderAsBearerToken } = ExtractJwt
// Trival user manager class -----------------------------------------------------
class Users {
constructor() {
this._users = {}
}
async registerUser(userName, password, profile = {}) {
if (this._users[userName]) {
return null
}
const passwordHash = await bcrypt.hash(password, Users._SALT_ROUNDS)
this._users[userName] = {
userName,
passwordHash,
profile
}
return this._users[userName]
}
async validateUser(userName, password) {
const user = this._users[userName]
if (!user || !await bcrypt.compare(password, user.passwordHash)) {
return null
}
return user
}
async changePassword(userName, oldPassword, newPassword) {
const user = this._users[userName]
if (!user) {
return null
}
if (await this.validateUser(userName, oldPassword)) {
this._users[userName].passwordHash = await bcrypt.hash(newPassword, Users._SALT_ROUNDS)
}
}
getUserById(userName) {
const user = this._users[userName]
return user
}
}
Users._SALT_ROUNDS = 10
const users = new Users()
// Server setup -----------------------------------------------------
function cookieExtractor(req) {
var token = null;
if (req && req.cookies)
{
token = req.signedCookies[jwtOptions.jwtCookieName];
}
return token;
};
const port = process.env.PORT || 8080
const app = express()
const jwtOptions = {
jwtFromRequest: fromExtractors([cookieExtractor, fromAuthHeaderAsBearerToken()]),
secretOrKey: 'secret',
issuer: 'mydomain.com',
audience: 'api.mydomain.com',
jwtCookieName: 'jwt'
}
const strategy = new JwtStrategy(jwtOptions,
async (jwt_payload, next) => {
console.log('payload received', jwt_payload);
// usually this would be a database call:
const user = await users.getUserById(jwt_payload.sub)
if (user) {
next(null, user);
} else {
next(null, false);
}
}
)
passport.use(strategy);
app.use(passport.initialize())
app.use(bodyParser.urlencoded({ extended: true }))
app.use(bodyParser.json())
app.use(cookieParser(jwtOptions.secretOrKey))
app.get('/', (req, res) => {
res.json({ message: 'up!' })
})
app.post('/register', async (req, res) => {
const { userName, passwd } = req.body
const user = await users.registerUser(userName, passwd)
if (!user) {
res.status(422).json({ message: "cannot register user"})
} else {
res.status(201).json({ message: "User Registered"})
}
})
app.post('/login', async (req, res) => {
const { userName, passwd } = req.body
const user = await users.validateUser(userName, passwd)
if (!user) {
res.status(401).json({ message: 'invalid login'})
}
else {
const payload = {
sub: user.userName,
aud: jwtOptions.audience,
iss: jwtOptions.issuer
}
const token = jwt.sign(payload, jwtOptions.secretOrKey)
res
.cookie(jwtOptions.jwtCookieName, token, {httpOnly: true, secure: true, signed: true})
.json({ message: 'ok', token: token })
}
})
function debugJWT(req, res, next) {
console.log(req.get('Authorization'))
next()
}
app.get('/secret', debugJWT, passport.authenticate('jwt', { session: false }),
(req, res) => {
res.json({ "message": "Success"})
})
app.listen(port)
console.log(`Running on port ${port}`)
{
"devDependencies": {
"neutrino": "^7.0.1",
"neutrino-preset-node": "^7.0.1",
"source-map-support": "^0.4.18"
},
"scripts": {
"start": "neutrino start",
"build": "neutrino build"
},
"dependencies": {
"bcrypt": "^1.0.3",
"body-parser": "^1.18.2",
"cookie-parser": "^1.4.3",
"express": "^4.15.5",
"jsonwebtoken": "^8.0.1",
"passport": "^0.4.0",
"passport-jwt": "^3.0.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment