Last active
April 19, 2018 23:24
-
-
Save luishrd/5f1d32223518c530752bb937dc077b4f to your computer and use it in GitHub Desktop.
This file contains 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
{ | |
"username": "peregrin", | |
"password": "took" | |
} |
This file contains 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
const express = require('express'); | |
const morgan = require('morgan'); | |
const helmet = require('helmet'); | |
const cors = require('cors'); | |
module.exports = function(server) { | |
server.use(helmet()); | |
server.use(morgan('dev')); | |
server.use(express.json()); | |
server.use(cors()); | |
}; |
This file contains 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
{ | |
"name": "jwtting", | |
"version": "1.0.0", | |
"description": "", | |
"main": "server.js", | |
"scripts": { | |
"start": "nodemon server.js" | |
}, | |
"keywords": [], | |
"author": "", | |
"license": "ISC", | |
"devDependencies": { | |
"nodemon": "^1.17.3" | |
}, | |
"dependencies": { | |
"bcrypt": "^2.0.0", | |
"cors": "^2.8.4", | |
"express": "^4.16.3", | |
"helmet": "^3.12.0", | |
"jsonwebtoken": "^8.2.1", | |
"mongoose": "^5.0.15", | |
"morgan": "^1.9.0", | |
"passport": "^0.4.0", | |
"passport-jwt": "^4.0.0", | |
"passport-local": "^1.0.0" | |
} | |
} |
This file contains 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
const jwt = require('jsonwebtoken'); | |
const passport = require('passport'); | |
const LocalStrategy = require('passport-local'); | |
const User = require('../users/User'); | |
const secret = 'no size limit on tokens'; | |
const { ExtractJwt } = require('passport-jwt'); | |
const JwtStrategy = require('passport-jwt').Strategy; | |
// { usernameField: email } | |
const localStrategy = new LocalStrategy(function(username, password, done) { | |
User.findOne({ username }, function(err, user) { | |
if (err) { | |
done(err); | |
} | |
if (!user) { | |
done(null, false); | |
} | |
user.verifyPassword(password, function(err, isValid) { | |
if (err) { | |
return done(err); | |
} | |
if (isValid) { | |
const { _id, username, race } = user; | |
return done(null, { _id, username, race }); // placed on req.user | |
} | |
return done(null, false); | |
}); | |
}); | |
}); | |
const jwtOptions = { | |
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), | |
secretOrKey: secret, | |
}; | |
const jwtStrategy = new JwtStrategy(jwtOptions, function(payload, done) { | |
User.findById(payload.sub) | |
.select('-password') | |
.then(user => { | |
if (user) { | |
done(null, user); | |
} else { | |
done(null, false); | |
} | |
}) | |
.catch(err => { | |
return done(err, false); | |
}); | |
}); | |
passport.use(localStrategy); | |
passport.use(jwtStrategy); // new line | |
const authenticate = passport.authenticate('local', { session: false }); | |
const protected = passport.authenticate('jwt', { session: false }); // new line | |
module.exports = function(server) { | |
server.get('/api/hobbits', protected, (req, res) => { | |
User.find({ race: 'hobbit' }) | |
.select('-password') | |
.then(hobbits => { | |
res.json(hobbits); | |
}) | |
.catch(err => { | |
res.status(500).json(err); | |
}); | |
}); | |
server.post('/api/login', authenticate, (req, res) => { | |
res.json({ token: makeToken(req.user), user: req.user }); | |
}); | |
// sanity check route | |
server.get('/', function(req, res) { | |
res.send({ api: 'up and running' }); | |
}); | |
server.post('/api/register', function(req, res) { | |
const credentials = req.body; | |
// add a pre('save') hook to the User schema | |
// that will hash the password before | |
// persisting the user to the database | |
const user = new User(credentials); | |
user.save().then(inserted => { | |
const token = makeToken(inserted); | |
res.status(201).json({ token }); | |
}); | |
}); | |
}; | |
function makeToken(user) { | |
// sub: subject (id) who the token is about | |
// iat: issued at time | |
const timestamp = new Date().getTime(); | |
const payload = { | |
sub: user._id, | |
iat: timestamp, | |
username: user.username, | |
race: user.race, | |
}; | |
const options = { expiresIn: '4h' }; | |
return jwt.sign(payload, secret, options); | |
} |
This file contains 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
const express = require('express'); | |
const mongoose = require('mongoose'); | |
const server = express(); | |
const setupMiddleware = require('./setup/middleware')(server); | |
const setupRoutes = require('./setup/routes')(server); | |
// alternatively we can do this in two steps like so: | |
// we first import the module | |
// const setupMiddleware = require('./setup/middleware'); | |
// then execute it passing the server instance | |
// setupMiddleware(server); | |
mongoose | |
.connect('mongodb://localhost/auth') | |
.then(cnn => { | |
console.log('\n=== connected to mongo ===\n'); | |
}) | |
.catch(err => { | |
console.log('\n=== ERROR connecting to mongo ===\n'); | |
}); | |
server.listen(5000, () => console.log('\n=== API on port 5k ===\n')); |
This file contains 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
const mongoose = require('mongoose'); | |
const bcrypt = require('bcrypt'); | |
const userSchema = new mongoose.Schema({ | |
username: { | |
type: String, | |
required: true, | |
unique: true, | |
lowercase: true, | |
// normalize all users to lowercase | |
// helps later when making queries | |
}, | |
password: { | |
type: String, | |
required: true, | |
minlength: 4, // make this at least 10 or 12 | |
// in a production application | |
}, | |
race: { | |
type: String, | |
required: true, | |
}, | |
}); | |
userSchema.pre('save', function(next) { | |
// const user = this; // scope is the document | |
// you need to do this if the function passed to | |
// then is a regular function, not an arrow | |
// function to make sure the binding of this | |
// is correct. See code below. | |
// bcrypt.hash(this.password, 10).then(function(hash) { | |
bcrypt.hash(this.password, 10).then(hash => { | |
// different scope if using function(hash) | |
// same document scope if using arrow function | |
this.password = hash; | |
next(); | |
}); | |
}); | |
userSchema.methods.verifyPassword = function(guess, callback) { | |
bcrypt.compare(guess, this.password, function(err, isValid) { | |
if (err) { | |
return callback(err); | |
} | |
// here there was no error | |
callback(null, isValid); | |
}); | |
}; | |
module.exports = mongoose.model('User', userSchema, 'users'); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment