diff --git a/python/device-oauth/main.py b/python/device-oauth/main.py index 38cf91b..f52d237 100644 --- a/python/device-oauth/main.py +++ b/python/device-oauth/main.py @@ -1,21 +1,44 @@ +import json from datetime import datetime -from cozepy import DeviceOAuthApp, load_oauth_app_from_config +from cozepy import DeviceOAuthApp, load_oauth_app_from_config, Coze, TokenAuth, User COZE_OAUTH_CONFIG_PATH = "coze_oauth_config.json" -def load_coze_oauth_app(config_path) -> DeviceOAuthApp: +def load_app_config(config_path) -> dict: with open(config_path, "r") as file: config = file.read() + return json.loads(config) + - return load_oauth_app_from_config(config) # type: ignore +def load_coze_oauth_app(config_path) -> DeviceOAuthApp: + try: + with open(config_path, "r") as file: + config = json.loads(file.read()) + return load_oauth_app_from_config(config) # type: ignore + except FileNotFoundError: + raise Exception( + f"Configuration file not found: {config_path}. Please make sure you have created the OAuth configuration file." + ) + except Exception as e: + raise Exception(f"Failed to load OAuth configuration: {str(e)}") def timestamp_to_datetime(timestamp: int) -> str: return datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S") +app_config = load_app_config(COZE_OAUTH_CONFIG_PATH) +coze_oauth_app = load_coze_oauth_app(COZE_OAUTH_CONFIG_PATH) + + +def users_me(access_token) -> User: + coze = Coze(auth=TokenAuth(access_token), base_url=app_config["coze_api_base"]) + + return coze.users.me() + + if __name__ == "__main__": coze_oauth_app = load_coze_oauth_app(COZE_OAUTH_CONFIG_PATH) @@ -32,3 +55,8 @@ def timestamp_to_datetime(timestamp: int) -> str: print(f"[device-oauth] refresh_token: {oauth_token.refresh_token}") expires_str = timestamp_to_datetime(oauth_token.expires_in) print(f"[device-oauth] expires_in: {oauth_token.expires_in} ({expires_str})") + + user = users_me(oauth_token.access_token) + print(f"[user_info] user_id: {user.user_id}") + print(f"[user_info] user_name: {user.user_name}") + print(f"[user_info] nick_name: {user.nick_name}") diff --git a/python/device-oauth/requirements.txt b/python/device-oauth/requirements.txt index db17a66..c89dc59 100644 --- a/python/device-oauth/requirements.txt +++ b/python/device-oauth/requirements.txt @@ -1 +1 @@ -cozepy==0.10.1 \ No newline at end of file +cozepy==0.11.0 \ No newline at end of file diff --git a/python/jwt-oauth/main.py b/python/jwt-oauth/main.py index 1047a3d..17ec860 100644 --- a/python/jwt-oauth/main.py +++ b/python/jwt-oauth/main.py @@ -25,7 +25,7 @@ def load_app_config(config_path) -> dict: def load_coze_oauth_app(config_path) -> JWTOAuthApp: try: with open(config_path, "r") as file: - config = file.read() + config = json.loads(file.read()) return load_oauth_app_from_config(config) # type: ignore except FileNotFoundError: raise Exception( diff --git a/python/jwt-oauth/requirements.txt b/python/jwt-oauth/requirements.txt index e54616b..05051b0 100644 --- a/python/jwt-oauth/requirements.txt +++ b/python/jwt-oauth/requirements.txt @@ -1,2 +1,2 @@ Flask==2.2.5 -cozepy==0.10.1 \ No newline at end of file +cozepy==0.11.0 \ No newline at end of file diff --git a/python/pkce-oauth/main.py b/python/pkce-oauth/main.py index a50fb88..e81d7f7 100644 --- a/python/pkce-oauth/main.py +++ b/python/pkce-oauth/main.py @@ -2,7 +2,7 @@ import secrets from datetime import datetime -from cozepy import load_oauth_app_from_config, PKCEOAuthApp +from cozepy import load_oauth_app_from_config, PKCEOAuthApp, TokenAuth, Coze from flask import Flask, redirect, request, session app = Flask( @@ -25,7 +25,7 @@ def load_app_config(config_path) -> dict: def load_coze_oauth_app(config_path) -> PKCEOAuthApp: try: with open(config_path, "r") as file: - config = file.read() + config = json.loads(file.read()) return load_oauth_app_from_config(config) # type: ignore except FileNotFoundError: raise Exception( @@ -52,6 +52,7 @@ def render_template(template: str, kwargs: dict) -> str: if not kwargs: kwargs = {} kwargs["coze_www_base"] = app_config["coze_www_base"] + kwargs["coze_api_base"] = app_config["coze_api_base"] template = read_html_template(template) for key, value in kwargs.items(): template = template.replace(f"{{{{{key}}}}}", str(value)) @@ -184,5 +185,26 @@ def refresh_token(): return {"error": f"Failed to refresh token: {str(e)}"}, 500 +@app.route("/users_me") +def users_me(): + access_token = request.args.get("access_token") + if not access_token: + return render_template( + "websites/error.html", {"error": "Access token is required"} + ) + coze = Coze(auth=TokenAuth(access_token), base_url=app_config["coze_api_base"]) + + try: + user = coze.users.me() + return { + "user_id": user.user_id, + "user_name": user.user_name, + "nick_name": user.nick_name, + "avatar_url": user.avatar_url, + } + except Exception as e: + return {"error": f"Failed to get user info: {str(e)}"}, 500 + + if __name__ == "__main__": app.run(debug=False, use_reloader=False, port=8080) diff --git a/python/pkce-oauth/requirements.txt b/python/pkce-oauth/requirements.txt index e54616b..05051b0 100644 --- a/python/pkce-oauth/requirements.txt +++ b/python/pkce-oauth/requirements.txt @@ -1,2 +1,2 @@ Flask==2.2.5 -cozepy==0.10.1 \ No newline at end of file +cozepy==0.11.0 \ No newline at end of file diff --git a/python/web-oauth/main.py b/python/web-oauth/main.py index 02c95c8..feb00c8 100644 --- a/python/web-oauth/main.py +++ b/python/web-oauth/main.py @@ -2,7 +2,7 @@ import secrets from datetime import datetime -from cozepy import load_oauth_app_from_config, WebOAuthApp +from cozepy import load_oauth_app_from_config, WebOAuthApp, Coze, TokenAuth from flask import Flask, redirect, request, session app = Flask( @@ -25,7 +25,7 @@ def load_app_config(config_path) -> dict: def load_coze_oauth_app(config_path) -> WebOAuthApp: try: with open(config_path, "r") as file: - config = file.read() + config = json.loads(file.read()) return load_oauth_app_from_config(config) # type: ignore except FileNotFoundError: raise Exception( @@ -48,6 +48,7 @@ def render_template(template: str, kwargs: dict) -> str: if not kwargs: kwargs = {} kwargs["coze_www_base"] = app_config["coze_www_base"] + kwargs["coze_api_base"] = app_config["coze_api_base"] template = read_html_template(template) for key, value in kwargs.items(): template = template.replace(f"{{{{{key}}}}}", str(value)) @@ -182,5 +183,26 @@ def refresh_token(): return {"error": f"Failed to refresh token: {str(e)}"}, 500 +@app.route("/users_me") +def users_me(): + access_token = request.args.get("access_token") + if not access_token: + return render_template( + "websites/error.html", {"error": "Access token is required"} + ) + coze = Coze(auth=TokenAuth(access_token), base_url=app_config["coze_api_base"]) + + try: + user = coze.users.me() + return { + "user_id": user.user_id, + "user_name": user.user_name, + "nick_name": user.nick_name, + "avatar_url": user.avatar_url, + } + except Exception as e: + return {"error": f"Failed to get user info: {str(e)}"}, 500 + + if __name__ == "__main__": app.run(debug=False, use_reloader=False, port=8080) diff --git a/python/web-oauth/requirements.txt b/python/web-oauth/requirements.txt index e54616b..05051b0 100644 --- a/python/web-oauth/requirements.txt +++ b/python/web-oauth/requirements.txt @@ -1,2 +1,2 @@ Flask==2.2.5 -cozepy==0.10.1 \ No newline at end of file +cozepy==0.11.0 \ No newline at end of file diff --git a/shared/websites/callback.html b/shared/websites/callback.html index 6964b80..e725aab 100644 --- a/shared/websites/callback.html +++ b/shared/websites/callback.html @@ -27,10 +27,49 @@ .card-body { padding: 3rem 2rem; } + .logo-section { + display: flex; + align-items: center; + justify-content: center; + gap: 1rem; + margin: 0 auto 1.5rem; + } .logo { width: 64px; height: 64px; - margin: 0 auto 1.5rem; + } + .user-avatar { + width: 64px; + height: 64px; + border-radius: 50%; + cursor: pointer; + position: relative; + } + .user-info-tooltip { + position: absolute; + background: white; + padding: 0.75rem 1rem; + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + min-width: 200px; + top: 100%; + left: 50%; + transform: translateX(-50%); + margin-top: 0.5rem; + display: none; + z-index: 1000; + } + .user-info-tooltip.show { + display: block; + } + .user-info-item { + margin: 0.25rem 0; + font-size: 0.875rem; + color: #333; + } + .user-info-label { + color: #666; + margin-right: 0.5rem; } .token-section { background-color: #f8f9ff; @@ -150,9 +189,11 @@
- - - +
+ + + +

Authorization Successful

You have successfully authorized the application. Here's your authorization information:

@@ -201,6 +242,7 @@

Access Token

// 隐藏 refresh token 行 document.querySelector('.refresh-token-row').style.display = 'none'; } + fetchUserInfo(); }); function showMessage(message, isSuccess = true) { @@ -271,6 +313,65 @@

Access Token

console.error('Failed to copy text: ', err); }); } + + // 获取用户信息并展示 + async function fetchUserInfo() { + const accessToken = document.getElementById('access_token').textContent; + const refreshTokenEle = document.getElementById('refresh_token'); + const isJWT = !refreshTokenEle || !refreshTokenEle.textContent.trim(); + + if (isJWT) { + return; // JWT 模式不请求用户信息 + } + + try { + const response = await fetch(`/users_me?access_token=${accessToken}`); + + if (!response.ok) { + throw new Error('Failed to fetch user info'); + } + + const userData = await response.json(); + + if (!userData.user_id) { + return; // 如果没有用户ID,不展示用户信息 + } + + // 创建用户头像和信息元素 + const userAvatarContainer = document.createElement('div'); + userAvatarContainer.style.position = 'relative'; + + const userAvatar = document.createElement('img'); + userAvatar.src = userData.avatar_url; + userAvatar.alt = 'User Avatar'; + userAvatar.className = 'user-avatar'; + + const userInfoTooltip = document.createElement('div'); + userInfoTooltip.className = 'user-info-tooltip'; + userInfoTooltip.innerHTML = ` + + + + `; + + userAvatarContainer.appendChild(userAvatar); + userAvatarContainer.appendChild(userInfoTooltip); + + // 添加鼠标事件 + userAvatar.addEventListener('mouseenter', () => { + userInfoTooltip.classList.add('show'); + }); + userAvatar.addEventListener('mouseleave', () => { + userInfoTooltip.classList.remove('show'); + }); + + // 将用户头像添加到logo区域 + const logoSection = document.getElementById('logoSection'); + logoSection.appendChild(userAvatarContainer); + } catch (error) { + console.error('Error fetching user info:', error); + } + } \ No newline at end of file