
Google OAuth Integration in Node.js with Code Examples (2025 Guide)
Authentication is the backbone of modern web applications, and Google OAuth has become one of the most trusted and user-friendly authentication methods available. If you've ever clicked "Sign in with Google" on a website, you've experienced OAuth in action. In this comprehensive guide, we'll walk through implementing Google OAuth 2.0 in your Node.js applications from scratch.
What is Google OAuth and Why Should You Use It?
OAuth 2.0 is an authorization framework that allows third-party applications to access user data without exposing passwords. When you integrate Google OAuth into your Node.js application, you're leveraging Google's robust authentication infrastructure, which means:
- Enhanced Security: No need to store user passwords
- Better User Experience: Users can sign in with accounts they already have
- Reduced Development Time: Let Google handle the complex authentication logic
- Trust Factor: Users trust Google's authentication system
According to recent studies, applications with social login options see up to 20% higher conversion rates compared to traditional email/password registration forms.
Prerequisites
Before we dive into the code, make sure you have:
- Node.js (v16 or higher) installed on your machine
- Basic understanding of Express.js
- A Google account for accessing Google Cloud Console
- npm or yarn package manager
Setting Up Your Google OAuth Credentials
The first step is creating OAuth credentials in the Google Cloud Console. This is where your application gets its identity.
Step 1: Create a Google Cloud Project
- Navigate to the Google Cloud Console
- Click on "Select a Project" and then "New Project"
- Give your project a meaningful name like "my-nodejs-oauth-app"
- Click "Create"
Step 2: Enable Google+ API
- In your project dashboard, go to "APIs & Services" > "Library"
- Search for "Google+ API" or "Google People API"
- Click on it and press "Enable"
Step 3: Create OAuth 2.0 Credentials
- Go to "APIs & Services" > "Credentials"
- Click "Create Credentials" and select "OAuth client ID"
- Configure the OAuth consent screen if prompted
- Choose "Web application" as the application type
- Add authorized redirect URIs (e.g.,
http://localhost:3000/auth/google/callback) - Click "Create"
You'll receive a Client ID and Client Secret. Keep these safe—we'll need them in our application.
Building the Node.js Application
Let's build a complete authentication system step by step.
Step 1: Initialize Your Project
mkdir google-oauth-nodejs
cd google-oauth-nodejs
npm init -yStep 2: Install Required Dependencies
npm install express express-session passport passport-google-oauth20 dotenvHere's what each package does:
- express: Web framework for Node.js
- express-session: Session middleware for maintaining user sessions
- passport: Authentication middleware for Node.js
- passport-google-oauth20: Passport strategy for Google OAuth 2.0
- dotenv: Loads environment variables from .env file
Step 3: Create Environment Variables
Create a .env file in your project root:
GOOGLE_CLIENT_ID=your_client_id_here
GOOGLE_CLIENT_SECRET=your_client_secret_here
SESSION_SECRET=your_random_session_secret
CALLBACK_URL=http://localhost:3000/auth/google/callback
PORT=3000Important: Never commit your .env file to version control. Add it to your .gitignore file.
Step 4: Set Up Passport Configuration
Create a file called config/passport.js:
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
passport.use(
new GoogleStrategy(
{
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: process.env.CALLBACK_URL,
},
async (accessToken, refreshToken, profile, done) => {
try {
// Here you would typically:
// 1. Check if user exists in your database
// 2. If not, create a new user
// 3. Return the user object
const user = {
googleId: profile.id,
email: profile.emails[0].value,
name: profile.displayName,
picture: profile.photos[0].value,
};
// For demo purposes, we're just returning the profile
// In production, you'd save this to a database
return done(null, user);
} catch (error) {
return done(error, null);
}
}
)
);
// Serialize user for the session
passport.serializeUser((user, done) => {
done(null, user);
});
// Deserialize user from the session
passport.deserializeUser((user, done) => {
done(null, user);
});
module.exports = passport;Step 5: Create the Main Server File
Create server.js:
require('dotenv').config();
const express = require('express');
const session = require('express-session');
const passport = require('./config/passport');
const app = express();
// Session configuration
app.use(
session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
secure: process.env.NODE_ENV === 'production', // Use secure cookies in production
maxAge: 24 * 60 * 60 * 1000, // 24 hours
},
})
);
// Initialize Passport
app.use(passport.initialize());
app.use(passport.session());
// Middleware to check if user is authenticated
const isAuthenticated = (req, res, next) => {
if (req.isAuthenticated()) {
return next();
}
res.redirect('/');
};
// Routes
app.get('/', (req, res) => {
res.send(`
<html>
<head>
<title>Google OAuth Demo</title>
<style>
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.container {
text-align: center;
background: white;
padding: 40px;
border-radius: 10px;
box-shadow: 0 10px 25px rgba(0,0,0,0.2);
}
a {
display: inline-block;
margin-top: 20px;
padding: 12px 30px;
background: #4285f4;
color: white;
text-decoration: none;
border-radius: 5px;
font-weight: bold;
}
a:hover {
background: #357ae8;
}
</style>
</head>
<body>
<div class="container">
<h1>Welcome to Google OAuth Demo</h1>
<p>Click below to sign in with your Google account</p>
<a href="/auth/google">Sign in with Google</a>
</div>
</body>
</html>
`);
});
// Google OAuth routes
app.get(
'/auth/google',
passport.authenticate('google', {
scope: ['profile', 'email'],
})
);
app.get(
'/auth/google/callback',
passport.authenticate('google', { failureRedirect: '/' }),
(req, res) => {
res.redirect('/profile');
}
);
// Protected route
app.get('/profile', isAuthenticated, (req, res) => {
res.send(`
<html>
<head>
<title>Profile</title>
<style>
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.profile-card {
background: white;
padding: 40px;
border-radius: 10px;
box-shadow: 0 10px 25px rgba(0,0,0,0.2);
text-align: center;
max-width: 400px;
}
img {
border-radius: 50%;
width: 100px;
height: 100px;
margin-bottom: 20px;
}
.logout-btn {
display: inline-block;
margin-top: 20px;
padding: 10px 25px;
background: #ea4335;
color: white;
text-decoration: none;
border-radius: 5px;
}
.logout-btn:hover {
background: #c5362d;
}
</style>
</head>
<body>
<div class="profile-card">
<img src="${req.user.picture}" alt="Profile Picture">
<h2>${req.user.name}</h2>
<p>${req.user.email}</p>
<p><strong>Google ID:</strong> ${req.user.googleId}</p>
<a href="/logout" class="logout-btn">Logout</a>
</div>
</body>
</html>
`);
});
// Logout route
app.get('/logout', (req, res) => {
req.logout((err) => {
if (err) {
return next(err);
}
res.redirect('/');
});
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});Testing Your OAuth Integration
- Start your server:
node server.js- Open your browser and navigate to
http://localhost:3000 - Click "Sign in with Google"
- You'll be redirected to Google's login page
- After successful authentication, you'll be redirected back to your profile page
Adding Database Integration
In a real-world application, you'll want to store user information in a database. Here's an example using MongoDB with Mongoose:
npm install mongooseUpdate your config/passport.js to include database operations:
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const mongoose = require('mongoose');
// User model
const UserSchema = new mongoose.Schema({
googleId: {
type: String,
required: true,
unique: true,
},
email: {
type: String,
required: true,
},
name: String,
picture: String,
createdAt: {
type: Date,
default: Date.now,
},
});
const User = mongoose.model('User', UserSchema);
passport.use(
new GoogleStrategy(
{
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: process.env.CALLBACK_URL,
},
async (accessToken, refreshToken, profile, done) => {
try {
// Check if user already exists
let user = await User.findOne({ googleId: profile.id });
if (!user) {
// Create new user
user = await User.create({
googleId: profile.id,
email: profile.emails[0].value,
name: profile.displayName,
picture: profile.photos[0].value,
});
}
return done(null, user);
} catch (error) {
return done(error, null);
}
}
)
);
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser(async (id, done) => {
try {
const user = await User.findById(id);
done(null, user);
} catch (error) {
done(error, null);
}
});
module.exports = passport;Add MongoDB connection to your server.js:
const mongoose = require('mongoose');
// Connect to MongoDB
mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/oauth-demo', {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => console.log('MongoDB connected'))
.catch(err => console.error('MongoDB connection error:', err));Best Practices and Security Considerations
1. Environment Variables
Always store sensitive information in environment variables, never in your codebase. Use different credentials for development and production environments.
2. HTTPS in Production
Always use HTTPS in production. OAuth requires secure connections to protect user data during the authentication flow.
3. Token Storage
Never store access tokens in localStorage or sessionStorage on the client side. Keep them server-side in secure, HTTP-only cookies or session storage.
4. Scope Minimization
Only request the permissions (scopes) your application actually needs. For basic authentication, profile and email are usually sufficient.
5. Session Security
Configure session cookies properly:
session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
secure: true, // Requires HTTPS
httpOnly: true, // Prevents XSS attacks
maxAge: 24 * 60 * 60 * 1000, // 24 hours
sameSite: 'lax', // CSRF protection
},
})6. Error Handling
Implement comprehensive error handling:
app.get(
'/auth/google/callback',
passport.authenticate('google', {
failureRedirect: '/login',
failureMessage: true
}),
(req, res) => {
res.redirect('/profile');
}
);
// Error handling middleware
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something went wrong!');
});Common Issues and Troubleshooting
Issue 1: Redirect URI Mismatch
Error: redirect_uri_mismatch
Solution: Ensure the callback URL in your Google Cloud Console exactly matches the one in your application code, including the protocol (http/https) and port.
Issue 2: Invalid Client Error
Error: invalid_client
Solution: Double-check your Client ID and Client Secret in your .env file. Make sure there are no extra spaces or quotes.
Issue 3: Session Not Persisting
Problem: User gets logged out after page refresh
Solution: Verify that:
- You're using
express-sessioncorrectly - Session secret is set
- You've called
passport.session()after initializing sessions
Issue 4: Cookies Not Working
Problem: Session cookies aren't being set
Solution: Check your cookie configuration. If testing locally without HTTPS, set secure: false in development.
Advanced Features
Refresh Token Implementation
To keep users logged in longer, implement refresh token logic:
passport.use(
new GoogleStrategy(
{
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: process.env.CALLBACK_URL,
accessType: 'offline', // Request refresh token
prompt: 'consent', // Force consent screen
},
async (accessToken, refreshToken, profile, done) => {
try {
let user = await User.findOne({ googleId: profile.id });
if (!user) {
user = await User.create({
googleId: profile.id,
email: profile.emails[0].value,
name: profile.displayName,
picture: profile.photos[0].value,
refreshToken: refreshToken, // Store refresh token
});
} else if (refreshToken) {
// Update refresh token if new one is provided
user.refreshToken = refreshToken;
await user.save();
}
return done(null, user);
} catch (error) {
return done(error, null);
}
}
)
);Multiple OAuth Providers
You can easily add other providers like Facebook or GitHub:
npm install passport-facebook passport-github2Configure each strategy similar to Google OAuth and create separate routes for each provider.
Deployment Considerations
Environment Configuration
When deploying to production (Heroku, AWS, Vercel, etc.), update your environment variables:
- Set production OAuth credentials
- Update callback URLs in Google Cloud Console
- Enable HTTPS
- Set
NODE_ENV=production
Scaling Considerations
For applications expecting high traffic:
- Use Redis for session storage instead of memory-based sessions
- Implement rate limiting to prevent abuse
- Consider using JWT tokens for stateless authentication
Performance Optimization
Session Store with Redis
npm install connect-redis redisconst redis = require('redis');
const RedisStore = require('connect-redis')(session);
const redisClient = redis.createClient();
app.use(
session({
store: new RedisStore({ client: redisClient }),
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
})
);Testing Your OAuth Implementation
Create a test file test/auth.test.js:
const request = require('supertest');
const app = require('../server');
describe('OAuth Routes', () => {
test('GET / returns homepage', async () => {
const response = await request(app).get('/');
expect(response.statusCode).toBe(200);
});
test('GET /profile redirects unauthenticated users', async () => {
const response = await request(app).get('/profile');
expect(response.statusCode).toBe(302);
});
});Conclusion
Implementing Google OAuth in Node.js doesn't have to be complicated. By following this guide, you've learned how to:
- Set up Google OAuth credentials
- Build a complete authentication system with Passport.js
- Implement database integration for user management
- Follow security best practices
- Handle common issues and errors
- Deploy your application to production
OAuth provides a secure, user-friendly authentication method that can significantly improve your application's user experience. The patterns we've covered here can be extended to support multiple OAuth providers, giving your users even more login options.
For more Node.js tutorials and web development guides, check out ItsEzCode where we break down complex concepts into easy-to-follow tutorials.
Additional Resources
- Google OAuth 2.0 Documentation
- Passport.js Official Documentation
- Express.js Guide
- OAuth 2.0 Simplified
- Node.js Security Best Practices
Need Help?
If you run into issues implementing Google OAuth in your Node.js application, feel free to check out more tutorials at ItsEzCode. We're here to help you build better applications!
Did you find this guide helpful? Share it with fellow developers who might benefit from learning about Google OAuth integration in Node.js. Happy coding!

Malik Saqib
I craft short, practical AI & web dev articles. Follow me on LinkedIn.