Add Google and GitHub OAuth Social Login
Integrate real Google and GitHub OAuth flows into the existing Node/Express app with persistent user records and session handling.
The route
8 steps to Done
- 01
Install OAuth dependencies
Add the required npm packages for Passport and the Google/GitHub strategies.
Preview prompt + verify gate ▾ Hide ▴
1. Open a terminal in the project root. 2. Execute `npm install passport passport-google-oauth20 passport-github2`. 3. Verify that the installation succeeded without errors. 4. Open package.json and confirm that the three new dependencies appear under "dependencies" with semantic version ranges. 5. Commit the updated package.json and package-lock.json (or yarn.lock) to version control.
- ✓ Does package.json contain a "passport" entry?
- ✓ Does package.json contain a "passport-google-oauth20" entry?
- ✓ Does package.json contain a "passport-github2" entry?
- 02
Create .env entries and load them
Store Google and GitHub client credentials securely and make them available to the app.
Preview prompt + verify gate ▾ Hide ▴
1. In the project root, create (or edit) a file named `.env`. 2. Add the following lines with placeholder values (they will be replaced later with real keys): 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 3. Open the main server entry file (e.g., app.js or server.js) and ensure the line `require('dotenv').config();` appears at the very top before any other imports. 4. Save the files and restart the server to confirm no startup errors. 5. Run `node -e "console.log(process.env.GOOGLE_CLIENT_ID)"` to verify the variable is loaded.
- ✓ Does .env contain GOOGLE_CLIENT_ID?
- ✓ Does .env contain GITHUB_CLIENT_SECRET?
- ✓ Is `require('dotenv').config();` the first line in the server entry file?
- 03
Configure Passport and session serialization
Initialize Passport in the Express app and set up user serialize/deserialize logic for session persistence.
Preview prompt + verify gate ▾ Hide ▴
1. Open the main Express configuration file (e.g., app.js). 2. At the top, after loading dotenv, add `const passport = require('passport');`. 3. Immediately after the existing `app.use(session(...))` line, insert: `app.use(passport.initialize());` `app.use(passport.session());` 4. Below the middleware section, implement: `passport.serializeUser((user, done) => { done(null, user.id); });` `passport.deserializeUser(async (id, done) => { try { const user = await User.findById(id); done(null, user); } catch (err) { done(err); } });` 5. Ensure the User model is required in the file. 6. Save and restart the server; there should be no runtime errors.
- ✓ Is `app.use(passport.initialize());` present?
- ✓ Is `app.use(passport.session());` present?
- ✓ Do serializeUser and deserializeUser functions exist and reference User?
- 04
Implement Google OAuth strategy
Create a Passport strategy that authenticates users via Google and creates/fetches a user record.
Preview prompt + verify gate ▾ Hide ▴
1. In a new file `config/passport-google.js` (or inside a central passport config), add: `const GoogleStrategy = require('passport-google-oauth20').Strategy;` 2. Call `passport.use(new GoogleStrategy({ clientID: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET, callbackURL: '/auth/google/callback', scope: ['profile', 'email'] }, async (accessToken, refreshToken, profile, done) => { try { const existing = await User.findOne({ provider: 'google', providerId: profile.id }); if (existing) return done(null, existing); const newUser = await User.create({ provider: 'google', providerId: profile.id, displayName: profile.displayName, email: profile.emails[0].value }); return done(null, newUser); } catch (err) { return done(err, null); } }));` 3. Ensure this file is required somewhere near the top of the app so the strategy registers. 4. No placeholder strings – use the actual env variables. 5. Save and restart server; any syntax errors should be fixed.
- ✓ Is GoogleStrategy imported from 'passport-google-oauth20'?
- ✓ Does the strategy use process.env.GOOGLE_CLIENT_ID and SECRET?
- ✓ Does the verify callback query User by provider='google' and providerId?
- 05
Implement GitHub OAuth strategy
Add a Passport strategy for GitHub authentication mirroring the Google flow.
Preview prompt + verify gate ▾ Hide ▴
1. In `config/passport-github.js`, add: `const GitHubStrategy = require('passport-github2').Strategy;` 2. Register the strategy: `passport.use(new GitHubStrategy({ clientID: process.env.GITHUB_CLIENT_ID, clientSecret: process.env.GITHUB_CLIENT_SECRET, callbackURL: '/auth/github/callback', scope: ['user:email'] }, async (accessToken, refreshToken, profile, done) => { try { const existing = await User.findOne({ provider: 'github', providerId: profile.id }); if (existing) return done(null, existing); const email = profile.emails && profile.emails[0] ? profile.emails[0].value : null; const newUser = await User.create({ provider: 'github', providerId: profile.id, displayName: profile.displayName || profile.username, email }); return done(null, newUser); } catch (err) { return done(err, null); } }));` 3. Require this config file from the main server so it runs. 4. Ensure no placeholder strings – use the actual env variables. 5. Restart and verify no startup errors.
- ✓ Is GitHubStrategy imported from 'passport-github2'?
- ✓ Does the strategy read process.env.GITHUB_CLIENT_ID and SECRET?
- ✓ Does the verify function store provider='github' and providerId?
- 06
Add authentication routes
Create Express routes to start OAuth flows, handle callbacks, and log out users.
Preview prompt + verify gate ▾ Hide ▴
1. In `routes/auth.js` (create the file if it doesn't exist), add: `const router = require('express').Router(); const passport = require('passport');` 2. Define routes: `router.get('/google', passport.authenticate('google', { scope: ['profile', 'email'] }));` `router.get('/google/callback', passport.authenticate('google', { failureRedirect: '/login' }), (req, res) => { res.redirect('/'); });` `router.get('/github', passport.authenticate('github', { scope: ['user:email'] }));` `router.get('/github/callback', passport.authenticate('github', { failureRedirect: '/login' }), (req, res) => { res.redirect('/'); });` `router.get('/logout', (req, res) => { req.logout(function(err){ if(err){return next(err);} res.redirect('/'); }); });` 3. Export the router and mount it in the main app with `app.use('/auth', require('./routes/auth'));`. 4. Ensure no placeholder URLs; use '/' or '/login' as appropriate. 5. Restart the server and verify that hitting `/auth/google` redirects to Google consent screen (you can check HTTP 302).
- ✓ Does /auth/google route exist and use passport.authenticate('google')?
- ✓ Does /auth/github/callback redirect on success?
- ✓ Is there a /auth/logout route that calls req.logout and redirects?
- 07
Extend User model for social providers
Add fields to store OAuth provider information and ensure existing email login still works.
Preview prompt + verify gate ▾ Hide ▴
1. Open `models/User.js`. 2. Locate the Mongoose schema definition (e.g., `const UserSchema = new mongoose.Schema({ ... })`). 3. Add two new fields: `provider: { type: String, enum: ['local', 'google', 'github'], default: 'local' },` `providerId: { type: String }` 4. Adjust the email field to be required only when provider is 'local' (you can add a `required: function() { return this.provider === 'local'; }`). 5. Save the file and run any existing migrations or simply restart; Mongoose will add new fields to new documents. 6. Verify that creating a user via the Google or GitHub strategy stores these new fields. 7. Do NOT leave any placeholder schema definitions; the schema must be valid JavaScript.
- ✓ Does the User schema contain a provider field with enum ['local','google','github']?
- ✓ Does the schema contain a providerId field?
- ✓ After a social login, does the stored user document have non‑null provider and providerId?
- 08
End‑to‑end verification of social login
Confirm that both Google and GitHub OAuth flows work, users are persisted, and logout clears the session.
Preview prompt + verify gate ▾ Hide ▴
1. Start the server (`npm start` or `node app.js`). 2. In a browser, navigate to `http://localhost:3000/auth/google`. Complete the Google consent screen and allow the app. 3. After redirect, verify you are logged in (e.g., visit a protected route `/profile` that shows `req.user`). 4. Open your database (MongoDB) and locate the newly created user document; ensure `provider: 'google'` and `providerId` are present. 5. In the same browser, go to `http://localhost:3000/auth/logout` and confirm you are logged out (protected route redirects to `/login`). 6. Repeat steps 2‑5 for GitHub using `http://localhost:3000/auth/github`. 7. Document the results: note the user IDs, provider fields, and that logout clears the session. 8. Ensure no placeholder text remains in any route file. 9. Finally, write a short summary in a file `SOCIAL_LOGIN_TEST.md` confirming PASS for both providers.
- ✓ Can you log in with Google and see a user document with provider 'google'?
- ✓ Can you log in with GitHub and see a user document with provider 'github'?
- ✓ Does /auth/logout clear the session and redirect appropriately?