Skip to content
Neerav Gupta

Google Authentication in Express.js with TypeScript, Mongoose & JWT

Learn how to implement Google Authentication in an Express.js application using TypeScript, Passport.js, MongoDB with Mongoose, and JWT authentication

Backend , Authentication 1 min read

In this blog, we will implement Google Authentication in an Express.js app using:

  • TypeScript
  • Express.js
  • Mongoose
  • Passport.js
  • JWT Authentication

Terminal window
└── src/
├── config/
├── controllers/
├── middlewares/
├── models/
├── routes/
├── utils/
├── app.ts
└── index.ts
├── .env
├── package.json
└── tsconfig.json

Terminal window
npm install passport passport-google-oauth20
npm install jsonwebtoken cookie-parser bcrypt mongoose
npm install -D @types/passport-google-oauth20

Go to Google Cloud Console

create a new project

Configure OAuth Consent Screen

go to clients create oauth2.0 client

create oauth2.0 client

copy credentials


Create a .env file:

GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GOOGLE_CALLBACK_URL=http://localhost:3000/api/v1/auth/google/callback

Your user model MUST contain these fields:

refreshToken: {
type: String,
},
googleId: {
type: String,
},

If you already support password login, make password optional:

password: {
type: String,
required: false,
},

Also add checks wherever password login is used:

if (!user.password) {
throw new Error("Password login not available for this account");
}

Create: src/config/passport.ts

import passport from "passport";
import { Strategy as GoogleStrategy } from "passport-google-oauth20";
import { User } from "../models/User.model.js";
import { generateUsername } from "../utils/usernameGen.js";
passport.use(
new GoogleStrategy(
{
clientID: process.env.GOOGLE_CLIENT_ID!!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!!,
callbackURL: process.env.GOOGLE_CALLBACK_URL,
},
async (_accessToken, _refreshToken, profile, done) => {
try {
const email = profile.emails?.[0]?.value;
if (!email) {
return done(new Error("No email found"));
}
let user = await User.findOne({
$or: [{ googleId: profile.id }, { email }],
});
if (user && !user.googleId) {
user.googleId = profile.id;
await user.save();
}
const username = await generateUsername(profile.displayName);
if (!user) {
user = await User.create({
username,
fullName: profile.displayName,
email,
googleId: profile.id,
avatarUrl: profile.photos?.[0]?.value || "",
});
}
user = await User.findById(user._id).select(
"-password -refreshToken -googleId"
);
if (!user) {
return done(new Error("User not found after creation"));
}
return done(null, user);
} catch (error) {
return done(error as Error);
}
}
)
);
export default passport;

Create: src/utils/usernameGen.ts

Your logic should:

  • Convert full name into username
  • Add numbers if username already exists
  • Ensure uniqueness

Example:

john
john12
john123

src/controllers/auth.controller.ts

export const googleAuth = asyncHandler(async (req: Request, res: Response) => {
const user = req.user!;
const { accessToken, refreshToken } =
await generateTokens(user._id);
return res
.status(200)
.cookie("accessToken", accessToken, cookieOptions)
.cookie("refreshToken", refreshToken, cookieOptions)
.json({
success: true,
user,
});
});

src/routes/auth.route.ts

router.route("/google").get(
passport.authenticate("google", {
scope: ["profile", "email"],
session: false,
})
);
router.route("/google/callback").get(
passport.authenticate("google", {
failureRedirect: "/login",
session: false,
failureMessage: "Failed to login with Google",
}),
googleAuth
);

Inside app.ts

import passport from "./config/passport.js";
app.use(passport.initialize());

  1. User visits: /api/v1/auth/google
  2. Google login page opens
  3. User authenticates
  4. Google redirects back to callback URL
  5. User gets JWT tokens
  6. Cookies are set

Open: http://localhost:3000/api/v1/auth/google

If configured correctly:

  • Google login opens
  • User account gets created
  • JWT cookies are generated

Ensure callback URL matches exactly in:

  • Google Console
  • .env
  • Passport Strategy

Make sure scope contains:

scope: ["profile", "email"]

Use secure cookies in production:

secure: true,
httpOnly: true,
sameSite: "strict",

You now have Google Authentication working with:

  • Express.js
  • TypeScript
  • Mongoose
  • Passport.js
  • JWT

You can further extend this with:

  • GitHub OAuth
  • Discord OAuth
  • Role-based auth
  • Email verification

Happy coding 🚀

Comments