From ee82439e6773e4173b6a20dbd60436f1d62599e4 Mon Sep 17 00:00:00 2001 From: Xie Yanbo Date: Wed, 30 Jul 2025 16:00:15 +0800 Subject: [PATCH] feat: add Feishu OAuth integration Implement Feishu OAuth provider using standard client: - Set up Feishu-specific endpoints for authorization, token, and userinfo - Use user_id as sub claim for Feishu user identification - Extract correct user information from nested 'data' field in Feishu responses Configuration requirements: - Set FEISHU_CLIENT_ID and FEISHU_CLIENT_SECRET environment variables to enable Feishu OAuth - Set ENABLE_OAUTH_SIGNUP=true to allow automatic user creation after OAuth login - Set DEFAULT_USER_ROLE=user to grant immediate access after OAuth registration - Set OAUTH_MERGE_ACCOUNTS_BY_EMAIL=true to enable merging of existing user accounts with matching emails --- backend/dev.sh | 2 +- backend/open_webui/config.py | 52 +++++++++++++++++++++++++++++++ backend/open_webui/utils/oauth.py | 2 ++ src/routes/auth/+page.svelte | 10 ++++++ 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/backend/dev.sh b/backend/dev.sh index 504b8f7554..042fbd9efa 100755 --- a/backend/dev.sh +++ b/backend/dev.sh @@ -1,3 +1,3 @@ -export CORS_ALLOW_ORIGIN="http://localhost:5173" +export CORS_ALLOW_ORIGIN="http://localhost:5173;http://localhost:8080" PORT="${PORT:-8080}" uvicorn open_webui.main:app --port $PORT --host 0.0.0.0 --forwarded-allow-ips '*' --reload diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index 069faab439..efa37a0142 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -513,6 +513,30 @@ OAUTH_GROUPS_CLAIM = PersistentConfig( os.environ.get("OAUTH_GROUPS_CLAIM", os.environ.get("OAUTH_GROUP_CLAIM", "groups")), ) +FEISHU_CLIENT_ID = PersistentConfig( + "FEISHU_CLIENT_ID", + "oauth.feishu.client_id", + os.environ.get("FEISHU_CLIENT_ID", ""), +) + +FEISHU_CLIENT_SECRET = PersistentConfig( + "FEISHU_CLIENT_SECRET", + "oauth.feishu.client_secret", + os.environ.get("FEISHU_CLIENT_SECRET", ""), +) + +FEISHU_OAUTH_SCOPE = PersistentConfig( + "FEISHU_OAUTH_SCOPE", + "oauth.feishu.scope", + os.environ.get("FEISHU_OAUTH_SCOPE", "contact:user.base:readonly"), +) + +FEISHU_REDIRECT_URI = PersistentConfig( + "FEISHU_REDIRECT_URI", + "oauth.feishu.redirect_uri", + os.environ.get("FEISHU_REDIRECT_URI", ""), +) + ENABLE_OAUTH_ROLE_MANAGEMENT = PersistentConfig( "ENABLE_OAUTH_ROLE_MANAGEMENT", "oauth.enable_role_mapping", @@ -705,6 +729,32 @@ def load_oauth_providers(): "register": oidc_oauth_register, } + if FEISHU_CLIENT_ID.value and FEISHU_CLIENT_SECRET.value: + def feishu_oauth_register(client: OAuth): + client.register( + name="feishu", + client_id=FEISHU_CLIENT_ID.value, + client_secret=FEISHU_CLIENT_SECRET.value, + access_token_url="https://open.feishu.cn/open-apis/authen/v2/oauth/token", + authorize_url="https://accounts.feishu.cn/open-apis/authen/v1/authorize", + api_base_url="https://open.feishu.cn/open-apis", + userinfo_endpoint="https://open.feishu.cn/open-apis/authen/v1/user_info", + client_kwargs={ + "scope": FEISHU_OAUTH_SCOPE.value, + **( + {"timeout": int(OAUTH_TIMEOUT.value)} + if OAUTH_TIMEOUT.value + else {} + ), + }, + redirect_uri=FEISHU_REDIRECT_URI.value, + ) + + OAUTH_PROVIDERS["feishu"] = { + "register": feishu_oauth_register, + "sub_claim": "user_id", + } + configured_providers = [] if GOOGLE_CLIENT_ID.value: configured_providers.append("Google") @@ -712,6 +762,8 @@ def load_oauth_providers(): configured_providers.append("Microsoft") if GITHUB_CLIENT_ID.value: configured_providers.append("GitHub") + if FEISHU_CLIENT_ID.value: + configured_providers.append("Feishu") if configured_providers and not OPENID_PROVIDER_URL.value: provider_list = ", ".join(configured_providers) diff --git a/backend/open_webui/utils/oauth.py b/backend/open_webui/utils/oauth.py index 7eedc30c31..f48a1756b7 100644 --- a/backend/open_webui/utils/oauth.py +++ b/backend/open_webui/utils/oauth.py @@ -602,6 +602,8 @@ class OAuthManager: or (auth_manager_config.OAUTH_USERNAME_CLAIM not in user_data) ): user_data: UserInfo = await client.userinfo(token=token) + if provider == "feishu" and isinstance(user_data, dict) and "data" in user_data: + user_data = user_data["data"] if not user_data: log.warning(f"OAuth callback failed, user data is missing: {token}") raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED) diff --git a/src/routes/auth/+page.svelte b/src/routes/auth/+page.svelte index 68c0df81ea..62eb47e6b0 100644 --- a/src/routes/auth/+page.svelte +++ b/src/routes/auth/+page.svelte @@ -523,6 +523,16 @@ > {/if} + {#if $config?.oauth?.providers?.feishu} + + {/if} {/if}