Skip to content

feat(auth): add DingTalk OAuth2 login support#467

Open
konglong87 wants to merge 4 commits into
iflytek:mainfrom
konglong87:feature/dingtalk-oauth2
Open

feat(auth): add DingTalk OAuth2 login support#467
konglong87 wants to merge 4 commits into
iflytek:mainfrom
konglong87:feature/dingtalk-oauth2

Conversation

@konglong87
Copy link
Copy Markdown

Add DingTalk (钉钉) OAuth2 login support to SkillHub.

DingTalk uses a non-standard OAuth2 flow:

  • Token exchange: JSON body instead of form-urlencoded
  • User info: Custom header x-acs-dingtalk-access-token instead of Authorization: Bearer

This PR leverages the existing OAuthClaimsExtractor strategy pattern, adding three provider-specific components:

┌─────────────────────────────┬────────────────────────────────────────────────────────────────────────┐
│ New file │ Purpose │
├─────────────────────────────┼────────────────────────────────────────────────────────────────────────┤
│ DingTalkClaimsExtractor │ Maps DingTalk fields (openId, nick, unionId) to normalized OAuthClaims │
├─────────────────────────────┼────────────────────────────────────────────────────────────────────────┤
│ DingTalkTokenResponseClient │ Handles DingTalk JSON token exchange │
├─────────────────────────────┼────────────────────────────────────────────────────────────────────────┤
│ DingTalkOAuth2UserService │ Fetches user info via DingTalk custom header │
└─────────────────────────────┴────────────────────────────────────────────────────────────────────────┘

SecurityConfig uses delegating wrappers to route registrationId=dingtalk to these custom components, preserving standard behavior for GitHub, GitLab, and OIDC.

Files changed (7 total)

New (4):

  • DingTalkClaimsExtractor.java
  • DingTalkTokenResponseClient.java
  • DingTalkOAuth2UserService.java
  • dingtalk-logo.svg

Modified (3):

  • SecurityConfig.java — added delegating token/user service routing
  • application.yml — added DingTalk OAuth2 registration (env vars, no hardcoded secrets)
  • .env.release.example — added OAUTH2_DINGTALK_CLIENT_ID/SECRET env vars

Zero-change components

No changes needed to:

  • OAuthLoginFlowService — auto-discovers all OAuthClaimsExtractor beans
  • IdentityBindingService — handles any providerCode generically
  • AuthMethodCatalog — dynamically lists all OAuth registrations
  • LoginButton (frontend) — dynamically renders all OAUTH_REDIRECT methods with provider logos

Configuration

Set these environment variables to enable DingTalk login:
OAUTH2_DINGTALK_CLIENT_ID=
OAUTH2_DINGTALK_CLIENT_SECRET=

Register your app at https://open-dev.dingtalk.com/ and request the Contact.User.Read scope.

Test plan

  • Verify DingTalk OAuth2 login flow works end-to-end
  • Verify existing GitHub/GitLab/OIDC login still works
  • Verify DingTalk login button appears on login page
  • Verify user provisioning via IdentityBindingService.bindOrCreate()
  • Verify access policy evaluation for DingTalk provider

@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

DingTalk (钉钉) uses a non-standard OAuth2 flow that requires:
- JSON body for token exchange (instead of form-urlencoded)
- Custom header (x-acs-dingtalk-access-token) for user info requests

This PR integrates DingTalk by leveraging the existing OAuthClaimsExtractor
strategy pattern, adding three provider-specific components:

- DingTalkClaimsExtractor: maps DingTalk user fields to normalized OAuthClaims
- DingTalkTokenResponseClient: handles DingTalk's JSON token exchange
- DingTalkOAuth2UserService: fetches user info via DingTalk's custom header

SecurityConfig uses delegating wrappers to route DingTalk requests to
these custom components while preserving standard behavior for all other
providers (GitHub, GitLab, OIDC).

No changes needed to OAuthLoginFlowService, IdentityBindingService,
AuthMethodCatalog, or frontend LoginButton — all are provider-agnostic.
…ess policy

DingTalkOAuth2UserService was directly calling IdentityBindingService.bindOrCreate(),
bypassing access policy evaluation. Refactored to delegate to
OAuthLoginFlowService.authenticate() for consistent policy + binding,
matching the pattern used by CustomOAuth2UserService.

Also aligned the returned DefaultOAuth2User structure (providerLogin
attribute key, authorities from platformRoles) with CustomOAuth2UserService
so OAuth2LoginSuccessHandler works uniformly across all providers.
@konglong87 konglong87 force-pushed the feature/dingtalk-oauth2 branch from a1dd34c to f7370c8 Compare May 28, 2026 07:04
…ring Data JPA proxy

Package-private JPA repository interfaces cannot be proxied by Spring Data's
JpaRepositoryFactory when using Spring Boot devtools or certain class loader
configurations, causing UnsatisfiedDependencyException at startup.
@konglong87
Copy link
Copy Markdown
Author

I have completed the CLA signing. Please re-run the CLA check, thanks.

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