Monday, July 13 2020
In this tutorial explains about How to Build Authentication App with OAuth2.0 and Node.Js OAuth2 is a method for authentication for accessing API. OAuth2.0 allows authorization without the external application getting the user’s email address or password. Instead, the external application gets a token that authorizes access to the user’s account.
OAuth is better than other authentication methods. The main difference is OAuth is much secure and credentials can’t be hacked. OAuth is preferred one.
Click here to read about Authentication app using JWT in Node.js
OAuth authentication that allows a user (owner) to grant permission for external application to access their information (resource).
npm init npm install express dotenv express-session generate-password mongoose passport passport-google-oauth ejs --save npm install nodemon --save-dev
After the installation and create following files and folders in your project directory.
touch index.js // Root Directory mkdir oauth mkdir models mkdir routes mkdir views // Authnetication cd oauth touch google.auth.js // Database cd models touch db.js touch user.model.js //Routing cd routes touch user.route.js //Views cd views mkdir pages cd pages touch auth.ejs success.ejs
GOOGLE_CLIENT_ID=<!-- PASTE YOUR GOOGLE CLIENT ID --> GOOGLE_CLIENT_SECRET=<!-- PASTE YOUR GOOGLE CLIENT SECRET --> CALLBACK_API_URL=http://localhost:3000/oauth2callback MONGODB_URI=mongodb://localhost:27017/test
Before create a schema we have to make an connection with mongodb in /models/db.js file with following contents.
// MongoDB connection const mongoose = require('mongoose'); mongoose.Promise = global.Promise; mongoose.connect(process.env.MONGODB_URI, {useUnifiedTopology: true, useNewUrlParser: true, useCreateIndex: true}); const db = mongoose.connection; db.on('error', console.error.bind(console, 'MongoDB connection error'));
Create schema for users table in /models/user.model.js file with following contents
sid means social network id. Its unique one to identify the user. Its get from google oauth2.0
const mongoose = require('mongoose'); const validator = require('validator'); const userSchema = new mongoose.Schema({ name: { type: String, required: true, trim: true, max: 65 }, email: { type: String, required: true, trim: true, unique: true, lowercase: true, max: 65, validator: value => { if (!validator.isEmail(value)) { throw new Error({ error: 'Invalid Email address' }); } } }, password: { type: String, required: true, trim: true, minlength: 8 }, provider: { type: String, required: true, trim: true }, sid: { type: String, required: true, trim: true } }); module.exports = mongoose.model('User', userSchema);
In app.js file with following contents
const express = require('express'); const session = require('express-session'); const passport = require('passport'); require('dotenv').config(); require('./models/db'); const routes = require('./routes/user.route'); const app = express(); app.set('view engine', 'ejs'); app.use(session({ resave: false, saveUninitialized: true, secret: 'SECRET' })); app.use(passport.initialize()); app.use(passport.session()); app.use('/', routes); const port = process.env.PORT || 3000; app.listen(port , () => console.log('App listening on port ' + port));
In views/pages/auth.ejs file with following contents
This is a home page for this application and it contains login button to get user data from google.
<!doctype html> <html> <head> <title>Google SignIn</title> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"> <style> body { display: -ms-flexbox; display: -webkit-box; display: flex; -ms-flex-align: center; -ms-flex-pack: center; -webkit-box-align: center; align-items: center; -webkit-box-pack: center; justify-content: center; padding-top: 40px; padding-bottom: 40px; background-color: #f5f5f5; } .form-signin { width: 100%; max-width: 500px; padding: 15px; margin: 0 auto; border: 1px solid #ccc; } </style> </head> <body class="text-center"> <form class="form-signin"> <h2 class="h3 mb-3 text-primary">Build App with Node.js and OAuth2</h2> <h2 class="h3 mb-3 text-primary">Login or Register</h2> <a href="/auth/google" class="btn btn-danger"><span class="fa fa-google"></span> SignIn with Google</a> </form> </body> </html>
In views/pages/success.ejs file with following contents
If you’re logged successful and you will redirect to this page. In this page has user information and logout option.
<!doctype html> <html> <head> <title>Google SignIn</title> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"> <!-- load bootstrap css --> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"> <!-- load fontawesome --> <style> body { padding-top:70px; } </style> </head> <body> <div class="container"> <div class="row"> <div class="col-12"> <h2 class="text-primary text-center"><span class="fa fa-user"></span> Profile</h2> <p> <strong>Name:</strong>: <%= result.name %></p> <a href="/auth/logout">Logout</a> </div> </div> </div> </body> </html>
In oauth/google.oauth.js file with following contents
Passport strategies for authenticating with Google using ONLY OAuth 2.0.
passport-google-oauth dependency used for authenticate using Google in your Node.js applications.
const passport = require('passport'); const generator = require('generate-password'); const GoogleStrategy = require('passport-google-oauth').OAuth2Strategy; const User = require('../models/user.model'); const AccessInfo = { clientID: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET, callbackURL: process.env.CALLBACK_API_URL }; const verifyCallback = async (accessToken, refreshToken, profile, done) => { User.findOne({ sid: profile.id }, function(err, user) { if (err) { return done(err); } if (!user) { user = new User({ name: profile.displayName, email: profile.emails[0].value, password: generator.generate({ length: 10, numbers: true }), provider: 'google', sid: profile.id }); user.save(function(err) { if (err) console.log(err); return done(err, user); }); } else { //found user. Return return done(err, user); } }); }; passport.use(new GoogleStrategy(AccessInfo, verifyCallback)); passport.serializeUser((user, done) => { done(null, user.id); }); passport.deserializeUser((id, done) => { User.findById(id).then(user => { done(null, user); }); });
In routes/user.route.js file with following contents
Use
passport.authenticate()
, specifying the'google'
strategy, to authenticate requests.
const router = require('express').Router(); const passport = require('passport'); const googleoAuth = require('../oauth/google.oauth'); const User = require('../models/user.model'); function isUserAuthenticated(req, res, next) { if (req.user) { next(); } else { res.redirect('/'); } } router.get('/', function(req, res) { res.render('pages/auth'); }); router.get('/auth/google', passport.authenticate('google', { scope : ['profile', 'email'] })); router.get('/oauth2callback', passport.authenticate('google', { failureRedirect: '/error' }), function(req, res) { // Successful authentication, redirect success. res.redirect('/user/account'); } ); router.get('/user/account', isUserAuthenticated, (req, res) => { if (req.user.id) { User.findById(req.user.id, function (err, user) { if (err) { console.log("ERROR" + err) } res.render('pages/success', { result: user }); }); } else { return res.status(201).json({message: "Invalid access"}); } }); router.get('/error', (req, res) => res.send("error logging in")); router.get("/auth/google/redirect",passport.authenticate("google", { scope: ['profile', 'email'] }),(req,res)=>{ res.send(req.user); res.send("you reached the redirect URI"); }); router.get("/auth/logout", (req, res) => { req.logout(); res.redirect('/'); }); module.exports = router;
Click here to read more about articles in node.js