Episode 9: Using OAuth for Social Login (Google, GitHub)
Enable users to log in using Google and GitHub accounts via OAuth 2.0, using Passport.js and passport-google-oauth20 / passport-github2.
Episode 9: Using OAuth for Social Login (Google, GitHub)
Dependences:
- Express.js
- Passport.js
- passport-google-oauth20
- passport-github2
- JWT in cookies
- dotenv
1. Install OAuth Packages
1
npm install passport passport-google-oauth20 passport-github2
2. Set Environment Variables
In .env:
1
2
3
4
5
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret
You can get these from:
- Google Cloud Console
- GitHub Developer Settings
Make sure to set the callback URLs correctly:
- http://localhost:3000/auth/google/callback
- http://localhost:3000/auth/github/callback
3. Create a Basic Passport Setup
Create config/passport.js:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const GitHubStrategy = require('passport-github2').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) => {
let user = await User.findOne({ oauthId: profile.id, provider: 'google' });
if (!user) {
user = await User.create({
oauthId: profile.id,
provider: 'google',
email: profile.emails[0].value,
name: profile.displayName
});
}
done(null, user);
}));
passport.use(new GitHubStrategy({
clientID: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
callbackURL: '/auth/github/callback'
}, async (accessToken, refreshToken, profile, done) => {
let user = await User.findOne({ oauthId: profile.id, provider: 'github' });
if (!user) {
user = await User.create({
oauthId: profile.id,
provider: 'github',
email: profile.emails?.[0]?.value || '',
name: profile.displayName || profile.username
});
}
done(null, user);
}));
4. Register Passport in app.js
1
2
3
4
const passport = require('passport');
require('./config/passport');
app.use(passport.initialize());
5. Update User Model to Support OAuth
In models/User.js, extend the schema:
1
2
3
oauthId: { type: String },
provider: { type: String },
name: String
6. Create OAuth Routes
In routes/oauth.js:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const express = require('express');
const router = express.Router();
const jwt = require('jsonwebtoken');
const passport = require('passport');
// Google
router.get('/google', passport.authenticate('google', { scope: ['profile', 'email'] }));
router.get('/google/callback', passport.authenticate('google', { session: false }), (req, res) => {
const token = jwt.sign({ id: req.user._id }, process.env.JWT_SECRET);
res.cookie('token', token, { httpOnly: true });
res.redirect('/');
});
// GitHub
router.get('/github', passport.authenticate('github', { scope: ['user:email'] }));
router.get('/github/callback', passport.authenticate('github', { session: false }), (req, res) => {
const token = jwt.sign({ id: req.user._id }, process.env.JWT_SECRET);
res.cookie('token', token, { httpOnly: true });
res.redirect('/');
});
module.exports = router;
In app.js:
1
2
const oauthRoutes = require('./routes/oauth');
app.use('/auth', oauthRoutes);
7. Add Buttons in Login View
In views/login.hbs:
Notes
- No passwords are stored for OAuth users.
- If you later want to distinguish OAuth users, use user.provider field.
- Combine OAuth with regular login for best UX flexibility.
This post is licensed under CC BY 4.0 by the author.