User Authentication with Node.js, JWT and Google OAuth 2.0 | Backend Cookbook 5

yes
yes
Nov 5 · 4 min read

User Authentication(Log in、Sign up、Log out)對 Stateful Application 來說是個必要的功能,而目前市面上的 App 幾乎都有提供 Log in with Google 這樣子的 OAuth-based solution,其優點之一就是把 Authentication 的流程簡化了而讓 User 覺得方便。


TL;DR

為了讓 User 能用 Google 帳號登入網站,我們得實作 OAuth-based Google Login,需要一個 Frontend、一個 Backend,並設定好 Resource / Authorization Server (Google OAuth 2.0)。


Steps

  1. OAuth 2.0 Roles
  2. OAuth-based Google Login Flow Chart
  3. Setting up Google OAuth 2.0
  4. Client Backend
  5. Client Frontend

1. OAuth 2.0 Roles

從 OAuth 的 role 可以看出 OAuth 的架構並對應到後面的實作,所以在這邊特別列出來。

  • Resource owner
    在這裡是指要登入網站的 User,Resource 指的是 User 的 email、profile、Google id 等。
  • Resource server
    就是 Google。
  • Client
    在這裡是指我們要實作的網站的 Frontend 加 Backend。
  • Authorization server
    也是 Google,Backend 需要跟他要 OAuth token。

2. OAuth-based Google Login Flow Chart

用一張圖說明接下來要實作什麼。

OAuth-based Google Login Flow Chart

3. Setting up Google OAuth 2.0

開始實作囉~。

首先在 Google API Console 裡開一個 Project。

Create a project on Google API Console.

接著 Create 一個 OAuth client,取得 Client ID 和 Client Secret,並設定 Authorized Origin 和 Redirect URL。

最後,設定 API 的 Scope,這邊我們只需要 User 的 email、profile 和 openid。


4. Client Backend

使用 PostgresSQLExpress.jsgoogle-api-nodejs-client,按照上面的 Flow Chart 來實作。

// .env
GOOGLE_API_CLIENT_ID=
GOOGLE_API_CLIENT_SECRET=
GOOGLE_API_REDIRECT=http://localhost:3000/login-google
// Create Auth URL for Client Frontend
const { google } = require("googleapis");
const oAuth2Client = new google.auth.OAuth2(
clientId: process.env.GOOGLE_API_CLIENT_ID,
clientSecret: process.env.GOOGLE_API_CLIENT_SECRET,
redirect: process.env.GOOGLE_API_REDIRECT
);
function getAuthUrl() {
return oauth2Client.generateAuthUrl({
access_type: "offline",
prompt: "consent",
scope: [
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile"
]
});
}
app.get("/login", async (req, res) => {
const url = getAuthUrl()
res.json({ url });
});
// Handle Redirect URL
app.get("/login-google", async (req, res) => {
const { code } = req.query;
const { tokens } = await oAuth2Client.getToken(code);
oAuth2Client.credentials = tokens;
const oauth2 = google.oauth2("v2");
// Get Google User
const {
data: { email, id: google_open_id }
} = await oauth2.userinfo.v2.me.get({
auth: oAuth2Client
});
// Upsert User
const { rows } = await client.query(`
insert into
user (email, google_open_id)
values ($1, $2)
on conflict (google_open_id)
do update set email=$1
returning *`,
[email, google_open_id]
);
const user = rows[0];
// Create JWT
var token = jwt.sign(user, secret);
// Login Redirect to Frontend
res.redirect(`${process.env.WEB_BASE_URL}/login/google?token=${token}`);
  • access_type: Indicates whether your application can refresh access tokens when the user is not present at the browser.
  • prompt: Optional. A space-delimited, case-sensitive list of prompts to present the user.
  • scope: OAuth 2.0 Scopes for Google APIs

5. Client Frontend

使用 React,按照上面的 Flow Chart 實作。

首先會跟 Client Backend 拿到 Auth URL,Redirect 過去後就會出現 Google 的 Log in 畫面。

// Get and Open Auth URL
const login = async() => {
const {
data: { url }
} = await axios.request({
method: "get",
url: `${process.env.API_BASE_URL}/login`
});
window.location = url;
}
Redirect to the Auth URL to log in with Google

成功 Log in Google 後 Google 會 Redirect 到 Client Backend,Client Backend 會再帶一個 Token 並 Redirect 回 Client Frontend,最後 Frontend 就得處理這個 Login Request 和 Token。

// Handle Login Request
import { useEffect } from "react";
import qs from "qs";
useEffect(() => {
const token =
qs.parse(
props.location.search,
{ ignoreQueryPrefix: true }
).token || localStorage.getItem("token");
if (token) {
// Save the JWT (JSON Web Token)
localStorage.setItem("token", token);
...
} else {
// Unautherized
}
}, []);

Demo through Paaaack!

Reference

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade