From a2c5663765c74af51a63bd59f2aa4504654541a8 Mon Sep 17 00:00:00 2001 From: Robin Xiang Date: Tue, 1 Aug 2023 15:54:35 +0800 Subject: [PATCH 1/9] fix(oauth2): fix a bug that refresh_token could be shared across services when `global_credentials` is set as `true`. With `global_credential=true`, `access_token` can be shared across services, as well as `refresh_token` currently. This means that a `refresh_token` belonging to a service can be used to refresh tokens belonging to another service, which is consider as a bug. In this PR, the scope is taken into account as a new creteria of the request validation. Scopes associated with a token provided in the request will be compared with those configured in the Oauth2 instance hit by that request. FTI-5173 --- kong/plugins/oauth2/access.lua | 27 +++-- spec/03-plugins/25-oauth2/03-access_spec.lua | 100 +++++++++++++++++++ 2 files changed, 120 insertions(+), 7 deletions(-) diff --git a/kong/plugins/oauth2/access.lua b/kong/plugins/oauth2/access.lua index d3147e351b1..84401fdb447 100644 --- a/kong/plugins/oauth2/access.lua +++ b/kong/plugins/oauth2/access.lua @@ -757,19 +757,32 @@ local function issue_token(conf) error_description = "Invalid " .. REFRESH_TOKEN } - else - -- Check that the token belongs to the client application - if token.credential.id ~= client.id then + -- Check that the token belongs to the client application + elseif token.credential.id ~= client.id then response_params = { [ERROR] = "invalid_client", error_description = "Invalid client authentication" } - else + else + -- Check scopes + if token.scope then + for v in token.scope:gmatch("%S+") do + if not table_contains(conf.scopes, v) then + response_params = { + [ERROR] = "invalid_scope", + error_description = "scope mismatch", + } + break + end + end + end + + if not response_params[ERROR] then response_params = generate_token(conf, kong.router.get_service(), - client, - token.authenticated_userid, - token.scope, state, false, token) + client, + token.authenticated_userid, + token.scope, state, false, token) -- Delete old token if refresh token not persisted if not conf.reuse_refresh_token then kong.db.oauth2_tokens:delete({ id = token.id }) diff --git a/spec/03-plugins/25-oauth2/03-access_spec.lua b/spec/03-plugins/25-oauth2/03-access_spec.lua index 95a1913f351..fdd855af078 100644 --- a/spec/03-plugins/25-oauth2/03-access_spec.lua +++ b/spec/03-plugins/25-oauth2/03-access_spec.lua @@ -262,6 +262,8 @@ describe("Plugin: oauth2 [#" .. strategy .. "]", function() local service15 = admin_api.services:insert() local service16 = admin_api.services:insert() local service17 = admin_api.services:insert() + local service18 = admin_api.services:insert() + local service19 = admin_api.services:insert() local route1 = assert(admin_api.routes:insert({ hosts = { "oauth2.com" }, @@ -377,6 +379,18 @@ describe("Plugin: oauth2 [#" .. strategy .. "]", function() service = service17, })) + local route18 = assert(admin_api.routes:insert({ + hosts = { "oauth2_18.com" }, + protocols = { "http", "https" }, + service = service18, + })) + + local route19 = assert(admin_api.routes:insert({ + hosts = { "oauth2_19.com" }, + protocols = { "http", "https" }, + service = service19, + })) + local service_grpc = assert(admin_api.services:insert { name = "grpc", url = helpers.grpcbin_url, @@ -556,6 +570,22 @@ describe("Plugin: oauth2 [#" .. strategy .. "]", function() global_credentials = true, } }) + + admin_api.oauth2_plugins:insert({ + route = { id = route18.id }, + config = { + scopes = { "scope18" }, + global_credentials = true, + } + }) + + admin_api.oauth2_plugins:insert({ + route = { id = route19.id }, + config = { + scopes = { "scope19" }, + global_credentials = true, + } + }) end) before_each(function () @@ -2905,6 +2935,76 @@ describe("Plugin: oauth2 [#" .. strategy .. "]", function() }) assert.res_status(401, res) end) + it("fails to refresh token when scope is mismatching", function () + -- provision code + local code, body, res + local request_client = helpers.proxy_ssl_client() + body = { + provision_key = "provision123", + client_id = "clientid123", + response_type = "code", + scope = "scope18", + state = "hello", + authenticated_userid = "userid123", + } + res = assert(request_client:send { + method = "POST", + path = "/oauth2/authorize", + body = body, + headers = kong.table.merge({ + ["Host"] = "oauth2_18.com", + ["Content-Type"] = "application/json" + }) + }) + res = assert(cjson.decode(assert.res_status(200, res))) + if res.redirect_uri then + local iterator, err = ngx.re.gmatch(res.redirect_uri, "^http://google\\.com/kong\\?code=([\\w]{32,32})&state=hello$") + assert.is_nil(err) + local m, err = iterator() + assert.is_nil(err) + code = m[1] + end + + -- provision token + body = { + code = code, + client_id = "clientid123", + client_secret = "secret123", + grant_type = "authorization_code", + redirect_uri = "http://google.com/kong", + } + res = assert(request_client:send { + method = "POST", + path = "/oauth2/token", + body = body, + headers = { + ["Host"] = "oauth2_18.com", + ["Content-Type"] = "application/json" + } + }) + local token = assert(cjson.decode(assert.res_status(200, res))) + + -- refresh token with mismatching scope + res = assert(request_client:send { + method = "POST", + path = "/oauth2/token", + body = { + refresh_token = token.refresh_token, + client_id = "clientid123", + client_secret = "secret123", + grant_type = "refresh_token", + }, + headers = { + ["Host"] = "oauth2_19.com", + ["Content-Type"] = "application/json" + } + }) + res = assert(cjson.decode(assert.res_status(400, res))) + assert.same({ + error = "invalid_scope", + error_description = "scope mismatch" + }, res) + end) it("fails when a correct access_token is being sent in the wrong header", function() local token = provision_token("oauth2_11.com",nil,"clientid1011","secret1011") From 15a407a8ef3f3f4f13d556d46bb0d29e2d75aad2 Mon Sep 17 00:00:00 2001 From: Robin Xiang Date: Wed, 2 Aug 2023 12:21:46 +0800 Subject: [PATCH 2/9] add changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 18fda7de886..788203e10b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,8 @@ `max_retry_delay` must now be `number`s greater than 0.001 (seconds). [#10840](https://github.com/Kong/kong/pull/10840) +- For OAuth2 plugin, `scope` has been taken into account as a new creteria of the request validation. When refreshing token with `refresh_token`, the scopes associated with the `refresh_token` provided in the request must be same with or a subset of the scopes configured in the OAuth2 plugin instance hit by the request. + [#11342](https://github.com/Kong/kong/pull/11342) ### Additions From 3e993cca3bafae9da3b285e51c042839eff68a68 Mon Sep 17 00:00:00 2001 From: Robin Xiang Date: Tue, 8 Aug 2023 10:36:44 +0800 Subject: [PATCH 3/9] indent --- kong/plugins/oauth2/access.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kong/plugins/oauth2/access.lua b/kong/plugins/oauth2/access.lua index 84401fdb447..3b1f9b4ca26 100644 --- a/kong/plugins/oauth2/access.lua +++ b/kong/plugins/oauth2/access.lua @@ -780,9 +780,9 @@ local function issue_token(conf) if not response_params[ERROR] then response_params = generate_token(conf, kong.router.get_service(), - client, - token.authenticated_userid, - token.scope, state, false, token) + client, + token.authenticated_userid, + token.scope, state, false, token) -- Delete old token if refresh token not persisted if not conf.reuse_refresh_token then kong.db.oauth2_tokens:delete({ id = token.id }) From 0f3746677e9fb560191a6201c86ec734a1fce84a Mon Sep 17 00:00:00 2001 From: Robin Xiang Date: Tue, 8 Aug 2023 18:09:13 +0800 Subject: [PATCH 4/9] Update CHANGELOG.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hans Hübner --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 788203e10b5..31c94ac73c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,7 +31,7 @@ `max_retry_delay` must now be `number`s greater than 0.001 (seconds). [#10840](https://github.com/Kong/kong/pull/10840) -- For OAuth2 plugin, `scope` has been taken into account as a new creteria of the request validation. When refreshing token with `refresh_token`, the scopes associated with the `refresh_token` provided in the request must be same with or a subset of the scopes configured in the OAuth2 plugin instance hit by the request. +- For OAuth2 plugin, `scope` has been taken into account as a new criterion of the request validation. When refreshing token with `refresh_token`, the scopes associated with the `refresh_token` provided in the request must be same with or a subset of the scopes configured in the OAuth2 plugin instance hit by the request. [#11342](https://github.com/Kong/kong/pull/11342) ### Additions From fcf5c52e4148c412d5d628ab4eee790421d20db4 Mon Sep 17 00:00:00 2001 From: Robin Xiang Date: Tue, 8 Aug 2023 21:07:40 +0800 Subject: [PATCH 5/9] Update kong/plugins/oauth2/access.lua MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hans Hübner --- kong/plugins/oauth2/access.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kong/plugins/oauth2/access.lua b/kong/plugins/oauth2/access.lua index 3b1f9b4ca26..cf7c4b16db7 100644 --- a/kong/plugins/oauth2/access.lua +++ b/kong/plugins/oauth2/access.lua @@ -767,8 +767,8 @@ local function issue_token(conf) else -- Check scopes if token.scope then - for v in token.scope:gmatch("%S+") do - if not table_contains(conf.scopes, v) then + for scope in token.scope:gmatch("%S+") do + if not table_contains(conf.scopes, scope) then response_params = { [ERROR] = "invalid_scope", error_description = "scope mismatch", From 7ba904209d40575b8a1ee188e1473bc73d3589d6 Mon Sep 17 00:00:00 2001 From: Robin Xiang Date: Tue, 8 Aug 2023 21:23:41 +0800 Subject: [PATCH 6/9] add a test case that verifies the subsetting functionality --- spec/03-plugins/25-oauth2/03-access_spec.lua | 166 +++++++++++-------- 1 file changed, 101 insertions(+), 65 deletions(-) diff --git a/spec/03-plugins/25-oauth2/03-access_spec.lua b/spec/03-plugins/25-oauth2/03-access_spec.lua index fdd855af078..c8674e659e3 100644 --- a/spec/03-plugins/25-oauth2/03-access_spec.lua +++ b/spec/03-plugins/25-oauth2/03-access_spec.lua @@ -264,6 +264,7 @@ describe("Plugin: oauth2 [#" .. strategy .. "]", function() local service17 = admin_api.services:insert() local service18 = admin_api.services:insert() local service19 = admin_api.services:insert() + local service20 = admin_api.services:insert() local route1 = assert(admin_api.routes:insert({ hosts = { "oauth2.com" }, @@ -391,6 +392,12 @@ describe("Plugin: oauth2 [#" .. strategy .. "]", function() service = service19, })) + local route20 = assert(admin_api.routes:insert({ + hosts = { "oauth2_20.com" }, + protocols = { "http", "https" }, + service = service20, + })) + local service_grpc = assert(admin_api.services:insert { name = "grpc", url = helpers.grpcbin_url, @@ -586,6 +593,14 @@ describe("Plugin: oauth2 [#" .. strategy .. "]", function() global_credentials = true, } }) + + admin_api.oauth2_plugins:insert({ + route = { id = route20.id }, + config = { + scopes = { "scope18", "scope20" }, + global_credentials = true, + } + }) end) before_each(function () @@ -2935,75 +2950,96 @@ describe("Plugin: oauth2 [#" .. strategy .. "]", function() }) assert.res_status(401, res) end) - it("fails to refresh token when scope is mismatching", function () - -- provision code - local code, body, res - local request_client = helpers.proxy_ssl_client() - body = { - provision_key = "provision123", - client_id = "clientid123", - response_type = "code", - scope = "scope18", - state = "hello", - authenticated_userid = "userid123", - } - res = assert(request_client:send { - method = "POST", - path = "/oauth2/authorize", - body = body, - headers = kong.table.merge({ - ["Host"] = "oauth2_18.com", - ["Content-Type"] = "application/json" + describe("refreshing token", function() + local request_client, token + it("fails when scope is mismatching", function () + -- provision code + local code, body, res + request_client = helpers.proxy_ssl_client() + body = { + provision_key = "provision123", + client_id = "clientid123", + response_type = "code", + scope = "scope18", + state = "hello", + authenticated_userid = "userid123", + } + res = assert(request_client:send { + method = "POST", + path = "/oauth2/authorize", + body = body, + headers = kong.table.merge({ + ["Host"] = "oauth2_18.com", + ["Content-Type"] = "application/json" + }) }) - }) - res = assert(cjson.decode(assert.res_status(200, res))) - if res.redirect_uri then - local iterator, err = ngx.re.gmatch(res.redirect_uri, "^http://google\\.com/kong\\?code=([\\w]{32,32})&state=hello$") - assert.is_nil(err) - local m, err = iterator() - assert.is_nil(err) - code = m[1] - end + res = assert(cjson.decode(assert.res_status(200, res))) + if res.redirect_uri then + local iterator, err = ngx.re.gmatch(res.redirect_uri, "^http://google\\.com/kong\\?code=([\\w]{32,32})&state=hello$") + assert.is_nil(err) + local m, err = iterator() + assert.is_nil(err) + code = m[1] + end - -- provision token - body = { - code = code, - client_id = "clientid123", - client_secret = "secret123", - grant_type = "authorization_code", - redirect_uri = "http://google.com/kong", - } - res = assert(request_client:send { - method = "POST", - path = "/oauth2/token", - body = body, - headers = { - ["Host"] = "oauth2_18.com", - ["Content-Type"] = "application/json" + -- provision token + body = { + code = code, + client_id = "clientid123", + client_secret = "secret123", + grant_type = "authorization_code", + redirect_uri = "http://google.com/kong", } - }) - local token = assert(cjson.decode(assert.res_status(200, res))) + res = assert(request_client:send { + method = "POST", + path = "/oauth2/token", + body = body, + headers = { + ["Host"] = "oauth2_18.com", + ["Content-Type"] = "application/json" + } + }) + token = assert(cjson.decode(assert.res_status(200, res))) - -- refresh token with mismatching scope - res = assert(request_client:send { - method = "POST", - path = "/oauth2/token", - body = { - refresh_token = token.refresh_token, - client_id = "clientid123", - client_secret = "secret123", - grant_type = "refresh_token", - }, - headers = { - ["Host"] = "oauth2_19.com", - ["Content-Type"] = "application/json" - } - }) - res = assert(cjson.decode(assert.res_status(400, res))) - assert.same({ - error = "invalid_scope", - error_description = "scope mismatch" - }, res) + -- refresh token with mismatching scope + res = assert(request_client:send { + method = "POST", + path = "/oauth2/token", + body = { + refresh_token = token.refresh_token, + client_id = "clientid123", + client_secret = "secret123", + grant_type = "refresh_token", + }, + headers = { + ["Host"] = "oauth2_19.com", + ["Content-Type"] = "application/json" + } + }) + res = assert(cjson.decode(assert.res_status(400, res))) + assert.same({ + error = "invalid_scope", + error_description = "scope mismatch" + }, res) + end) + it("successes when scope is a subset", function() + -- refresh token with mismatching scope + local res = assert(request_client:send { + method = "POST", + path = "/oauth2/token", + body = { + refresh_token = token.refresh_token, + client_id = "clientid123", + client_secret = "secret123", + grant_type = "refresh_token", + }, + headers = { + ["Host"] = "oauth2_20.com", + ["Content-Type"] = "application/json" + } + }) + res = assert(cjson.decode(assert.res_status(200, res))) + end) end) it("fails when a correct access_token is being sent in the wrong header", function() local token = provision_token("oauth2_11.com",nil,"clientid1011","secret1011") From 439ca663b9e8bc7340b05c95a8e1c0dec310dbd6 Mon Sep 17 00:00:00 2001 From: Robin Xiang Date: Tue, 8 Aug 2023 21:28:44 +0800 Subject: [PATCH 7/9] lint --- spec/03-plugins/25-oauth2/03-access_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/03-plugins/25-oauth2/03-access_spec.lua b/spec/03-plugins/25-oauth2/03-access_spec.lua index c8674e659e3..ad11ea600f6 100644 --- a/spec/03-plugins/25-oauth2/03-access_spec.lua +++ b/spec/03-plugins/25-oauth2/03-access_spec.lua @@ -3038,7 +3038,7 @@ describe("Plugin: oauth2 [#" .. strategy .. "]", function() ["Content-Type"] = "application/json" } }) - res = assert(cjson.decode(assert.res_status(200, res))) + assert(cjson.decode(assert.res_status(200, res))) end) end) it("fails when a correct access_token is being sent in the wrong header", function() From e27a9263ad3fec5eba5543e593c6a7800de7bd40 Mon Sep 17 00:00:00 2001 From: Robin Xiang Date: Wed, 9 Aug 2023 18:12:17 +0800 Subject: [PATCH 8/9] Update spec/03-plugins/25-oauth2/03-access_spec.lua MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hans Hübner --- spec/03-plugins/25-oauth2/03-access_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/03-plugins/25-oauth2/03-access_spec.lua b/spec/03-plugins/25-oauth2/03-access_spec.lua index ad11ea600f6..65c535f9c75 100644 --- a/spec/03-plugins/25-oauth2/03-access_spec.lua +++ b/spec/03-plugins/25-oauth2/03-access_spec.lua @@ -3022,7 +3022,7 @@ describe("Plugin: oauth2 [#" .. strategy .. "]", function() error_description = "scope mismatch" }, res) end) - it("successes when scope is a subset", function() + it("succeeds when scope is a subset", function() -- refresh token with mismatching scope local res = assert(request_client:send { method = "POST", From 34715ad6f05ce8005a60a5698bd63eaf9cd6a46c Mon Sep 17 00:00:00 2001 From: Robin Xiang Date: Wed, 9 Aug 2023 18:27:00 +0800 Subject: [PATCH 9/9] seperates test case --- kong/plugins/oauth2/access.lua | 2 +- spec/03-plugins/25-oauth2/03-access_spec.lua | 237 ++++++++++++------- 2 files changed, 152 insertions(+), 87 deletions(-) diff --git a/kong/plugins/oauth2/access.lua b/kong/plugins/oauth2/access.lua index cf7c4b16db7..0a2ff97f830 100644 --- a/kong/plugins/oauth2/access.lua +++ b/kong/plugins/oauth2/access.lua @@ -771,7 +771,7 @@ local function issue_token(conf) if not table_contains(conf.scopes, scope) then response_params = { [ERROR] = "invalid_scope", - error_description = "scope mismatch", + error_description = "Scope mismatch", } break end diff --git a/spec/03-plugins/25-oauth2/03-access_spec.lua b/spec/03-plugins/25-oauth2/03-access_spec.lua index 65c535f9c75..9d353bdb0bc 100644 --- a/spec/03-plugins/25-oauth2/03-access_spec.lua +++ b/spec/03-plugins/25-oauth2/03-access_spec.lua @@ -265,6 +265,7 @@ describe("Plugin: oauth2 [#" .. strategy .. "]", function() local service18 = admin_api.services:insert() local service19 = admin_api.services:insert() local service20 = admin_api.services:insert() + local service21 = admin_api.services:insert() local route1 = assert(admin_api.routes:insert({ hosts = { "oauth2.com" }, @@ -398,6 +399,12 @@ describe("Plugin: oauth2 [#" .. strategy .. "]", function() service = service20, })) + local route21 = assert(admin_api.routes:insert({ + hosts = { "oauth2_21.com" }, + protocols = { "http", "https" }, + service = service21, + })) + local service_grpc = assert(admin_api.services:insert { name = "grpc", url = helpers.grpcbin_url, @@ -597,7 +604,15 @@ describe("Plugin: oauth2 [#" .. strategy .. "]", function() admin_api.oauth2_plugins:insert({ route = { id = route20.id }, config = { - scopes = { "scope18", "scope20" }, + scopes = { "scope20" }, + global_credentials = true, + } + }) + + admin_api.oauth2_plugins:insert({ + route = { id = route21.id }, + config = { + scopes = { "scope20", "scope21" }, global_credentials = true, } }) @@ -2950,97 +2965,147 @@ describe("Plugin: oauth2 [#" .. strategy .. "]", function() }) assert.res_status(401, res) end) - describe("refreshing token", function() - local request_client, token - it("fails when scope is mismatching", function () - -- provision code - local code, body, res - request_client = helpers.proxy_ssl_client() - body = { - provision_key = "provision123", - client_id = "clientid123", - response_type = "code", - scope = "scope18", - state = "hello", - authenticated_userid = "userid123", - } - res = assert(request_client:send { - method = "POST", - path = "/oauth2/authorize", - body = body, - headers = kong.table.merge({ - ["Host"] = "oauth2_18.com", - ["Content-Type"] = "application/json" - }) - }) - res = assert(cjson.decode(assert.res_status(200, res))) - if res.redirect_uri then - local iterator, err = ngx.re.gmatch(res.redirect_uri, "^http://google\\.com/kong\\?code=([\\w]{32,32})&state=hello$") - assert.is_nil(err) - local m, err = iterator() - assert.is_nil(err) - code = m[1] - end - -- provision token - body = { - code = code, + it("refreshing token fails when scope is mismatching", function () + -- provision code + local code, body, res + local request_client = helpers.proxy_ssl_client() + body = { + provision_key = "provision123", client_id = "clientid123", - client_secret = "secret123", - grant_type = "authorization_code", - redirect_uri = "http://google.com/kong", - } - res = assert(request_client:send { - method = "POST", - path = "/oauth2/token", - body = body, - headers = { - ["Host"] = "oauth2_18.com", - ["Content-Type"] = "application/json" - } + response_type = "code", + scope = "scope18", + state = "hello", + authenticated_userid = "userid123", + } + res = assert(request_client:send { + method = "POST", + path = "/oauth2/authorize", + body = body, + headers = kong.table.merge({ + ["Host"] = "oauth2_18.com", + ["Content-Type"] = "application/json" }) - token = assert(cjson.decode(assert.res_status(200, res))) + }) + res = assert(cjson.decode(assert.res_status(200, res))) + if res.redirect_uri then + local iterator, err = ngx.re.gmatch(res.redirect_uri, "^http://google\\.com/kong\\?code=([\\w]{32,32})&state=hello$") + assert.is_nil(err) + local m, err = iterator() + assert.is_nil(err) + code = m[1] + end - -- refresh token with mismatching scope - res = assert(request_client:send { - method = "POST", - path = "/oauth2/token", - body = { - refresh_token = token.refresh_token, - client_id = "clientid123", - client_secret = "secret123", - grant_type = "refresh_token", - }, - headers = { - ["Host"] = "oauth2_19.com", - ["Content-Type"] = "application/json" - } - }) - res = assert(cjson.decode(assert.res_status(400, res))) - assert.same({ - error = "invalid_scope", - error_description = "scope mismatch" - }, res) - end) - it("succeeds when scope is a subset", function() - -- refresh token with mismatching scope - local res = assert(request_client:send { - method = "POST", - path = "/oauth2/token", - body = { - refresh_token = token.refresh_token, - client_id = "clientid123", - client_secret = "secret123", - grant_type = "refresh_token", - }, - headers = { - ["Host"] = "oauth2_20.com", - ["Content-Type"] = "application/json" - } + -- provision token + body = { + code = code, + client_id = "clientid123", + client_secret = "secret123", + grant_type = "authorization_code", + redirect_uri = "http://google.com/kong", + } + res = assert(request_client:send { + method = "POST", + path = "/oauth2/token", + body = body, + headers = { + ["Host"] = "oauth2_18.com", + ["Content-Type"] = "application/json" + } + }) + local token = assert(cjson.decode(assert.res_status(200, res))) + + -- refresh token with mismatching scope + res = assert(request_client:send { + method = "POST", + path = "/oauth2/token", + body = { + refresh_token = token.refresh_token, + client_id = "clientid123", + client_secret = "secret123", + grant_type = "refresh_token", + }, + headers = { + ["Host"] = "oauth2_19.com", + ["Content-Type"] = "application/json" + } + }) + res = assert(cjson.decode(assert.res_status(400, res))) + assert.same({ + error = "invalid_scope", + error_description = "Scope mismatch" + }, res) + request_client:close() + end) + + it("refreshing token succeeds when scope is a subset", function() + -- provision code + local code, body, res + local request_client = helpers.proxy_ssl_client() + body = { + provision_key = "provision123", + client_id = "clientid123", + response_type = "code", + scope = "scope20", + state = "hello", + authenticated_userid = "userid123", + } + res = assert(request_client:send { + method = "POST", + path = "/oauth2/authorize", + body = body, + headers = kong.table.merge({ + ["Host"] = "oauth2_20.com", + ["Content-Type"] = "application/json" }) - assert(cjson.decode(assert.res_status(200, res))) - end) + }) + res = assert(cjson.decode(assert.res_status(200, res))) + if res.redirect_uri then + local iterator, err = ngx.re.gmatch(res.redirect_uri, "^http://google\\.com/kong\\?code=([\\w]{32,32})&state=hello$") + assert.is_nil(err) + local m, err = iterator() + assert.is_nil(err) + code = m[1] + end + + -- provision token + body = { + code = code, + client_id = "clientid123", + client_secret = "secret123", + grant_type = "authorization_code", + redirect_uri = "http://google.com/kong", + } + res = assert(request_client:send { + method = "POST", + path = "/oauth2/token", + body = body, + headers = { + ["Host"] = "oauth2_20.com", + ["Content-Type"] = "application/json" + } + }) + local token = assert(cjson.decode(assert.res_status(200, res))) + + -- refresh token with mismatching scope + local res = assert(request_client:send { + method = "POST", + path = "/oauth2/token", + body = { + refresh_token = token.refresh_token, + client_id = "clientid123", + client_secret = "secret123", + grant_type = "refresh_token", + }, + headers = { + ["Host"] = "oauth2_21.com", + ["Content-Type"] = "application/json" + } + }) + assert(cjson.decode(assert.res_status(200, res))) + request_client:close() end) + it("fails when a correct access_token is being sent in the wrong header", function() local token = provision_token("oauth2_11.com",nil,"clientid1011","secret1011")