Using Google OAuth with Express
Prerequisites
- Node.js and npm (or yarn) installed
- Basic understanding of React and Express
- A Google Cloud Platform project
- A Google OAuth client ID and secret
Google Cloud Setup
Step 1: Set Up Google Cloud Project
- Open Google Console
- Create a new Google Cloud Platform project.
- Open OAuth consent screen (API & Services > Oauth consent screen)
- Proceed trough the flow
5. Create OAuth credentials:
- Go to the Credentials page.
- Create an OAuth client ID.
- Choose “Web application” as the application type.
- Add your authorized JavaScript origins (e.g., http://localhost:3000) and redirect URIs (e.g., http://localhost:5000/auth/google/callback).
- Download the JSON key file or copy the id and secret.
Express Setup
Installing dependencies
1npm install googleapis crypto express express-session
In this tutorial, I assume you already familiar with express, so I’ll not giving the complete code.. if you need it, please put a comment.. I’ll update it
Setup auth routers
I have routers that function to handle authentication
1//routers/auth.js
2
3import express from "express";
4import { getMe, getUrl, handleCallback } from "@/controllers/auth";
5export const router = express.Router();
6
7router.get("/", getUrl);
8router.get("/me", getMe);
9router.get("/callback", handleCallback);
10
11export default router;
12
So in this routers, I have 3 routes for auth, handling the auth callback, and getting the user auth info
Setup Controller
1//controllers/auth.js
2
3import { google } from "googleapis";
4import crypto from "crypto";
5const oauth2Client = new google.auth.OAuth2(
6 process.env.GOOGLE_CLIENT_ID,
7 process.env.GOOGLE_CLIENT_SECRET,
8 process.env.GOOGLE_REDIRECT_URL
9);
10
11export const getUrl = async (req, res) => {
12 const state = crypto.randomBytes(32).toString("hex");
13 req.session.state = state;
14 const url = oauth2Client.generateAuthUrl({
15 scope: [
16 "https://www.googleapis.com/auth/userinfo.email",
17 "https://www.googleapis.com/auth/userinfo.profile",
18 ],
19 state,
20 });
21 res.redirect(url);
22};
23
24export const handleCallback = async (req, res) => {
25 const query = req.query;
26 const { code, state } = query;
27 if (state !== req.session.state) {
28 console.log("State mismatch. Possible CSRF attack");
29 return res.status(403).send("State mismatch.");
30 } else {
31 let { tokens } = await oauth2Client.getToken(code);
32 return res.send(tokens);
33 }
34};
35
36export const getMe = async (req, res) => {
37 if (!req.session.tokens) {
38 return res.status(403).send("Unauthenticated");
39 }
40 oauth2Client.setCredentials(req.session.tokens);
41
42 const oauth2 = google.oauth2({
43 auth: oauth2Client,
44 version: "v2",
45 });
46 const { data } = await oauth2.userinfo.get();
47 return res.send(data);
48};
49
Explanation
1const oauth2Client = new google.auth.OAuth2( process.env.GOOGLE_CLIENT_ID, process.env.GOOGLE_CLIENT_SECRET, process.env.GOOGLE_REDIRECT_URL);
2
3export const getUrl = async (req, res) => {
4 const state = crypto.randomBytes(32).toString("hex");
5 req.session.state = state;
6 const url = oauth2Client.generateAuthUrl({
7 scope: [
8 "https://www.googleapis.com/auth/userinfo.email",
9 "https://www.googleapis.com/auth/userinfo.profile",
10 ],
11 state,
12 });
13 res.redirect(url);
14};
- GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, and GOOGLE_REDIRECT_URL we got those value from the previous step above
- state is optional, but good to have to reduce the risk of CSRF attacks.
- those function will generate the auth login, and redirect users to OAuth page.
1export const handleCallback = async (req, res) => {
2 const query = req.query;
3 const { code, state } = query;
4 if (state !== req.session.state) {
5 return res.status(403).send("State mismatch.");
6 } else {
7 let { tokens } = await oauth2Client.getToken(code);
8 req.session.tokens = tokens;
9 return res.send(tokens);
10 }
11};
please make sure the route to handle this function is match GOOGLE_REDIRECT_URL.
in this part
1 if (state !== req.session.state) {
2 console.log("State mismatch. Possible CSRF attack");
3 return res.status(403).send("State mismatch.");
4 } else {
Is only required if you are using state in previous step. if you didn’t use it, you can skip it.
1 let { tokens } = await oauth2Client.getToken(code);
2 req.session.tokens = tokens;
3 return res.send(tokens);
this part will convert code from callback to tokens that you can consume.
this is just the basic example. you can consume at frontend, or you can put it session or cookies so you can use it later.
1export const getMe = async (req, res) => {
2 if (!req.session.tokens) {
3 return res.status(403).send("Unauthenticated");
4 }
5 oauth2Client.setCredentials(req.session.tokens);
6
7 const oauth2 = google.oauth2({
8 auth: oauth2Client,
9 version: "v2",
10 });
11 const { data } = await oauth2.userinfo.get();
12 return res.send(data);
13};
this part will get the userprofile data.
I hope this post can help you, thank you for reading