Zuletzt aktiv 9 months ago

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

timmy hat die Gist bearbeitet 9 months ago. Zu Änderung gehen

1 file changed, 48 insertions, 18 deletions

flask_google_oauth_example.py

@@ -1,45 +1,75 @@
1 - from flask import Flask, redirect, url_for, session
1 + from flask import Flask, redirect, url_for, session, request
2 2 from authlib.integrations.flask_client import OAuth
3 3 import os
4 4 from dotenv import load_dotenv
5 5
6 + # 載入環境變數
6 7 load_dotenv()
8 +
7 9 app = Flask(__name__)
8 - app.secret_key = "your_secret_key" # 必須設置 Secret Key
10 + app.secret_key = os.getenv("FLASK_SECRET_KEY") # 從環境變數讀取
9 11
10 12 oauth = OAuth(app)
11 13
12 - # 註冊 Google OAuth2 提供者,使用 discovery endpoint 自動獲取 metadata
13 - google = oauth.register(
14 - name='google',
15 - client_id=os.getenv("GOOGLE_CLIENT_ID"),
16 - client_secret=os.getenv("GOOGLE_CLIENT_SECRET"),
17 - server_metadata_url='https://accounts.google.com/.well-known/openid-configuration',
14 + # 建立 Keycloak 的 OIDC 設定
15 + # 透過 server_metadata_url 自動取得 OIDC Discovery 設定
16 + keycloak = 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",
18 21 client_kwargs={
19 - 'scope': 'openid profile email',
22 + 'scope': 'openid profile email', # 根據需求調整
20 23 }
21 24 )
22 25
23 26 @app.route('/')
24 27 def homepage():
25 - return '歡迎!<a href="/login">使用 Google 登入</a>'
28 + return '歡迎!<a href="/login">使用 Keycloak 登入</a>'
26 29
27 30 @app.route('/login')
28 31 def login():
29 - # 生成一個隨機 nonce,作為一次性驗證令牌
32 + # 生成一個隨機 nonce 作為一次性驗證令牌
30 33 nonce = os.urandom(16).hex()
31 34 session['nonce'] = nonce
32 - # 將 nonce 傳遞給 authorize_redirect
33 - return google.authorize_redirect(url_for('auth', _external=True), nonce=nonce)
35 + return keycloak.authorize_redirect(
36 + redirect_uri=url_for('auth', _external=True),
37 + nonce=nonce
38 + )
34 39
35 40 @app.route('/auth')
36 41 def auth():
37 - token = google.authorize_access_token()
38 - # 從 session 中取得先前生成的 nonce
42 + # 取得 Keycloak 回傳的參數並換取 token
43 + token = keycloak.authorize_access_token()
39 44 nonce = session.get('nonce')
40 - # 將 nonce 傳入以驗證 ID Token
41 - user_info = google.parse_id_token(token, nonce=nonce)
42 - return f"歡迎, {user_info['name']}!"
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')
53 + def 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')
60 + def 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}")
43 72
44 73 if __name__ == '__main__':
45 74 app.run(debug=True)
75 +

timmy hat die Gist bearbeitet 10 months ago. Zu Änderung gehen

2 files changed, 47 insertions

.env(Datei erstellt)

@@ -0,0 +1,2 @@
1 + GOOGLE_CLIENT_ID=你的 Google Client ID
2 + GOOGLE_CLIENT_SECRET=你的 Google Client Secret

flask_google_oauth_example.py(Datei erstellt)

@@ -0,0 +1,45 @@
1 + from flask import Flask, redirect, url_for, session
2 + from authlib.integrations.flask_client import OAuth
3 + import os
4 + from dotenv import load_dotenv
5 +
6 + load_dotenv()
7 + app = Flask(__name__)
8 + app.secret_key = "your_secret_key" # 必須設置 Secret Key
9 +
10 + oauth = OAuth(app)
11 +
12 + # 註冊 Google OAuth2 提供者,使用 discovery endpoint 自動獲取 metadata
13 + google = oauth.register(
14 + name='google',
15 + client_id=os.getenv("GOOGLE_CLIENT_ID"),
16 + client_secret=os.getenv("GOOGLE_CLIENT_SECRET"),
17 + server_metadata_url='https://accounts.google.com/.well-known/openid-configuration',
18 + client_kwargs={
19 + 'scope': 'openid profile email',
20 + }
21 + )
22 +
23 + @app.route('/')
24 + def homepage():
25 + return '歡迎!<a href="/login">使用 Google 登入</a>'
26 +
27 + @app.route('/login')
28 + def login():
29 + # 生成一個隨機 nonce,作為一次性驗證令牌
30 + nonce = os.urandom(16).hex()
31 + session['nonce'] = nonce
32 + # 將 nonce 傳遞給 authorize_redirect
33 + return google.authorize_redirect(url_for('auth', _external=True), nonce=nonce)
34 +
35 + @app.route('/auth')
36 + def auth():
37 + token = google.authorize_access_token()
38 + # 從 session 中取得先前生成的 nonce
39 + nonce = session.get('nonce')
40 + # 將 nonce 傳入以驗證 ID Token
41 + user_info = google.parse_id_token(token, nonce=nonce)
42 + return f"歡迎, {user_info['name']}!"
43 +
44 + if __name__ == '__main__':
45 + app.run(debug=True)
Neuer Älter