This is an automated email from the ASF dual-hosted git repository.
shreemaanabhishek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push:
new 5083ad605 feat: auth plugins respond with `www-authenticate` header
with realm (#12864)
5083ad605 is described below
commit 5083ad605149d2e7364a19be4758828f4b5b33b4
Author: Shreemaan Abhishek <[email protected]>
AuthorDate: Thu Jan 8 13:44:28 2026 +0545
feat: auth plugins respond with `www-authenticate` header with realm
(#12864)
---
apisix/plugins/basic-auth.lua | 8 +-
apisix/plugins/hmac-auth.lua | 4 +
apisix/plugins/jwt-auth.lua | 3 +
apisix/plugins/key-auth.lua | 3 +
apisix/plugins/ldap-auth.lua | 11 ++-
apisix/schema_def.lua | 15 +++
docs/en/latest/plugins/basic-auth.md | 1 +
docs/en/latest/plugins/hmac-auth.md | 3 +-
docs/en/latest/plugins/jwt-auth.md | 1 +
docs/en/latest/plugins/key-auth.md | 1 +
docs/en/latest/plugins/ldap-auth.md | 1 +
t/plugin/basic-auth-realm.t | 159 +++++++++++++++++++++++++++++++
t/plugin/hmac-auth-realm.t | 159 +++++++++++++++++++++++++++++++
t/plugin/jwt-auth-realm.t | 159 +++++++++++++++++++++++++++++++
t/plugin/key-auth-realm.t | 159 +++++++++++++++++++++++++++++++
t/plugin/ldap-auth-realm.t | 177 +++++++++++++++++++++++++++++++++++
16 files changed, 858 insertions(+), 6 deletions(-)
diff --git a/apisix/plugins/basic-auth.lua b/apisix/plugins/basic-auth.lua
index 752ea8133..a6a90d98c 100644
--- a/apisix/plugins/basic-auth.lua
+++ b/apisix/plugins/basic-auth.lua
@@ -32,7 +32,8 @@ local schema = {
hide_credentials = {
type = "boolean",
default = false,
- }
+ },
+ realm = schema_def.get_realm_schema("basic"),
},
anonymous_consumer = schema_def.anonymous_consumer_schema,
}
@@ -124,7 +125,6 @@ end
local function find_consumer(ctx)
local auth_header = core.request.header(ctx, "Authorization")
if not auth_header then
- core.response.set_header("WWW-Authenticate", "Basic realm='.'")
return nil, nil, "Missing authorization in request"
end
@@ -157,15 +157,17 @@ end
function _M.rewrite(conf, ctx)
- local cur_consumer, consumer_conf, err = find_consumer(ctx)
+ local cur_consumer, consumer_conf, err = find_consumer(ctx, conf)
if not cur_consumer then
if not conf.anonymous_consumer then
+ core.response.set_header("WWW-Authenticate", "Basic realm=\"" ..
conf.realm .. "\"")
return 401, { message = err }
end
cur_consumer, consumer_conf, err =
consumer.get_anonymous_consumer(conf.anonymous_consumer)
if not cur_consumer then
err = "basic-auth failed to authenticate the request, code: 401.
error: " .. err
core.log.error(err)
+ core.response.set_header("WWW-Authenticate", "Basic realm=\"" ..
conf.realm .. "\"")
return 401, { message = "Invalid user authorization" }
end
end
diff --git a/apisix/plugins/hmac-auth.lua b/apisix/plugins/hmac-auth.lua
index 60eca605e..97dda11fe 100644
--- a/apisix/plugins/hmac-auth.lua
+++ b/apisix/plugins/hmac-auth.lua
@@ -63,6 +63,7 @@ local schema = {
default = false,
},
hide_credentials = {type = "boolean", default = false},
+ realm = schema_def.get_realm_schema("hmac"),
anonymous_consumer = schema_def.anonymous_consumer_schema,
},
}
@@ -346,14 +347,17 @@ function _M.rewrite(conf, ctx)
local cur_consumer, consumers_conf, err = find_consumer(conf, ctx)
if not cur_consumer then
if not conf.anonymous_consumer then
+ core.response.set_header("WWW-Authenticate", "hmac realm=\"" ..
conf.realm .. "\"")
return 401, { message = err }
end
cur_consumer, consumers_conf, err =
consumer.get_anonymous_consumer(conf.anonymous_consumer)
if not cur_consumer then
if auth_utils.is_running_under_multi_auth(ctx) then
+ core.response.set_header("WWW-Authenticate", "hmac realm=\""
.. conf.realm .. "\"")
return 401, err
end
core.log.error(err)
+ core.response.set_header("WWW-Authenticate", "hmac realm=\"" ..
conf.realm .. "\"")
return 401, { message = "Invalid user authorization" }
end
end
diff --git a/apisix/plugins/jwt-auth.lua b/apisix/plugins/jwt-auth.lua
index 197efc5fa..4c32609bf 100644
--- a/apisix/plugins/jwt-auth.lua
+++ b/apisix/plugins/jwt-auth.lua
@@ -58,6 +58,7 @@ local schema = {
type = "boolean",
default = false
},
+ realm = schema_def.get_realm_schema("jwt"),
anonymous_consumer = schema_def.anonymous_consumer_schema,
},
}
@@ -307,12 +308,14 @@ function _M.rewrite(conf, ctx)
local consumer, consumer_conf, err = find_consumer(conf, ctx)
if not consumer then
if not conf.anonymous_consumer then
+ core.response.set_header("WWW-Authenticate", "Bearer realm=\"" ..
conf.realm .. "\"")
return 401, { message = err }
end
consumer, consumer_conf, err =
consumer_mod.get_anonymous_consumer(conf.anonymous_consumer)
if not consumer then
err = "jwt-auth failed to authenticate the request, code: 401.
error: " .. err
core.log.error(err)
+ core.response.set_header("WWW-Authenticate", "Bearer realm=\"" ..
conf.realm .. "\"")
return 401, { message = "Invalid user authorization"}
end
end
diff --git a/apisix/plugins/key-auth.lua b/apisix/plugins/key-auth.lua
index 7bef9f83c..020326dce 100644
--- a/apisix/plugins/key-auth.lua
+++ b/apisix/plugins/key-auth.lua
@@ -30,6 +30,7 @@ local schema = {
type = "string",
default = "apikey",
},
+ realm = schema_def.get_realm_schema("key"),
hide_credentials = {
type = "boolean",
default = false,
@@ -104,12 +105,14 @@ function _M.rewrite(conf, ctx)
local consumer, consumer_conf, err = find_consumer(ctx, conf)
if not consumer then
if not conf.anonymous_consumer then
+ core.response.set_header("WWW-Authenticate", "apikey realm=\"" ..
conf.realm .. "\"")
return 401, { message = err}
end
consumer, consumer_conf, err =
consumer_mod.get_anonymous_consumer(conf.anonymous_consumer)
if not consumer then
err = "key-auth failed to authenticate the request, code: 401.
error: " .. err
core.log.error(err)
+ core.response.set_header("WWW-Authenticate", "apikey realm=\"" ..
conf.realm .. "\"")
return 401, { message = "Invalid user authorization"}
end
end
diff --git a/apisix/plugins/ldap-auth.lua b/apisix/plugins/ldap-auth.lua
index 1d89efef8..30c836742 100644
--- a/apisix/plugins/ldap-auth.lua
+++ b/apisix/plugins/ldap-auth.lua
@@ -19,6 +19,8 @@ local ngx = ngx
local ngx_re = require("ngx.re")
local consumer_mod = require("apisix.consumer")
local ldap = require("resty.ldap")
+local schema_def = require("apisix.schema_def")
+
local schema = {
type = "object",
@@ -28,7 +30,8 @@ local schema = {
ldap_uri = { type = "string" },
use_tls = { type = "boolean", default = false },
tls_verify = { type = "boolean", default = false },
- uid = { type = "string", default = "cn" }
+ uid = { type = "string", default = "cn" },
+ realm = schema_def.get_realm_schema("ldap"),
},
required = {"base_dn","ldap_uri"},
}
@@ -106,7 +109,7 @@ function _M.rewrite(conf, ctx)
-- 1. extract authorization from header
local auth_header = core.request.header(ctx, "Authorization")
if not auth_header then
- core.response.set_header("WWW-Authenticate", "Basic realm='.'")
+ core.response.set_header("WWW-Authenticate", "Basic realm=\"" ..
conf.realm .. "\"")
return 401, { message = "Missing authorization in request" }
end
@@ -117,6 +120,7 @@ function _M.rewrite(conf, ctx)
else
core.log.warn("nil user")
end
+ core.response.set_header("WWW-Authenticate", "Basic realm=\"" ..
conf.realm .. "\"")
return 401, { message = "Invalid authorization in request" }
end
@@ -136,6 +140,7 @@ function _M.rewrite(conf, ctx)
local res, err = ldap.ldap_authenticate(user.username, user.password,
ldapconf)
if not res then
core.log.warn("ldap-auth failed: ", err)
+ core.response.set_header("WWW-Authenticate", "Basic realm=\"" ..
conf.realm .. "\"")
return 401, { message = "Invalid user authorization" }
end
@@ -144,12 +149,14 @@ function _M.rewrite(conf, ctx)
-- 3. Retrieve consumer for authorization plugin
local consumer_conf = consumer_mod.plugin(plugin_name)
if not consumer_conf then
+ core.response.set_header("WWW-Authenticate", "Basic realm=\"" ..
conf.realm .. "\"")
return 401, { message = "Missing related consumer" }
end
local consumers = consumer_mod.consumers_kv(plugin_name, consumer_conf,
"user_dn")
local consumer = consumers[user_dn]
if not consumer then
+ core.response.set_header("WWW-Authenticate", "Basic realm=\"" ..
conf.realm .. "\"")
return 401, {message = "Invalid user authorization"}
end
consumer_mod.attach_consumer(ctx, consumer, consumer_conf)
diff --git a/apisix/schema_def.lua b/apisix/schema_def.lua
index 53b5476db..491265a58 100644
--- a/apisix/schema_def.lua
+++ b/apisix/schema_def.lua
@@ -29,6 +29,21 @@ _M.anonymous_consumer_schema = {
minLength = "1"
}
+function _M.get_realm_schema(default_val)
+ return {
+ type = "string",
+ -- Pattern: Only allow printable ASCII, but EXCLUDE " and \
+ -- \x20-\x21 (Space and !)
+ -- \x23-\x5B (# through [)
+ -- \x5D-\x7E (] through ~)
+ -- Escaped closing bracket (\x5D) assertion for PCRE compatibility
+ pattern = "^[\x20-\x21\x23-\x5B\\]-\x7E]+$",
+ default = default_val,
+ minLength = 1,
+ maxLength = 128,
+ }
+end
+
local id_schema = {
anyOf = {
{
diff --git a/docs/en/latest/plugins/basic-auth.md
b/docs/en/latest/plugins/basic-auth.md
index 37e38593c..a82424c3f 100644
--- a/docs/en/latest/plugins/basic-auth.md
+++ b/docs/en/latest/plugins/basic-auth.md
@@ -55,6 +55,7 @@ For Route:
|------------------|---------|----------|---------|------------------------------------------------------------------------|
| hide_credentials | boolean | False | false | If true, do not pass the
authorization request header to Upstream services. |
| anonymous_consumer | boolean | False | false | Anonymous Consumer name.
If configured, allow anonymous users to bypass the authentication. |
+| realm | string | False | basic | The realm to include in the
`WWW-Authenticate` header when authentication fails. |
## Examples
diff --git a/docs/en/latest/plugins/hmac-auth.md
b/docs/en/latest/plugins/hmac-auth.md
index 354af2a15..24f741f25 100644
--- a/docs/en/latest/plugins/hmac-auth.md
+++ b/docs/en/latest/plugins/hmac-auth.md
@@ -54,9 +54,10 @@ The following attributes are available for configurations on
Routes or Services.
| allowed_algorithms | array[string] | False |
["hmac-sha1","hmac-sha256","hmac-sha512"] | combination of
"hmac-sha1","hmac-sha256",and "hmac-sha512" | The list of HMAC algorithms
allowed.
|
| clock_skew | integer | False | 300 |
>=1 | Maximum allowable time difference in
seconds between the client request's timestamp and APISIX server's current
time. This helps account for discrepancies in time synchronization between the
client’s and server’s clocks and protect against replay attacks. The timestamp
in the Date header (must be in GMT format) will be used for the calculation.
|
| signed_headers | array[string] | False | |
| The list of HMAC-signed headers that should
be included in the client request's HMAC signature. |
-| validate_request_body | boolean | False | false |
| If true, validate the integrity of the request body to
ensure it has not been tampered with during transmission. Specifically, the
Plugin creates a SHA-256 base64-encoded digest and compare it to the `Digest`
header. If the Digest` header is missing or if the digests do not match, the
validation fails. |
+| validate_request_body | boolean | False | false |
| If true, validate the integrity of the request body to
ensure it has not been tampered with during transmission. Specifically, the
Plugin creates a SHA-256 base64-encoded digest and compare it to the `Digest`
header. If the `Digest` header is missing or if the digests do not match, the
validation fails. |
| hide_credentials | boolean | False | false |
| If true, do not pass the authorization request header to
Upstream services. |
| anonymous_consumer | string | False | |
| Anonymous Consumer name. If configured, allow anonymous users to
bypass the authentication. |
+| realm | string | False | hmac |
| The realm to include in the `WWW-Authenticate` header when
authentication fails.
|
NOTE: `encrypt_fields = {"secret_key"}` is also defined in the schema, which
means that the field will be stored encrypted in etcd. See [encrypted storage
fields](../plugin-develop.md#encrypted-storage-fields).
diff --git a/docs/en/latest/plugins/jwt-auth.md
b/docs/en/latest/plugins/jwt-auth.md
index a22b6fbb8..a85d84429 100644
--- a/docs/en/latest/plugins/jwt-auth.md
+++ b/docs/en/latest/plugins/jwt-auth.md
@@ -68,6 +68,7 @@ For Routes or Services:
| key_claim_name | string | False | key | The name of the JWT claim
that contains the user key (corresponds to Consumer's key attribute). |
| anonymous_consumer | string | False | false | Anonymous Consumer name. If
configured, allow anonymous users to bypass the authentication. |
| store_in_ctx | boolean | False | false | Set to true will store the
JWT payload in the request context (`ctx.jwt_auth_payload`). This allows
lower-priority plugins that run afterwards on the same request to retrieve and
use the JWT token. |
+| realm | string | False | jwt | The realm to include in
the `WWW-Authenticate` header when authentication fails. |
You can implement `jwt-auth` with [HashiCorp
Vault](https://www.vaultproject.io/) to store and fetch secrets and RSA keys
pairs from its [encrypted KV
engine](https://developer.hashicorp.com/vault/docs/secrets/kv) using the
[APISIX Secret](../terminology/secret.md) resource.
diff --git a/docs/en/latest/plugins/key-auth.md
b/docs/en/latest/plugins/key-auth.md
index 655eb3e31..8a7d7aa1a 100644
--- a/docs/en/latest/plugins/key-auth.md
+++ b/docs/en/latest/plugins/key-auth.md
@@ -58,6 +58,7 @@ For Route:
| query | string | False | apikey | The query string to get the key from.
Lower priority than header.
|
| hide_credentials | boolean | False | false | If true, do not pass the
header or query string with key to Upstream services. |
| anonymous_consumer | string | False | false | Anonymous Consumer name.
If configured, allow anonymous users to bypass the authentication. |
+| realm | string | False | key | The realm to include in
the `WWW-Authenticate` header when authentication fails. |
## Examples
diff --git a/docs/en/latest/plugins/ldap-auth.md
b/docs/en/latest/plugins/ldap-auth.md
index 7e33fc977..95b6884bd 100644
--- a/docs/en/latest/plugins/ldap-auth.md
+++ b/docs/en/latest/plugins/ldap-auth.md
@@ -53,6 +53,7 @@ For Route:
| use_tls | boolean | False | `false` | If set to `true` uses TLS.
|
| tls_verify| boolean | False | `false` | Whether to verify the
server certificate when `use_tls` is enabled; If set to `true`, you must set
`ssl_trusted_certificate` in `config.yaml`, and make sure the host of
`ldap_uri` matches the host in server certificate. |
| uid | string | False | `cn` | uid attribute.
|
+| realm | string | False | ldap | The realm to include in the
`WWW-Authenticate` header when authentication fails. |
## Enable plugin
diff --git a/t/plugin/basic-auth-realm.t b/t/plugin/basic-auth-realm.t
new file mode 100644
index 000000000..ff9ad3968
--- /dev/null
+++ b/t/plugin/basic-auth-realm.t
@@ -0,0 +1,159 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+use t::APISIX 'no_plan';
+
+repeat_each(1);
+no_long_string();
+no_root_location();
+
+run_tests;
+
+__DATA__
+
+=== TEST 1: sanity, default realm
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "plugins": {
+ "basic-auth": {}
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 2: verify default realm
+--- request
+GET /hello
+--- error_code: 401
+--- response_headers
+WWW-Authenticate: Basic realm="basic"
+
+
+
+=== TEST 3: set custom realm
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "plugins": {
+ "basic-auth": {
+ "realm": "secure-zone"
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 4: verify custom realm
+--- request
+GET /hello
+--- error_code: 401
+--- response_headers
+WWW-Authenticate: Basic realm="secure-zone"
+
+
+
+=== TEST 5: set anonymous consumer
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "plugins": {
+ "basic-auth": {
+ "anonymous_consumer": "missing-consumer"
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 6: verify anonymous consumer missing returns realm
+--- request
+GET /hello
+--- error_code: 401
+--- response_headers
+WWW-Authenticate: Basic realm="basic"
+--- error_log
+failed to get anonymous consumer
diff --git a/t/plugin/hmac-auth-realm.t b/t/plugin/hmac-auth-realm.t
new file mode 100644
index 000000000..34dc822d7
--- /dev/null
+++ b/t/plugin/hmac-auth-realm.t
@@ -0,0 +1,159 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+use t::APISIX 'no_plan';
+
+repeat_each(1);
+no_long_string();
+no_root_location();
+
+run_tests;
+
+__DATA__
+
+=== TEST 1: sanity, default realm
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "plugins": {
+ "hmac-auth": {}
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 2: verify default realm
+--- request
+GET /hello
+--- error_code: 401
+--- response_headers
+WWW-Authenticate: hmac realm="hmac"
+
+
+
+=== TEST 3: set custom realm
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "plugins": {
+ "hmac-auth": {
+ "realm": "my-hmac-realm"
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 4: verify custom realm
+--- request
+GET /hello
+--- error_code: 401
+--- response_headers
+WWW-Authenticate: hmac realm="my-hmac-realm"
+
+
+
+=== TEST 5: set anonymous consumer
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "plugins": {
+ "hmac-auth": {
+ "anonymous_consumer": "missing-consumer"
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 6: verify anonymous consumer missing returns realm
+--- request
+GET /hello
+--- error_code: 401
+--- response_headers
+WWW-Authenticate: hmac realm="hmac"
+--- error_log
+failed to get anonymous consumer
diff --git a/t/plugin/jwt-auth-realm.t b/t/plugin/jwt-auth-realm.t
new file mode 100644
index 000000000..1bb4c921a
--- /dev/null
+++ b/t/plugin/jwt-auth-realm.t
@@ -0,0 +1,159 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+use t::APISIX 'no_plan';
+
+repeat_each(1);
+no_long_string();
+no_root_location();
+
+run_tests;
+
+__DATA__
+
+=== TEST 1: sanity, default realm
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "plugins": {
+ "jwt-auth": {}
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 2: verify default realm
+--- request
+GET /hello
+--- error_code: 401
+--- response_headers
+WWW-Authenticate: Bearer realm="jwt"
+
+
+
+=== TEST 3: set custom realm
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "plugins": {
+ "jwt-auth": {
+ "realm": "my-jwt-realm"
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 4: verify custom realm
+--- request
+GET /hello
+--- error_code: 401
+--- response_headers
+WWW-Authenticate: Bearer realm="my-jwt-realm"
+
+
+
+=== TEST 5: set anonymous consumer
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "plugins": {
+ "jwt-auth": {
+ "anonymous_consumer": "missing-consumer"
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 6: verify anonymous consumer missing returns realm
+--- request
+GET /hello
+--- error_code: 401
+--- response_headers
+WWW-Authenticate: Bearer realm="jwt"
+--- error_log
+failed to get anonymous consumer
diff --git a/t/plugin/key-auth-realm.t b/t/plugin/key-auth-realm.t
new file mode 100644
index 000000000..ce3b83d8b
--- /dev/null
+++ b/t/plugin/key-auth-realm.t
@@ -0,0 +1,159 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+use t::APISIX 'no_plan';
+
+repeat_each(1);
+no_long_string();
+no_root_location();
+
+run_tests;
+
+__DATA__
+
+=== TEST 1: sanity, default realm
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "plugins": {
+ "key-auth": {}
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 2: verify default realm
+--- request
+GET /hello
+--- error_code: 401
+--- response_headers
+WWW-Authenticate: apikey realm="key"
+
+
+
+=== TEST 3: set custom realm
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "plugins": {
+ "key-auth": {
+ "realm": "my-custom-realm"
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 4: verify custom realm
+--- request
+GET /hello
+--- error_code: 401
+--- response_headers
+WWW-Authenticate: apikey realm="my-custom-realm"
+
+
+
+=== TEST 5: set anonymous consumer
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "plugins": {
+ "key-auth": {
+ "anonymous_consumer": "missing-consumer"
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 6: verify anonymous consumer missing returns realm
+--- request
+GET /hello
+--- error_code: 401
+--- response_headers
+WWW-Authenticate: apikey realm="key"
+--- error_log
+failed to get anonymous consumer
diff --git a/t/plugin/ldap-auth-realm.t b/t/plugin/ldap-auth-realm.t
new file mode 100644
index 000000000..63dd698b2
--- /dev/null
+++ b/t/plugin/ldap-auth-realm.t
@@ -0,0 +1,177 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+use t::APISIX 'no_plan';
+
+repeat_each(1);
+no_long_string();
+no_root_location();
+
+run_tests;
+
+__DATA__
+
+=== TEST 1: sanity, default realm
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "plugins": {
+ "ldap-auth": {
+ "base_dn": "dc=example,dc=com",
+ "ldap_uri": "ldap://127.0.0.1:1389"
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 2: verify default realm
+--- request
+GET /hello
+--- error_code: 401
+--- response_headers
+WWW-Authenticate: Basic realm="ldap"
+
+
+
+=== TEST 3: set custom realm
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "plugins": {
+ "ldap-auth": {
+ "base_dn": "dc=example,dc=com",
+ "ldap_uri": "ldap://127.0.0.1:1389",
+ "realm": "my-ldap-realm"
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 4: verify custom realm
+--- request
+GET /hello
+--- error_code: 401
+--- response_headers
+WWW-Authenticate: Basic realm="my-ldap-realm"
+
+
+
+=== TEST 5: ldap auth failure (missing header) returns realm
+--- request
+GET /hello
+--- error_code: 401
+--- response_headers
+WWW-Authenticate: Basic realm="my-ldap-realm"
+
+
+
+=== TEST 6: ldap auth failure (invalid credentials) returns realm
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "plugins": {
+ "ldap-auth": {
+ "base_dn": "dc=example,dc=com",
+ "ldap_uri": "ldap://127.0.0.1:1389",
+ "realm": "my-ldap-realm"
+ }
+ },
+ "upstream": {
+ "nodes": {
+ "127.0.0.1:1980": 1
+ },
+ "type": "roundrobin"
+ },
+ "uri": "/hello"
+ }]]
+ )
+
+ if code >= 300 then
+ ngx.status = code
+ end
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 7: verify ldap invalid credentials returns realm
+--- request
+GET /hello
+--- more_headers
+Authorization: Basic dXNlcjp3cm9uZw==
+--- error_code: 401
+--- response_headers
+WWW-Authenticate: Basic realm="my-ldap-realm"
+--- error_log
+ldap-auth failed