Google Login 實作
Google Cloud Console 設定、OAuth 授權流程與 ID Token 驗證實務
Overview##
上一篇介紹了 OAuth 2.0 和 OIDC 的原理。這篇聚焦在 Google Login 的實際設定和實作——從 Google Cloud Console 設定到完整的登入流程。
Google Login 基於標準的 OAuth 2.0 Authorization Code Flow + OIDC,是最常見的社群登入方式之一。
sequenceDiagram
participant U as User
participant C as Your App
participant G as Google
U->>C: Click Login with Google
C->>G: Redirect to /authorize
G->>U: Consent screen
U->>G: Grant permission
G->>C: Redirect with code
C->>G: Exchange code for tokens
G->>C: access_token + id_token
C->>C: Verify ID Token
C->>U: Login success
Note
Google Login 同時支援 Web、Android、iOS。本文以 Web(server-side)為主。
Setup##
Google Cloud Console 設定###
- 前往 Google Cloud Console
- 建立或選擇 Project
- APIs & Services → Credentials → Create Credentials → OAuth Client ID
- 設定 OAuth consent screen(應用名稱、logo、scope)
- Application type 選 Web application
- 設定 Authorized redirect URIs
# 開發環境
http://localhost:3000/auth/google/callback
# 生產環境
https://yourapp.com/auth/google/callback
取得 Client ID 和 Client Secret。
Consent Screen 設定###
| 設定 | 說明 |
|---|---|
| User Type | External(公開)或 Internal(僅組織內) |
| App Name | 顯示在授權頁面的應用名稱 |
| Scopes | 請求的權限(建議最小化) |
| Test Users | 開發階段的測試帳號 |
Warning
新建立的 OAuth App 預設在「Testing」狀態,只有 Test Users 能登入。要開放給所有使用者,需要通過 Google 的驗證審核(Verification),審核通常需要數天到數週。
Usage##
Step 1 — 導向授權頁面###
// GET /auth/google
import crypto from 'crypto';
function getGoogleAuthUrl(): string {
const state = crypto.randomBytes(16).toString('hex');
const nonce = crypto.randomBytes(16).toString('hex');
// Store state and nonce in session for later verification
const params = new URLSearchParams({
client_id: process.env.GOOGLE_CLIENT_ID!,
redirect_uri: 'https://yourapp.com/auth/google/callback',
response_type: 'code',
scope: 'openid email profile',
state,
nonce,
access_type: 'offline', // Request refresh token
prompt: 'consent', // Force consent screen
});
return `https://accounts.google.com/o/oauth2/v2/auth?${params}`;
}
| 參數 | 說明 |
|---|---|
scope | openid email profile 取得身份、email、名稱和頭像 |
access_type | offline 才會回傳 refresh token |
prompt | consent 強制顯示同意畫面(確保拿到 refresh token) |
Step 2 — 處理回調###
// GET /auth/google/callback
async function handleGoogleCallback(code: string, state: string) {
// 1. Verify state matches session
// 2. Exchange code for tokens
const tokenRes = await fetch('https://oauth2.googleapis.com/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
code,
client_id: process.env.GOOGLE_CLIENT_ID!,
client_secret: process.env.GOOGLE_CLIENT_SECRET!,
redirect_uri: 'https://yourapp.com/auth/google/callback',
grant_type: 'authorization_code',
}),
});
const tokens = await tokenRes.json();
// 3. Verify and decode ID token
const { OAuth2Client } = await import('google-auth-library');
const client = new OAuth2Client(process.env.GOOGLE_CLIENT_ID);
const ticket = await client.verifyIdToken({
idToken: tokens.id_token,
audience: process.env.GOOGLE_CLIENT_ID,
});
const payload = ticket.getPayload()!;
// 4. Find or create user in database
const user = await findOrCreateUser({
provider: 'google',
providerId: payload.sub, // Unique, stable ID
email: payload.email!,
name: payload.name!,
picture: payload.picture,
});
return user;
}
Step 3 — 使用 Access Token 取得更多資料###
如果需要 ID Token 以外的資料(如 Google Calendar、Drive),用 access token 呼叫 Google API:
// Use access token to call Google APIs
const userInfoRes = await fetch('https://www.googleapis.com/oauth2/v3/userinfo', {
headers: { Authorization: `Bearer ${tokens.access_token}` },
});
const userInfo = await userInfoRes.json();
// { sub, name, given_name, family_name, picture, email, email_verified, locale }
Configuration##
Google OAuth Scopes###
| Scope | 提供的資料 |
|---|---|
openid | 使用者的唯一 ID(sub) |
email | Email 地址 + 是否已驗證 |
profile | 名稱、頭像 URL、locale |
Tip
只請求你真正需要的 scope。請求過多權限會讓使用者在 consent screen 上猶豫,降低轉換率。大多數登入場景只需要 openid email profile。
ID Token Payload###
Google 回傳的 ID Token(JWT)包含:
| 欄位 | 說明 | 範例 |
|---|---|---|
sub | 唯一 ID(不變) | "1234567890" |
email | "john@gmail.com" | |
email_verified | Email 是否已驗證 | true |
name | 全名 | "John Doe" |
picture | 頭像 URL | "https://lh3.google..." |
given_name | 名 | "John" |
family_name | 姓 | "Doe" |
locale | 語言 | "zh-TW" |
資料庫 Schema###
支援多 provider 的設計:
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255),
name VARCHAR(255),
avatar_url TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE oauth_accounts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id),
provider VARCHAR(50) NOT NULL, -- 'google' | 'apple'
provider_id VARCHAR(255) NOT NULL, -- sub from ID token
email VARCHAR(255),
UNIQUE(provider, provider_id)
);
Tip
用 oauth_accounts 表而非在 users 表加 google_id 欄位。這樣可以輕鬆擴展到更多 provider,也支援一個使用者綁定多個登入方式。
Quiz##
Summary##
- Google Login 基於標準 OAuth 2.0 Authorization Code Flow + OIDC
- 在 Google Cloud Console 建立 OAuth Client ID,取得
client_id和client_secret - 用
google-auth-library驗證 ID Token,以sub識別使用者 access_type: 'offline'取得 refresh token,prompt: 'consent'確保顯示同意畫面- 新 App 預設在 Testing 狀態,開放使用需通過 Google 驗證審核
- 資料庫用
oauth_accounts表支援多 provider 擴展
留言 (0)
登入後即可留言