-
-
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: 'hello@example.com', | |
| 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: 'passwordreset@example.com', | |
| 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: 'passwordreset@example.com', | |
| 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