100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Node.js & Express Backend
35 minintermediate

OAuth 2.0 and Social Login (Passport.js)

OAuth 2.0 and Social Login (Passport.js)

OAuth 2.0 is an authorisation framework that allows a third-party application to access a user's data on an external service (Google, GitHub, Facebook) without ever receiving the user's password. The user authenticates directly with the external provider, which then issues an access token to your application. Passport.js is the dominant Node.js middleware library for implementing OAuth 2.0 strategies, with over 500 community-maintained strategy packages.

Analogy🏏Cricket
Think of it like cricket: Imagine a cricket academy that wants to let players log in with their BCCI credentials instead of creating a new account. The student visits the BCCI portal, logs in with their official credentials, and the BCCI issues an authorisation voucher to the academy. The academy never sees the student's BCCI password — only the voucher. The voucher says 'this player is verified; their name is Shubman Gill.' The academy creates a local profile using that information. OAuth 2.0 is exactly this delegated authorisation voucher system.

The OAuth 2.0 Authorization Code Flow

The most secure OAuth flow for web apps is the Authorization Code flow. Step 1: your app redirects the user to the provider's login page with your client_id and a redirect_uri. Step 2: the user consents and the provider redirects back to your redirect_uri with a short-lived code. Step 3: your server exchanges the code for an access token in a server-to-server request (never exposed to the browser). Step 4: you use the access token to fetch the user's profile.

bash
npm install passport passport-google-oauth20 passport-github2 express-session
javascript
// config/passport.js
const passport       = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const User           = require('../models/User');

passport.use(new GoogleStrategy({
  clientID:     process.env.GOOGLE_CLIENT_ID,
  clientSecret: process.env.GOOGLE_CLIENT_SECRET,
  callbackURL:  '/auth/google/callback'
}, async (accessToken, refreshToken, profile, done) => {
  try {
    // Find or create user based on Google profile ID
    let user = await User.findOne({ googleId: profile.id });
    if (!user) {
      user = await User.create({
        googleId:    profile.id,
        email:       profile.emails[0].value,
        displayName: profile.displayName,
        avatar:      profile.photos[0]?.value
      });
    }
    return done(null, user);
  } catch (err) {
    return done(err);
  }
}));

passport.serializeUser((user, done) => done(null, user.id));
passport.deserializeUser(async (id, done) => {
  const user = await User.findById(id);
  done(null, user);
});
Analogy🏏Cricket
Think of it like cricket: The Authorization Code flow is like a player's transfer process. The buying club (your app) sends a formal request to the player's current board (Google). The board issues a transfer approval code to the player. The player hands it to the buying club's office. The club's GM (server) then contacts the board directly to exchange it for the official transfer documents (access token). The player's board details never pass through the player's hands during the exchange — same server-to-server security guarantee.

Wiring Routes and Session Serialisation

Passport integrates with Express sessions: after successful OAuth, it serialises the user to the session so they remain logged in across requests. Alternatively, you can skip sessions entirely, issue your own JWT on callback success, and use stateless authentication for API routes.

javascript
// app.js
const session  = require('express-session');
const passport = require('passport');
require('./config/passport');

app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());

// routes/auth.js
// 1. Redirect to Google
router.get('/google', passport.authenticate('google', { scope: ['profile', 'email'] }));

// 2. Google redirects back here with the code
router.get('/google/callback',
  passport.authenticate('google', { failureRedirect: '/login?error=oauth' }),
  (req, res) => {
    // Optionally issue JWT here instead of relying on session
    const token = signAccess({ sub: req.user.id, role: req.user.role });
    res.redirect('/dashboard?token=' + token);
  }
);

// Protect a route with Passport session
function requireAuth(req, res, next) {
  if (req.isAuthenticated()) return next();
  res.status(401).json({ error: 'Unauthenticated' });
}

router.get('/profile', requireAuth, (req, res) => {
  res.json(req.user);
});
Analogy🏏Cricket
Think of it like cricket: Passport's session serialisation is like the team hotel key card system. After the BCCI confirms a player's identity at check-in (OAuth callback), the hotel stores the player's ID in their records and hands him a key card (session cookie). Every time he taps the card (subsequent requests), the hotel looks up his ID and grants access — without asking for his credentials again. deserializeUser is the key card reader.
Lesson 21 of 36
0% complete