diff --git a/apisix/plugins/openid-connect.lua b/apisix/plugins/openid-connect.lua index 6c94ea5d6d56..23d9fa5b9a1d 100644 --- a/apisix/plugins/openid-connect.lua +++ b/apisix/plugins/openid-connect.lua @@ -311,6 +311,7 @@ local schema = { pattern = "^[^:]+$" } }, + auth_accept_token_as_header_name = { type = "string", default = "Authorization" }, required_scopes = { description = "List of scopes that are required to be granted to the access token", type = "array", @@ -361,19 +362,12 @@ function _M.check_schema(conf) end -local function get_bearer_access_token(ctx) +local function get_bearer_access_token(ctx, conf) -- Get Authorization header, maybe. - local auth_header = core.request.header(ctx, "Authorization") + local auth_header = core.request.header(ctx, conf.auth_accept_token_as_header_name) if not auth_header then - -- No Authorization header, get X-Access-Token header, maybe. - local access_token_header = core.request.header(ctx, "X-Access-Token") - if not access_token_header then - -- No X-Access-Token header neither. - return false, nil, nil - end - - -- Return extracted header value. - return true, access_token_header, nil + core.log.error("Authorization header not found.") + return false, nil, nil end -- Check format of Authorization header. @@ -398,7 +392,7 @@ end local function introspect(ctx, conf) -- Extract token, maybe. - local has_token, token, err = get_bearer_access_token(ctx) + local has_token, token, err = get_bearer_access_token(ctx, conf) if err then return ngx.HTTP_BAD_REQUEST, err, nil, nil diff --git a/docs/en/latest/plugins/openid-connect.md b/docs/en/latest/plugins/openid-connect.md index 9cb806ce092d..3143b3d9c66b 100644 --- a/docs/en/latest/plugins/openid-connect.md +++ b/docs/en/latest/plugins/openid-connect.md @@ -95,6 +95,7 @@ description: OpenID Connect allows the client to obtain user information from th | introspection_interval | integer | False | 0 | | TTL of the cached and introspected access token in seconds. | | introspection_expiry_claim | string | False | | | Name of the expiry claim, which controls the TTL of the cached and introspected access token. The default value is 0, which means this option is not used and the plugin defaults to use the TTL passed by expiry claim defined in `introspection_expiry_claim`. If `introspection_interval` is larger than 0 and less than the TTL passed by expiry claim defined in `introspection_expiry_claim`, use `introspection_interval`. | | introspection_addon_headers | string[] | False | | | Array of strings. Used to append additional header values to the introspection HTTP request. If the specified header does not exist in origin request, value will not be appended. | +| auth_accept_token_as_header_name | string | False | "Authorization" | | Name of the request header from which to accept the access token. Defaults to `Authorization`. | | claim_validator.issuer.valid_issuers | string[] | False | | | Whitelist the vetted issuers of the jwt. When not passed by the user, the issuer returned by discovery endpoint will be used. In case both are missing, the issuer will not be validated. | NOTE: `encrypt_fields = {"client_secret"}` 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/t/plugin/openid-connect4.t b/t/plugin/openid-connect4.t index 9df55bee0ab1..99dc43d5413f 100644 --- a/t/plugin/openid-connect4.t +++ b/t/plugin/openid-connect4.t @@ -309,3 +309,153 @@ passed } --- response_body true + +=== TEST 7: Set new header name for authentication +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + require("apisix.plugins.openid-connect") + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "openid-connect": { + "client_id": "course_management", + "client_secret": "d1ec69e9-55d2-4109-a3ea-befa071579d5", + "discovery": "http://127.0.0.1:8080/realms/University/.well-known/openid-configuration", + "redirect_uri": "http://localhost:3000", + "ssl_verify": false, + "timeout": 10, + "bearer_only": true, + "realm": "University", + "auth_accept_token_as_header_name": "X-Access-Token", + "introspection_endpoint_auth_method": "client_secret_post", + "introspection_endpoint": "http://127.0.0.1:8080/realms/University/protocol/openid-connect/token/introspect" + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + +=== TEST 8: Access route +--- config + location /t { + content_by_lua_block { + -- Obtain valid access token from Keycloak using known username and password. + local json_decode = require("toolkit.json").decode + local http = require "resty.http" + local httpc = http.new() + local uri = "http://127.0.0.1:8080/realms/University/protocol/openid-connect/token" + local res, err = httpc:request_uri(uri, { + method = "POST", + body = "grant_type=password&client_id=course_management&client_secret=d1ec69e9-55d2-4109-a3ea-befa071579d5&username=teacher@gmail.com&password=123456", + headers = { + ["Content-Type"] = "application/x-www-form-urlencoded" + } + }) + + -- Check response from keycloak and fail quickly if there's no response. + if not res then + ngx.say(err) + return + end + + -- Check if response code was ok. + if res.status == 200 then + -- Get access token from JSON response body. + local body = json_decode(res.body) + local accessToken = body["access_token"] + + -- Access route using access token. Should work. + uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello" + local res, err = httpc:request_uri(uri, { + method = "GET", + headers = { + ["Authorization"] = "Bearer " .. body["access_token"] + } + }) + if res.status == 200 then + -- Route accessed successfully. + ngx.say(true) + else + -- Couldn't access route. + ngx.say(false) + end + else + -- Response from Keycloak not ok. + ngx.say(false) + end + } + } +--- error_code: 401 +--- error_log +Authorization header not found + +=== TEST 9: Access route +--- config + location /t { + content_by_lua_block { + -- Obtain valid access token from Keycloak using known username and password. + local json_decode = require("toolkit.json").decode + local http = require "resty.http" + local httpc = http.new() + local uri = "http://127.0.0.1:8080/realms/University/protocol/openid-connect/token" + local res, err = httpc:request_uri(uri, { + method = "POST", + body = "grant_type=password&client_id=course_management&client_secret=d1ec69e9-55d2-4109-a3ea-befa071579d5&username=teacher@gmail.com&password=123456", + headers = { + ["Content-Type"] = "application/x-www-form-urlencoded" + } + }) + + -- Check response from keycloak and fail quickly if there's no response. + if not res then + ngx.say(err) + return + end + + -- Check if response code was ok. + if res.status == 200 then + -- Get access token from JSON response body. + local body = json_decode(res.body) + local accessToken = body["access_token"] + + -- Access route using access token. Should work. + uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello" + local res, err = httpc:request_uri(uri, { + method = "GET", + headers = { + ["X-Access-Token"] = "Bearer " .. body["access_token"] + } + }) + if res.status == 200 then + -- Route accessed successfully. + ngx.say(true) + else + -- Couldn't access route. + ngx.say(false) + end + else + -- Response from Keycloak not ok. + ngx.say(false) + end + } + } +--- response_body +true \ No newline at end of file