Skip to content

feat: add dingtalk-auth plugin#13381

Merged
AlinsRan merged 5 commits into
masterfrom
feat/dingtalk-auth
May 25, 2026
Merged

feat: add dingtalk-auth plugin#13381
AlinsRan merged 5 commits into
masterfrom
feat/dingtalk-auth

Conversation

@AlinsRan

@AlinsRan AlinsRan commented May 17, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Add a new dingtalk-auth plugin that integrates DingTalk (钉钉) OAuth 2.0 authentication into APISIX routes. The plugin exchanges a DingTalk authorization code for a verified user identity and stores the result in an encrypted cookie session, so downstream requests only hit DingTalk APIs once per session.

How it works

Browser          APISIX             DingTalk API
  │                │                     │
  │── GET /api ───►│                     │
  │                │  no valid session   │
  │◄── 302 ────────│  redirect_uri       │
  │                │                     │
  │── GET /api?code=XXX ──────────────►│
  │                │                     │
  │                │── POST accessToken ►│
  │                │◄─ {accessToken} ────│
  │                │                     │
  │                │── POST userinfo ────►│
  │                │◄─ {result: {...}} ───│
  │                │                     │
  │                │  set encrypted session cookie
  │                │  set X-Userinfo header
  │                │── upstream request ─►
  │◄── response ───│
  1. Requests without a valid session cookie are redirected (302) to redirect_uri.
  2. When a DingTalk authorization code is found (query param code or header X-DingTalk-Code), it is exchanged for an access token via the DingTalk token API.
  3. The access token is cached with a 7000 s TTL (DingTalk's TTL is 7200 s) to avoid redundant requests.
  4. User information is fetched from the DingTalk user info API and stored in an encrypted lua-resty-session v4 cookie.
  5. Subsequent requests carrying the session cookie bypass all DingTalk API calls.
  6. When set_userinfo_header is true (default), the upstream receives user information in the X-Userinfo header as a Base64-encoded JSON object.
  7. DingTalk auth errors (invalid code) return 401; network or upstream errors return 503.

Attributes

Name Type Required Default Description
app_key string Yes DingTalk application key
app_secret string Yes DingTalk application secret (stored encrypted)
secret string Yes Session encryption secret, 8–32 chars (stored encrypted)
redirect_uri string Yes URI to redirect to when no auth code is present
code_query string No "code" Query parameter name for the authorization code
code_header string No "X-DingTalk-Code" HTTP header name for the authorization code
access_token_url string No DingTalk default DingTalk access token endpoint
userinfo_url string No DingTalk default DingTalk user info endpoint
timeout integer No 6000 HTTP request timeout in milliseconds
ssl_verify boolean No true Verify TLS certificates
set_userinfo_header boolean No true Forward user info to upstream in X-Userinfo header
cookie_expires_in integer No 86400 Session cookie TTL in seconds
secret_fallbacks array of string No Previous secrets for zero-downtime key rotation

Plugin priority: 2430 (between key-auth 2500 and consumer-restriction 2400).

Usage

1. Enable the plugin in config.yaml

plugins:
  - dingtalk-auth

2. Create a route

curl http://127.0.0.1:9180/apisix/admin/routes/1 \
  -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' \
  -X PUT -d '{
    "uri": "/api/*",
    "plugins": {
      "dingtalk-auth": {
        "app_key": "your-dingtalk-app-key",
        "app_secret": "your-dingtalk-app-secret",
        "secret": "your-session-secret",
        "redirect_uri": "https://login.dingtalk.com/oauth2/auth?response_type=code&client_id=your-app-key&redirect_uri=https://your-app.example.com/api/callback&scope=openid&prompt=consent"
      }
    },
    "upstream": {
      "type": "roundrobin",
      "nodes": {"httpbin.org:80": 1}
    }
  }'

3. Authentication flow

When a user visits your protected API without a session, they are redirected to DingTalk's login page. After authorizing, DingTalk calls back with a code query parameter. APISIX exchanges the code for user info, stores it in a session cookie, and forwards the request to upstream with the X-Userinfo header set:

X-Userinfo: eyJ1c2VySWQiOiAiMTIzNDU2IiwgIm5hbWUiOiAiSm9obiBEb2UifQ==

Decoded:

{"userId": "123456", "name": "John Doe"}

4. Key rotation (zero downtime)

To rotate the session encryption secret without invalidating existing sessions:

{
  "secret": "new-secret-value",
  "secret_fallbacks": ["old-secret-value"]
}

Existing sessions encrypted with old-secret-value continue to work until they expire.

Changes

  • New plugin: apisix/plugins/dingtalk-auth.lua
  • New tests: t/plugin/dingtalk-auth.t
  • New docs: docs/en/latest/plugins/dingtalk-auth.md
  • Updated: conf/config.yaml.example, docs/en/latest/config.json, t/admin/plugins.t, apisix/cli/config.lua

@dosubot dosubot Bot added size:XL This PR changes 500-999 lines, ignoring generated files. enhancement New feature or request plugin labels May 17, 2026
@AlinsRan
AlinsRan requested a review from Copilot May 18, 2026 01:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a new dingtalk-auth plugin that integrates DingTalk OAuth 2.0 authentication into APISIX routes. The plugin extracts an authorization code from query/header, exchanges it for an access token (with an LRU cache), retrieves user info from DingTalk, and persists the verified user in an encrypted lua-resty-session v4 cookie. It also optionally forwards user info to the upstream via an X-Userinfo header.

Changes:

  • New plugin implementation apisix/plugins/dingtalk-auth.lua with priority 2430 and encrypted app_secret/secret fields.
  • New test suite t/plugin/dingtalk-auth.t (13 cases) and registration in t/admin/plugins.t, apisix/cli/config.lua, and conf/config.yaml.example.
  • New English documentation docs/en/latest/plugins/dingtalk-auth.md and sidebar entry in docs/en/latest/config.json.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
apisix/plugins/dingtalk-auth.lua New plugin implementing DingTalk OAuth2 code-to-userinfo flow with session cookie.
apisix/cli/config.lua Registers dingtalk-auth in the default plugin list at priority 2430.
conf/config.yaml.example Adds dingtalk-auth to the example plugin list.
t/plugin/dingtalk-auth.t New test suite mocking DingTalk endpoints and exercising schema, redirect, code, session, and custom code-source paths.
t/admin/plugins.t Adds plugin to expected admin plugin list.
docs/en/latest/plugins/dingtalk-auth.md New English plugin documentation.
docs/en/latest/config.json Adds doc page to the English sidebar.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread apisix/plugins/dingtalk-auth.lua
Comment thread apisix/plugins/dingtalk-auth.lua Outdated
Comment thread apisix/plugins/dingtalk-auth.lua
Comment thread apisix/plugins/dingtalk-auth.lua Outdated
Comment thread apisix/plugins/dingtalk-auth.lua
Comment thread t/plugin/dingtalk-auth.t

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 8 comments.

Comment thread apisix/plugins/dingtalk-auth.lua Outdated
Comment thread apisix/plugins/dingtalk-auth.lua
Comment thread apisix/plugins/dingtalk-auth.lua Outdated
Comment thread apisix/plugins/dingtalk-auth.lua Outdated
Comment thread apisix/plugins/dingtalk-auth.lua Outdated
Comment thread apisix/plugins/dingtalk-auth.lua Outdated
Comment thread apisix/plugins/dingtalk-auth.lua Outdated
Comment thread t/plugin/dingtalk-auth.t
AlinsRan and others added 3 commits May 20, 2026 13:44
Add the dingtalk-auth plugin that integrates DingTalk OAuth 2.0
authentication into APISIX routes. The plugin:

- Validates a DingTalk authorization code from a configurable query
  parameter (default: "code") or request header (default:
  "X-DingTalk-Code")
- Exchanges the code for an access token via the DingTalk token API
  and caches it with a 7000-second TTL to avoid repeated token fetches
- Retrieves DingTalk user information and stores it in an encrypted
  cookie session (lua-resty-session v4)
- Forwards user information to upstream in the X-Userinfo header
  (Base64-encoded JSON) when set_userinfo_header is true
- Supports secret_fallbacks for zero-downtime session key rotation

Priority: 2430 (between key-auth 2500 and consumer-restriction 2400)

Co-authored-by: Copilot <[email protected]>
…nfo encoding error

- Check return value of sess:save() and return 500 if it fails, rather
  than silently proceeding with an unsaved session
- Handle encoding error for the X-Userinfo header gracefully instead of
  passing nil to base64_encode

Co-authored-by: Copilot <[email protected]>
Add dingtalk-auth to apisix/cli/config.lua so the plugin is loaded
by the APISIX runtime and recognized by the Admin API.

Co-authored-by: Copilot <[email protected]>
@AlinsRan
AlinsRan force-pushed the feat/dingtalk-auth branch from 6d03b36 to 0adcd44 Compare May 20, 2026 05:46
- Add minimum=1 to timeout and cookie_expires_in to reject non-positive
  values that would cause undefined behavior
- Add minLength=1 to optional string fields (code_header, code_query,
  redirect_uri, userinfo_url, access_token_url) to reject empty strings
- Use direct truthiness check for set_userinfo_header (schema always
  provides a boolean default)
- Distinguish auth failures (errcode != 0 → 401) from transient upstream
  errors (network/decode/non-200 → 503) in fetch_userinfo
- Improve access token failure message: 'Failed to obtain access token'
  instead of the misleading 'Invalid configuration'
- Redirect to redirect_uri instead of returning 500 when session cookie
  contains corrupted JSON; this matches the no-session behavior
- Remove ctx.external_user assignment (no standard consumer in OSS APISIX)
- Add error message assertions to schema tests 2 and 3
- Add note in schema comment about secret_fallbacks encrypt_fields
  limitation (array traversal not supported)

Co-authored-by: Copilot <[email protected]>
@AlinsRan
AlinsRan force-pushed the feat/dingtalk-auth branch from 0adcd44 to 908641e Compare May 20, 2026 08:39
encrypt_fields now supports array traversal (added in plugin.lua), so
secret_fallbacks can be encrypted at rest. Add it to encrypt_fields and
remove the outdated comment.

Co-authored-by: Copilot <[email protected]>
@AlinsRan
AlinsRan merged commit cd32b79 into master May 25, 2026
33 checks passed
@AlinsRan
AlinsRan deleted the feat/dingtalk-auth branch May 25, 2026 03:26
wistefan pushed a commit to wistefan/apisix that referenced this pull request Jun 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request plugin size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants