OAuth 2.0 & Social Login
OAuth 2.0 is an authorization framework that lets your app act on behalf of a user at an external service — or, in the social-login case, lets a provider (Google, GitHub, etc.) vouch for a user's identity so your app doesn't need to manage credentials at all. The user authenticates with Google, Google hands your app a code, your server exchanges it for tokens, you get the user's profile, and you find or create a local account. This page covers the authorization-code flow, the security parameters that keep it safe, and implementation with Passport.js.
The authorization-code flow
1. Your app redirects the user to the provider's authorization URL:
https://accounts.google.com/o/oauth2/auth?
client_id=...&redirect_uri=...&scope=email+profile&state=<random>
2. User logs into Google and approves access.
3. Provider redirects back to YOUR callback URL with a code:
https://yourapp.com/auth/google/callback?code=<one-time-code>&state=<same-random>
4. Your server exchanges the code for tokens (back-channel, never exposed to the browser):
POST https://oauth2.googleapis.com/token { code, client_id, client_secret, ... }
→ { access_token, id_token, refresh_token }
5. Use the access_token to call the provider's API for the user's profile.
6. Find or create the user in your own database; issue YOUR session or JWT.The `state` parameter prevents CSRF
Passport Google strategy
npm install passport-google-oauth20
import { Strategy as GoogleStrategy } from 'passport-google-oauth20'
passport.use(new GoogleStrategy(
{
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: 'https://yourapp.com/auth/google/callback',
scope: ['email', 'profile'],
},
async (accessToken, refreshToken, profile, done) => {
try {
// Find or create a local user record linked to this provider identity:
let user = await db.users.findByProvider('google', profile.id)
if (!user) {
user = await db.users.create({
email: profile.emails[0].value,
name: profile.displayName,
provider: 'google',
providerId: profile.id,
})
}
return done(null, user)
} catch (err) {
return done(err)
}
},
))Routes
// Redirect user to Google:
app.get('/auth/google', passport.authenticate('google'))
// Google redirects back here after the user approves:
app.get('/auth/google/callback',
passport.authenticate('google', { failureRedirect: '/login' }),
(req, res) => res.redirect('/dashboard'), // success
)Find-or-create: linking provider identity to your account
Registering your app with providers
Provider | Console URL | Env vars |
|---|---|---|
console.cloud.google.com → OAuth 2.0 |
| |
GitHub | github.com/settings/developers |
|
developers.facebook.com |
|
OAuth scopes — request only what you need
Request minimal scopes —
emailandprofilefor login; don't ask forwriteaccess if you only read.Inform users what access they are granting — users reject over-reaching apps.
Re-request additional scopes incrementally when a feature actually needs them.
The access token from OAuth is for calling that provider's API, not your own; issue your own session/JWT after login.