diff --git a/apisix/plugins/openid-connect.lua b/apisix/plugins/openid-connect.lua index 1ebc12deccda..3ecf9d246f0f 100644 --- a/apisix/plugins/openid-connect.lua +++ b/apisix/plugins/openid-connect.lua @@ -448,7 +448,7 @@ local function get_bearer_access_token(ctx) local auth_header = core.request.header(ctx, "Authorization") 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") + local access_token_header = ctx.openid_connect_client_x_access_token if not access_token_header then -- No X-Access-Token header neither. return false, nil, nil @@ -573,20 +573,11 @@ end local function add_access_token_header(ctx, conf, token) - if token then - -- Add Authorization or X-Access-Token header, respectively, if not already set. - if conf.set_access_token_header then - if conf.access_token_in_authorization_header then - if not core.request.header(ctx, "Authorization") then - -- Add Authorization header. - core.request.set_header(ctx, "Authorization", "Bearer " .. token) - end - else - if not core.request.header(ctx, "X-Access-Token") then - -- Add X-Access-Token header. - core.request.set_header(ctx, "X-Access-Token", token) - end - end + if token and conf.set_access_token_header then + if conf.access_token_in_authorization_header then + core.request.set_header(ctx, "Authorization", "Bearer " .. token) + else + core.request.set_header(ctx, "X-Access-Token", token) end end end @@ -626,6 +617,16 @@ end function _M.rewrite(plugin_conf, ctx) local conf = core.table.clone(plugin_conf) + -- Snapshot the client-supplied X-Access-Token (it doubles as a bearer + -- input via get_bearer_access_token) and clear the four headers this + -- plugin advertises as outputs so client-supplied values cannot bleed + -- through to the upstream. + ctx.openid_connect_client_x_access_token = core.request.header(ctx, "X-Access-Token") + core.request.set_header(ctx, "X-Access-Token", nil) + core.request.set_header(ctx, "X-Userinfo", nil) + core.request.set_header(ctx, "X-ID-Token", nil) + core.request.set_header(ctx, "X-Refresh-Token", nil) + -- Previously, we multiply conf.timeout before storing it in etcd. -- If the timeout is too large, we should not multiply it again. if not (conf.timeout >= 1000 and conf.timeout % 1000 == 0) then diff --git a/t/plugin/openid-connect-identity-headers.t b/t/plugin/openid-connect-identity-headers.t new file mode 100644 index 000000000000..ca842c827397 --- /dev/null +++ b/t/plugin/openid-connect-identity-headers.t @@ -0,0 +1,168 @@ +# +# 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(); + +add_block_preprocessor(sub { + my ($block) = @_; + + if ((!defined $block->error_log) && (!defined $block->no_error_log)) { + $block->set_value("no_error_log", "[error]"); + } + + if (!defined $block->request) { + $block->set_value("request", "GET /t"); + } +}); + +run_tests(); + +__DATA__ + +=== TEST 1: Set up route with unauth_action = "pass" so unauthenticated requests pass through. +--- 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": { + "openid-connect": { + "client_id": "kbyuFDidLLm280LIwVFiazOqjO3ty8KH", + "client_secret": "60Op4HFM0I8ajz0WdiStAbziZ-VFQttXuxixHHs2R7r7-CW8GR79l-mmLqMhc-Sa", + "discovery": "https://samples.auth0.com/.well-known/openid-configuration", + "redirect_uri": "https://iresty.com", + "ssl_verify": false, + "timeout": 10, + "scope": "apisix", + "unauth_action": "pass", + "use_pkce": false, + "session": { + "secret": "jwcE5v3pM9VhqLxmxFOH9uZaLo8u7KQK" + } + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/uri" + }]] + ) + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 2: Access route without authentication. Client-supplied X-Userinfo / X-ID-Token / X-Refresh-Token / X-Access-Token must not reach upstream. +--- request +GET /uri HTTP/1.1 +--- more_headers +X-Userinfo: client-supplied-userinfo +X-ID-Token: client-supplied-id-token +X-Refresh-Token: client-supplied-refresh-token +X-Access-Token: client-supplied-access-token +--- response_body +uri: /uri +host: localhost +x-real-ip: 127.0.0.1 +--- error_code: 200 + + + +=== TEST 3: Update route to bearer_only with embedded RS256 public key. +--- 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": { + "openid-connect": { + "client_id": "kbyuFDidLLm280LIwVFiazOqjO3ty8KH", + "client_secret": "60Op4HFM0I8ajz0WdiStAbziZ-VFQttXuxixHHs2R7r7-CW8GR79l-mmLqMhc-Sa", + "discovery": "https://samples.auth0.com/.well-known/openid-configuration", + "redirect_uri": "https://iresty.com", + "ssl_verify": false, + "timeout": 10, + "bearer_only": true, + "scope": "apisix", + "public_key": "-----BEGIN PUBLIC KEY-----\n]] .. + [[MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw86xcJwNxL2MkWnjIGiw\n]] .. + [[94QY78Sq89dLqMdV/Ku2GIX9lYkbS0VDGtmxDGJLBOYW4cKTX+pigJyzglLgE+nD\n]] .. + [[z3VJf2oCqSV74gTyEdi7sw9e1rCyR6dR8VA7LEpIHwmhnDhhjXy1IYSKRdiVHLS5\n]] .. + [[sYmaAGckpUo3MLqUrgydGj5tFzvK/R/ELuZBdlZM+XuWxYry05r860E3uL+VdVCO\n]] .. + [[oU4RJQknlJnTRd7ht8KKcZb6uM14C057i26zX/xnOJpaVflA4EyEo99hKQAdr8Sh\n]] .. + [[G70MOLYvGCZxl1o8S3q4X67MxcPlfJaXnbog2AOOGRaFar88XiLFWTbXMCLuz7xD\n]] .. + [[zQIDAQAB\n]] .. + [[-----END PUBLIC KEY-----", + "token_signing_alg_values_expected": "RS256", + "set_id_token_header": false, + "set_userinfo_header": false, + "claim_validator": { + "issuer": { + "valid_issuers": ["Mysoft corp"] + } + } + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/uri" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 4: Access route with valid bearer token and a client-supplied X-Access-Token. Upstream must see the validated token, not the client value. +--- request +GET /uri HTTP/1.1 +--- more_headers +Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhMSI6IkRhdGEgMSIsImlhdCI6MTU4NTEyMjUwMiwiZXhwIjoxOTAwNjk4NTAyLCJhdWQiOiJodHRwOi8vbXlzb2Z0Y29ycC5pbiIsImlzcyI6Ik15c29mdCBjb3JwIiwic3ViIjoic29tZUB1c2VyLmNvbSJ9.Vq_sBN7nH67vMDbiJE01EP4hvJYE_5ju6izjkOX8pF5OS4g2RWKWpL6h6-b0tTkCzG4JD5BEl13LWW-Gxxw0i9vEK0FLg_kC_kZLYB8WuQ6B9B9YwzmZ3OLbgnYzt_VD7D-7psEbwapJl5hbFsIjDgOAEx-UCmjUcl2frZxZavG2LUiEGs9Ri7KqOZmTLgNDMWfeWh1t1LyD0_b-eTInbasVtKQxMlb5kR0Ln_Qg5092L-irJ7dqaZma7HItCnzXJROdqJEsMIBAYRwDGa_w5kIACeMOdU85QKtMHzOenYFkm6zh_s59ndziTctKMz196Y8AL08xuTi6d1gEWpM92A +X-Access-Token: client-supplied-access-token +--- response_body +uri: /uri +authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhMSI6IkRhdGEgMSIsImlhdCI6MTU4NTEyMjUwMiwiZXhwIjoxOTAwNjk4NTAyLCJhdWQiOiJodHRwOi8vbXlzb2Z0Y29ycC5pbiIsImlzcyI6Ik15c29mdCBjb3JwIiwic3ViIjoic29tZUB1c2VyLmNvbSJ9.Vq_sBN7nH67vMDbiJE01EP4hvJYE_5ju6izjkOX8pF5OS4g2RWKWpL6h6-b0tTkCzG4JD5BEl13LWW-Gxxw0i9vEK0FLg_kC_kZLYB8WuQ6B9B9YwzmZ3OLbgnYzt_VD7D-7psEbwapJl5hbFsIjDgOAEx-UCmjUcl2frZxZavG2LUiEGs9Ri7KqOZmTLgNDMWfeWh1t1LyD0_b-eTInbasVtKQxMlb5kR0Ln_Qg5092L-irJ7dqaZma7HItCnzXJROdqJEsMIBAYRwDGa_w5kIACeMOdU85QKtMHzOenYFkm6zh_s59ndziTctKMz196Y8AL08xuTi6d1gEWpM92A +host: localhost +x-access-token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhMSI6IkRhdGEgMSIsImlhdCI6MTU4NTEyMjUwMiwiZXhwIjoxOTAwNjk4NTAyLCJhdWQiOiJodHRwOi8vbXlzb2Z0Y29ycC5pbiIsImlzcyI6Ik15c29mdCBjb3JwIiwic3ViIjoic29tZUB1c2VyLmNvbSJ9.Vq_sBN7nH67vMDbiJE01EP4hvJYE_5ju6izjkOX8pF5OS4g2RWKWpL6h6-b0tTkCzG4JD5BEl13LWW-Gxxw0i9vEK0FLg_kC_kZLYB8WuQ6B9B9YwzmZ3OLbgnYzt_VD7D-7psEbwapJl5hbFsIjDgOAEx-UCmjUcl2frZxZavG2LUiEGs9Ri7KqOZmTLgNDMWfeWh1t1LyD0_b-eTInbasVtKQxMlb5kR0Ln_Qg5092L-irJ7dqaZma7HItCnzXJROdqJEsMIBAYRwDGa_w5kIACeMOdU85QKtMHzOenYFkm6zh_s59ndziTctKMz196Y8AL08xuTi6d1gEWpM92A +x-real-ip: 127.0.0.1 +--- error_code: 200