Last active
February 4, 2025 22:29
-
-
Save AnsonT/9c9abe50566b56330ec784346b163d34 to your computer and use it in GitHub Desktop.
Simplest Passport/JWT implementation with cookie and bearer authentication support
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}`) | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"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