-
-
Save nemtsov/6c2c24fa565a29404b487c61ce5bae4f to your computer and use it in GitHub Desktop.
const crypto = require('crypto'); | |
const { promisify } = require('util'); | |
const express = require('express'); | |
const asyncify = require('express-asyncify'); | |
const session = require('express-session'); | |
const createFileStore = require('session-file-store'); | |
const nodemailer = require('nodemailer'); | |
const nodemailerSendgrid = require('nodemailer-sendgrid'); | |
const bodyParser = require('body-parser'); | |
const pass = require('passport'); | |
const LocalStrategy = require('passport-local'); | |
const { Strategy: GoogleStrategy } = require('passport-google-oauth20'); | |
const flash = require('connect-flash'); | |
const templates = require('./templates-passport'); | |
const PORT = 5000; | |
const SESSION_COOKIE_SECRET = ''; | |
const SESSOIN_COOKIE_MAX_AGE_IN_MS = 60 * 60 * 1000; | |
const SESSION_COOKIE_IS_SECURE = false; | |
const GOOGLE_CLIENT_ID = ''; | |
const GOOGLE_CLIENT_SECRET = ''; | |
const SENDGRID_API_KEY = ''; | |
const transport = nodemailer.createTransport(nodemailerSendgrid({ | |
apiKey: SENDGRID_API_KEY, | |
})); | |
const users = [{ | |
id: 'local/a0234aDdfj-2f4sdfa3oEerq-2U4', | |
fullName: 'A Ayevich', | |
email: '[email protected]', | |
password: 'password' | |
}]; | |
pass.serializeUser((user, cb) => cb(null, user)); | |
pass.deserializeUser((u, cb) => cb(null, u)); | |
pass.use(new LocalStrategy({ | |
usernameField: 'email', | |
}, (email, password, cb) => { | |
const user = users.find(u => u.email === email); | |
cb(null, (user && user.password === password) ? user : false); | |
})); | |
pass.use(new GoogleStrategy({ | |
clientID: GOOGLE_CLIENT_ID, | |
clientSecret: GOOGLE_CLIENT_SECRET, | |
callbackURL: `http://localhost:${PORT}/auth/google/callback` | |
}, (accessToken, refreshToken, profile, cb) => { | |
const user = { | |
id: `google/${profile.id}`, | |
email: profile.email, | |
fullName: profile.displayName, | |
profile, | |
tokens: { accessToken, refreshToken }, | |
}; | |
users.push(user); | |
cb(null, user); | |
})); | |
const app = asyncify(express()); | |
const FileStore = createFileStore(session); | |
app.disable('x-powered-by'); | |
app.use(session({ | |
store: new FileStore(), | |
name: 'sid', | |
resave: false, | |
saveUninitialized: false, | |
secret: SESSION_COOKIE_SECRET, | |
cookie: { | |
maxAge: SESSOIN_COOKIE_MAX_AGE_IN_MS, | |
secure: SESSION_COOKIE_IS_SECURE, | |
sameSite: 'lax', | |
}, | |
})); | |
app.use(flash()); | |
app.use(bodyParser.urlencoded({ extended: false })); | |
app.use(pass.initialize()); | |
app.use(pass.session()); | |
app.get('/', (req, res) => { | |
res.setHeader('Content-type', 'text/html'); | |
res.end(templates.layout(` | |
${req.user ? | |
templates.loggedInGreeting(req.user) : | |
templates.loggedOut()} | |
`)); | |
}); | |
app.get('/login', (req, res) => { | |
res.setHeader('Content-type', 'text/html'); | |
res.end(templates.layout(` | |
${templates.error(req.flash())} | |
${templates.loginForm()} | |
`)); | |
}); | |
app.get('/signup', (req, res) => { | |
res.setHeader('Content-type', 'text/html'); | |
res.end(templates.layout(` | |
${templates.error(req.flash())} | |
${templates.signupForm()} | |
`)); | |
}); | |
app.post('/login', pass.authenticate('local', { | |
successRedirect: '/', | |
failureRedirect: '/login', | |
failureFlash: true, | |
})); | |
app.get('/forgot', (req, res, next) => { | |
res.setHeader('Content-type', 'text/html'); | |
res.end(templates.layout(` | |
${templates.error(req.flash())} | |
${templates.forgotPassword()} | |
`)); | |
}); | |
app.post('/forgot', async (req, res, next) => { | |
const token = (await promisify(crypto.randomBytes)(20)).toString('hex'); | |
const user = users.find(u => u.email === req.body.email); | |
if (!user) { | |
req.flash('error', 'No account with that email address exists.'); | |
return res.redirect('/forgot'); | |
} | |
user.resetPasswordToken = token; | |
user.resetPasswordExpires = Date.now() + 3600000; | |
const resetEmail = { | |
to: user.email, | |
from: '[email protected]', | |
subject: 'Node.js Password Reset', | |
text: ` | |
You are receiving this because you (or someone else) have requested the reset of the password for your account. | |
Please click on the following link, or paste this into your browser to complete the process: | |
http://${req.headers.host}/reset/${token} | |
If you did not request this, please ignore this email and your password will remain unchanged. | |
`, | |
}; | |
await transport.sendMail(resetEmail); | |
req.flash('info', `An e-mail has been sent to ${user.email} with further instructions.`); | |
res.redirect('/forgot'); | |
}); | |
app.get('/reset/:token', (req, res) => { | |
const user = users.find(u => ( | |
(u.resetPasswordExpires > Date.now()) && | |
crypto.timingSafeEqual(Buffer.from(u.resetPasswordToken), Buffer.from(req.params.token)) | |
)); | |
if (!user) { | |
req.flash('error', 'Password reset token is invalid or has expired.'); | |
return res.redirect('/forgot'); | |
} | |
res.setHeader('Content-type', 'text/html'); | |
res.end(templates.layout(` | |
${templates.error(req.flash())} | |
${templates.resetPassword(user.resetPasswordToken)} | |
`)); | |
}); | |
app.post('/reset/:token', async (req, res) => { | |
const user = users.find(u => ( | |
(u.resetPasswordExpires > Date.now()) && | |
crypto.timingSafeEqual(Buffer.from(u.resetPasswordToken), Buffer.from(req.params.token)) | |
)); | |
if (!user) { | |
req.flash('error', 'Password reset token is invalid or has expired.'); | |
return res.redirect('/forgot'); | |
} | |
user.password = req.body.password; | |
delete user.resetPasswordToken; | |
delete user.resetPasswordExpires; | |
const resetEmail = { | |
to: user.email, | |
from: '[email protected]', | |
subject: 'Your password has been changed', | |
text: ` | |
This is a confirmation that the password for your account "${user.email}" has just been changed. | |
`, | |
}; | |
await transport.sendMail(resetEmail); | |
req.flash('success', `Success! Your password has been changed.`); | |
res.redirect('/'); | |
}); | |
app.get('/auth/google', | |
pass.authenticate('google', { scope: ['profile'] })); | |
app.get('/auth/google/callback', | |
pass.authenticate('google', { failureRedirect: '/login' }), | |
(req, res) => res.redirect('/')); | |
app.post('/signup', (req, res, next) => { | |
const user = { | |
id: 'local/a0234aDdfj-2f4sdfa3oEerq-2U4', | |
fullName: 'Boy Good', | |
email: req.body.email, | |
password: req.body.password, | |
}; | |
users.push(user); | |
req.login(user, (err) => { | |
if (err) next(err); | |
else res.redirect('/'); | |
}); | |
}); | |
app.listen(PORT, () => console.log(`on :${PORT}`)); |
@king9759 templates are a set of exported functions that return strings. The format looks like this:
module.exports = {
layout(body) {
return `
<html>
<body>
${body}
</body>
</html>
`;
}
};
And the app.post('/reset/:token', async (req, res) => {
route is there to allow a user to reset their password, provided they send us a valid token; one that we generated in https://gist.github.com/nemtsov/6c2c24fa565a29404b487c61ce5bae4f#file-passport-auth-with-reset-js-L158
Can you refactor the template lines with normal res.render ... It is Confusing me....... if I render reset file above the async function. How will I make it work, BY passing user.resetPasswordToken or what????
Friendly suggestion:
move line 126:
const token = (await promisify(crypto.randomBytes)(20)).toString('hex');
below the
if (!user) { req.flash('error', 'Password reset token is invalid or has expired.'); return res.redirect('/forgot'); }
(waste of time creating token if no user)
Is there way using pass.use(new GoogleStrategy({...});, by input email and password in Nodejs, i need to get "accesstoken" ?
Hi,
What is ./template-passport and how to get that??
Also please elaborate the async reset route