Skip to main content
Version: Next

dingtalk-auth

Description#

The dingtalk-auth Plugin integrates DingTalk OAuth 2.0 authentication into APISIX routes. It validates a DingTalk authorization code, exchanges it for an access token, and retrieves user information from the DingTalk open platform. Verified user information is cached in a secure cookie session so that subsequent requests are not interrupted.

Attributes#

NameTypeRequiredDefaultDescription
app_keystringTrueDingTalk application App Key (client ID).
app_secretstringTrueDingTalk application App Secret (client secret). This field is stored encrypted.
secretstringTrueKey used to sign and encrypt the cookie session (8โ€“32 characters). This field is stored encrypted.
redirect_uristringTrueURI to redirect the user to when no valid authorization code or session is present.
code_headerstringFalseX-DingTalk-CodeHTTP request header name from which to read the DingTalk authorization code.
code_querystringFalsecodeQuery parameter name from which to read the DingTalk authorization code.
access_token_urlstringFalsehttps://api.dingtalk.com/v1.0/oauth2/accessTokenDingTalk endpoint used to obtain an access token.
userinfo_urlstringFalsehttps://oapi.dingtalk.com/topapi/v2/user/getuserinfoDingTalk endpoint used to retrieve user information.
set_userinfo_headerbooleanFalsetrueWhen true, the verified user information is Base64-encoded and forwarded to the upstream in the X-Userinfo header.
timeoutintegerFalse6000Timeout in milliseconds for HTTP calls to DingTalk APIs.
ssl_verifybooleanFalsetrueWhether to verify the SSL certificate when calling DingTalk APIs.
cookie_expires_inintegerFalse86400Cookie session validity period in seconds.
secret_fallbacksarrayFalseList of fallback secrets used during key rotation (each 8โ€“32 characters).
note

encrypt_fields = {"app_secret", "secret"} is defined in the schema, which means both fields are stored encrypted in etcd. See encrypted storage fields.

Authentication flow#

Client                     APISIX (dingtalk-auth)            DingTalk
โ”‚ โ”‚ โ”‚
โ”‚โ”€โ”€โ”€โ”€ GET /resource โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บโ”‚ โ”‚
โ”‚ โ”‚ (no session, no code) โ”‚
โ”‚โ—„โ”€โ”€โ”€ 302 โ†’ redirect_uri โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚ โ”‚
โ”‚ โ”‚ โ”‚
โ”‚โ”€โ”€โ”€โ”€ GET /resource?code=xxx โ”€โ”€โ–บโ”‚ โ”‚
โ”‚ โ”‚โ”€โ”€โ”€โ”€ POST /accessToken โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บโ”‚
โ”‚ โ”‚โ—„โ”€โ”€โ”€ {"accessToken": "..."} โ”€โ”€โ”€โ”‚
โ”‚ โ”‚โ”€โ”€โ”€โ”€ POST /getuserinfo โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บโ”‚
โ”‚ โ”‚โ—„โ”€โ”€โ”€ {"result": {...}} โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚
โ”‚ โ”‚ (save userinfo in session) โ”‚
โ”‚โ—„โ”€โ”€โ”€ 200 + Set-Cookie โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚ โ”‚
โ”‚ โ”‚ โ”‚
โ”‚โ”€โ”€โ”€โ”€ GET /resource (Cookie) โ”€โ”€โ–บโ”‚ โ”‚
โ”‚ โ”‚ (session valid, skip auth) โ”‚
โ”‚โ—„โ”€โ”€โ”€ 200 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚ โ”‚

Enable Plugin#

You can enable the Plugin on a specific Route as shown below:

note

You can fetch the admin_key from config.yaml and save to an environment variable with the following command:

admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"//g')
curl http://127.0.0.1:9180/apisix/admin/routes/1 \
-H "X-API-KEY: $admin_key" \
-X PUT \
-d '{
"methods": ["GET"],
"uri": "/anything/*",
"plugins": {
"dingtalk-auth": {
"app_key": "<your-app-key>",
"app_secret": "<your-app-secret>",
"secret": "<session-secret-key>",
"redirect_uri": "https://login.dingtalk.com/oauth2/auth?..."
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'

Example usage#

Once you have enabled the Plugin, incoming requests to the Route are processed as follows:

  1. No session and no code: The Plugin generates a random state, stores it in the session cookie, and redirects the user to redirect_uri (typically a DingTalk OAuth login page) with a 302 response, appending state to the query string. Pass state through to DingTalk so that it is returned on the callback.
  2. Authorization code present (in the code query parameter or X-DingTalk-Code header): A code taken from the query parameter must arrive with the state bound to the session, otherwise the Plugin responds with 401. This ties the code to the browser that started the flow. A code taken from the X-DingTalk-Code header is exempt, since such requests come from non-browser clients. The Plugin then exchanges the code for an access token via access_token_url, then retrieves user information from userinfo_url. On success, the user information is stored in an encrypted cookie session and the original request proceeds.
  3. Valid session cookie: Subsequent requests carrying the session cookie bypass DingTalk API calls entirely and proceed directly to the upstream.

When set_userinfo_header is true (the default), the upstream receives the DingTalk user information in the X-Userinfo header as a Base64-encoded JSON object.

Custom code extraction#

By default the Plugin reads the authorization code from the code query parameter or the X-DingTalk-Code header. You can customize both names:

curl http://127.0.0.1:9180/apisix/admin/routes/1 \
-H "X-API-KEY: $admin_key" \
-X PUT \
-d '{
"methods": ["GET"],
"uri": "/anything/*",
"plugins": {
"dingtalk-auth": {
"app_key": "<your-app-key>",
"app_secret": "<your-app-secret>",
"secret": "<session-secret-key>",
"redirect_uri": "https://login.dingtalk.com/oauth2/auth?...",
"code_query": "dt_code",
"code_header": "X-Custom-DT-Code"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'

Key rotation#

Use secret_fallbacks to rotate the session signing key without invalidating existing sessions:

curl http://127.0.0.1:9180/apisix/admin/routes/1 \
-H "X-API-KEY: $admin_key" \
-X PUT \
-d '{
"methods": ["GET"],
"uri": "/anything/*",
"plugins": {
"dingtalk-auth": {
"app_key": "<your-app-key>",
"app_secret": "<your-app-secret>",
"secret": "<new-secret-key>",
"secret_fallbacks": ["<old-secret-key>"],
"redirect_uri": "https://login.dingtalk.com/oauth2/auth?..."
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'

Delete Plugin#

To remove the dingtalk-auth Plugin, delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect.

curl http://127.0.0.1:9180/apisix/admin/routes/1 \
-H "X-API-KEY: $admin_key" \
-X PUT \
-d '{
"methods": ["GET"],
"uri": "/anything/*",
"plugins": {},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'