Skip to content

feat: add dingtalk-auth plugin#13381

Open
AlinsRan wants to merge 4 commits into
masterfrom
feat/dingtalk-auth
Open

feat: add dingtalk-auth plugin#13381
AlinsRan wants to merge 4 commits into
masterfrom
feat/dingtalk-auth

Conversation

@AlinsRan
Copy link
Copy Markdown
Contributor

@AlinsRan AlinsRan commented May 17, 2026

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
Copy link
Copy Markdown

Copilot AI left a comment

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
Copy link
Copy Markdown

Copilot AI left a comment

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
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 <223556219+Copilot@users.noreply.github.com>
…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 <223556219+Copilot@users.noreply.github.com>
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 <223556219+Copilot@users.noreply.github.com>
@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 <223556219+Copilot@users.noreply.github.com>
@AlinsRan AlinsRan force-pushed the feat/dingtalk-auth branch from 0adcd44 to 908641e Compare May 20, 2026 08:39
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.

2 participants