Skip to content

feat(auth): add Feishu OAuth2 login provider - #696

Open
yhd4711499 wants to merge 4 commits into
iflytek:mainfrom
yhd4711499:feat/feishu-oauth-login
Open

feat(auth): add Feishu OAuth2 login provider#696
yhd4711499 wants to merge 4 commits into
iflytek:mainfrom
yhd4711499:feat/feishu-oauth-login

Conversation

@yhd4711499

Copy link
Copy Markdown

What

Adds Feishu (飞书/Lark) as an OAuth2 login provider alongside GitHub and GitLab. When OAUTH2_FEISHU_CLIENT_ID/OAUTH2_FEISHU_CLIENT_SECRET are configured, the login page automatically shows a Feishu button (the provider catalog is config-driven).

Why

Enterprise deployments need Feishu SSO. This was implemented and verified end-to-end against a real Feishu self-built app (QR login → callback → account provisioning → re-login dedup).

How

Feishu deviates from standard OAuth2, so the Spring oauth2Login pipeline is reused with three targeted customizations:

  • Authorize endpoint: Feishu requires app_id instead of client_id and does not accept a scope parameter (scopes are configured on the open-platform app). SkillHubOAuth2AuthorizationRequestResolver rebuilds the authorization URI for the feishu registration only.
  • Userinfo envelope: Feishu returns {code, msg, data} with errors reported as HTTP 200, which DefaultOAuth2UserService cannot consume. New ProviderOAuth2UserService extension point + FeishuOAuth2UserService unwrap data into flat attributes; OAuthLoginFlowService selects the loader per registrationId (GitHub/GitLab unchanged).
  • Token endpoint: uses client_secret_post.
  • Identity: binds on open_id (stable per app); union_id is kept in extra for potential cross-app migration. Email prefers enterprise_email over email; users without email are denied under the EMAIL_DOMAIN access policy (documented).

Also includes:

  • web/public/feishu-logo.svg (the only frontend change; button rendering/i18n adapt automatically)
  • .env.release.example and docs/03-authentication-design.md updates (Feishu as the non-standard provider integration template)
  • Separate fix: SKILLHUB_TRUST_FORWARDED_PROTO was undefined in docker-compose.staging.yml, breaking make staging with an nginx "unknown variable" error (pre-existing on main)

Testing

  • ./mvnw -pl skillhub-auth -am test: green (new FeishuOAuth2UserServiceTest, FeishuClaimsExtractorTest, resolver app_id/no-scope assertions)
  • make typecheck-web, make lint-web: green
  • make staging: green after the compose env fix
  • E2E with a real Feishu app: login button visible, redirect carries app_id, callback provisions the account (display name + avatar synced), second login reuses the same binding (1 binding / 1 user)
  • Note: ApiAccessDeniedHandlerTest has 2 pre-existing locale-dependent failures on this machine (fails on clean HEAD too, unrelated to this change)

Impact

  • No breaking changes; Feishu stays hidden until real credentials are configured (placeholder client-id is filtered by AuthMethodCatalog)
  • Operators: set OAUTH2_FEISHU_CLIENT_ID / OAUTH2_FEISHU_CLIENT_SECRET, grant contact:user.base:readonly + contact:user.email:readonly on the app, and register <base-url>/login/oauth2/code/feishu as a redirect URL

@CLAassistant

CLAassistant commented Aug 7, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Feishu deviates from standard OAuth2: the authorize endpoint requires
app_id instead of client_id, userinfo returns a {code, msg, data}
envelope with errors reported as HTTP 200, and the token endpoint uses
client_secret_post. Reuse the Spring oauth2Login pipeline and override
only the userinfo loading step via a ProviderOAuth2UserService extension
point, keeping GitHub/GitLab behavior unchanged. Bindings use open_id
as subject; union_id is kept in extra for future cross-app migration.

Signed-off-by: yhd <yhd4711499@live.com>
The nginx template references this variable and envsubst leaves
undefined variables untouched, causing nginx to fail with
'unknown skillhub_trust_forwarded_proto variable' when the staging
web container starts.

Signed-off-by: yhd <yhd4711499@live.com>
@yhd4711499
yhd4711499 force-pushed the feat/feishu-oauth-login branch from 93ef04a to 0bfedc4 Compare August 11, 2026 07:43
Replace the hand-drawn approximation with the official Feishu/Lark
tri-color brand mark (teal/blue/navy), matching the feishu.cn logo.
Source: homarr-labs/dashboard-icons.

Signed-off-by: yhd <yhd4711499@live.com>

@FenjuFu FenjuFu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes for two protocol/correctness issues:

  1. The custom resolver targets https://open.feishu.cn/open-apis/authen/v1/authorize, replaces client_id with app_id, and removes scope. Feishu's current server-side OAuth documentation specifies https://accounts.feishu.cn/open-apis/authen/v1/authorize, client_id, and optional scope, and recommends standard OAuth clients. Please switch to the current endpoint and standard parameters, or provide an official supported-flow reference and tests showing why the legacy app_id flow is required.

  2. FeishuClaimsExtractor marks every returned email as verified. Feishu's user-info documentation states that email and mobile fields are administrator-imported and are not verified with the user in real time. Please keep emailVerified false unless an independent verification signal exists, and update the tests.

The authentication module suite passes locally (126 tests), so these findings concern protocol compatibility and claim semantics rather than unit-test regressions.

Addresses review on PR iflytek#696:

1. The authorize step now uses the currently documented endpoint
   https://accounts.feishu.cn/open-apis/authen/v1/authorize with standard
   OAuth2 parameters (client_id, optional scope), so the legacy
   app_id/no-scope URI rewrite in SkillHubOAuth2AuthorizationRequestResolver
   is removed. The authorize host is overridable via
   OAUTH2_FEISHU_AUTHORIZE_URI; token and userinfo endpoints stay on
   OAUTH2_FEISHU_BASE_URI.

2. FeishuClaimsExtractor no longer claims emailVerified: Feishu emails are
   admin-imported and carry no real-time verification signal, so
   emailVerified is always false. EMAIL_DOMAIN policy matches the email
   domain only and is unaffected.

Signed-off-by: yhd <yhd4711499@live.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants