"Sign in with Google" — Without Storing Passwords
OAuth & Social Login
OAuth lets a third party vouch for the user's identity. Less code, fewer passwords, fewer breach risks.
What you'll learn
- Understand the OAuth dance
- Add Google login via Passport
- Securely handle the callback
OAuth 2.0 lets users sign in with their existing Google / GitHub / Apple account. Your app never sees their password — the provider vouches for them.
The Flow
1. User clicks "Sign in with Google"
2. Your app redirects to Google with client_id + scope
3. User logs into Google, approves the requested scope
4. Google redirects back to your callback URL with a code
5. Your server exchanges the code for an access token
6. Your server uses the token to fetch the user's info
7. Your server creates or finds a user, issues a session/JWT
Passport handles 2–6 for you.
Register With the Provider
For Google:
- Go to https://console.cloud.google.com
- Create a project
- Configure OAuth consent screen
- Create OAuth client ID, type = Web
- Add redirect URI:
https://yourapp.com/auth/google/callback - Copy the client ID and secret to
.env
Wire Up
npm install passport passport-google-oauth20 import passport from "passport";
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: "/auth/google/callback",
}, async (accessToken, refreshToken, profile, done) => {
try {
// 1. Look up by google id
let user = await db.users.findByGoogleId(profile.id);
// 2. Or create a new user
if (!user) {
const email = profile.emails[0].value;
user = await db.users.create({
googleId: profile.id,
email,
name: profile.displayName,
});
}
done(null, user);
} catch (err) {
done(err);
}
}));
app.get("/auth/google",
passport.authenticate("google", { scope: ["profile", "email"] })
);
app.get("/auth/google/callback",
passport.authenticate("google", { failureRedirect: "/login" }),
(req, res) => {
res.redirect("/dashboard");
}
); Combining With Local
You can mix: Google sign-in or email/password. Just register both strategies.
For accounts that match by email, decide a policy:
- Auto-link — if a Google sign-in’s email matches an existing user, link them
- Manual — show a “merge accounts” prompt
Auto-linking is friendlier; manual is safer (prevents account takeover via email spoofing).
State / PKCE
OAuth has a state parameter to prevent CSRF on the callback. Modern
strategies handle it automatically. Same for PKCE — recommended for
all OAuth flows now.
Scope Carefully
Only request what you need. email, profile is the minimum. Don’t
ask for Drive access if you don’t need it — users distrust apps
that overreach.
Hosted Alternative
If you have a choice and time matters: let Clerk / Auth0 do it. The integration is 50 lines, you get every provider for free, plus MFA, password resets, account linking. Implementing all of that yourself is a long road.
Helmet →