最終更新 9 months ago

使用 Flask 和 Authlib 整合 Google OAuth2,透過 OpenID Connect 進行身份驗證,讓使用者能安全登入應用程式。實作包括 authorize_redirect 及 parse_id_token,確保驗證的完整性與安全性。

.env Raw
1GOOGLE_CLIENT_ID=你的 Google Client ID
2GOOGLE_CLIENT_SECRET=你的 Google Client Secret
3
flask_google_oauth_example.py Raw
1from flask import Flask, redirect, url_for, session, request
2from authlib.integrations.flask_client import OAuth
3import os
4from dotenv import load_dotenv
5
6# 載入環境變數
7load_dotenv()
8
9app = Flask(__name__)
10app.secret_key = os.getenv("FLASK_SECRET_KEY") # 從環境變數讀取
11
12oauth = OAuth(app)
13
14# 建立 Keycloak 的 OIDC 設定
15# 透過 server_metadata_url 自動取得 OIDC Discovery 設定
16keycloak = oauth.register(
17 name='keycloak',
18 client_id=os.getenv("KEYCLOAK_CLIENT_ID"),
19 client_secret=os.getenv("KEYCLOAK_CLIENT_SECRET"),
20 server_metadata_url=f"{os.getenv('KEYCLOAK_BASE_URL')}/realms/{os.getenv('KEYCLOAK_REALM')}/.well-known/openid-configuration",
21 client_kwargs={
22 'scope': 'openid profile email', # 根據需求調整
23 }
24)
25
26@app.route('/')
27def homepage():
28 return '歡迎!<a href="/login">使用 Keycloak 登入</a>'
29
30@app.route('/login')
31def login():
32 # 生成一個隨機 nonce 作為一次性驗證令牌
33 nonce = os.urandom(16).hex()
34 session['nonce'] = nonce
35 return keycloak.authorize_redirect(
36 redirect_uri=url_for('auth', _external=True),
37 nonce=nonce
38 )
39
40@app.route('/auth')
41def auth():
42 # 取得 Keycloak 回傳的參數並換取 token
43 token = keycloak.authorize_access_token()
44 nonce = session.get('nonce')
45 user_info = keycloak.parse_id_token(token, nonce=nonce)
46 # 將使用者資訊存入 session,並清除 nonce 避免重複驗證
47 session['user'] = user_info
48 session.pop('nonce', None)
49 # 重導到乾淨的頁面
50 return redirect(url_for('profile'))
51
52@app.route('/profile')
53def profile():
54 user_info = session.get('user')
55 if user_info:
56 return f"歡迎, {user_info.get('name', '使用者')}!<br><a href='/logout'>登出</a>"
57 return redirect(url_for('homepage'))
58
59@app.route('/logout')
60def logout():
61 # 清除 session 中的所有資料
62 session.clear()
63
64 # 組成 Keycloak 的登出 URL
65 logout_url = f"{os.getenv('KEYCLOAK_BASE_URL')}/realms/{os.getenv('KEYCLOAK_REALM')}/protocol/openid-connect/logout"
66
67 # 登出後重導回首頁
68 redirect_uri = url_for('homepage', _external=True)
69
70 # 將重導 URI 作為參數附加到 Keycloak 登出 URL 上
71 return redirect(f"{logout_url}?redirect_uri={redirect_uri}")
72
73if __name__ == '__main__':
74 app.run(debug=True)
75
76