From 5c2e6c034eb64b524dc2c305b9f4629d45079781 Mon Sep 17 00:00:00 2001 From: Sten Hiedel Date: Fri, 4 Dec 2015 12:54:56 +0000 Subject: [PATCH] Change oauth2 authentication challenge to follow rfc2616, rfc2617, and rfc6750 --- kong/plugins/oauth2/access.lua | 11 +++-- spec/plugins/oauth2/access_spec.lua | 64 ++++++++++++++++++----------- 2 files changed, 49 insertions(+), 26 deletions(-) diff --git a/kong/plugins/oauth2/access.lua b/kong/plugins/oauth2/access.lua index 66c41ce9e2ec..3f6ad610ccdf 100644 --- a/kong/plugins/oauth2/access.lua +++ b/kong/plugins/oauth2/access.lua @@ -376,16 +376,21 @@ function _M.execute(conf) end end - local token = retrieve_token(parse_access_token(conf)) + local accessToken = parse_access_token(conf); + if not accessToken then + return responses.send_HTTP_UNAUTHORIZED({}, false, {["WWW-Authenticate"] = 'Bearer realm="service"'}) + end + + local token = retrieve_token(accessToken) if not token then - return responses.send_HTTP_FORBIDDEN("Invalid authentication credentials") + return responses.send_HTTP_UNAUTHORIZED({[ERROR] = "invalid_token", error_description = "The access token is invalid"}, false, {["WWW-Authenticate"] = 'Bearer realm="service" error="invalid_token" error_description="The access token is invalid"'}) end -- Check expiration date if token.expires_in > 0 then -- zero means the token never expires local now = timestamp.get_utc() if now - token.created_at > (token.expires_in * 1000) then - return responses.send_HTTP_BAD_REQUEST({[ERROR] = "invalid_request", error_description = "access_token expired"}) + return responses.send_HTTP_UNAUTHORIZED({[ERROR] = "invalid_token", error_description = "The access token expired"}, false, {["WWW-Authenticate"] = 'Bearer realm="service" error="invalid_token" error_description="The access token expired"'}) end end diff --git a/spec/plugins/oauth2/access_spec.lua b/spec/plugins/oauth2/access_spec.lua index d1cbe06d617e..586738893bcd 100644 --- a/spec/plugins/oauth2/access_spec.lua +++ b/spec/plugins/oauth2/access_spec.lua @@ -648,21 +648,6 @@ describe("Authentication Plugin", function() end) describe("Making a request", function() - - it("should return an error when nothing is being sent", function() - local response, status = http_client.post(STUB_GET_URL, { }, {host = "oauth2.com"}) - local body = cjson.decode(response) - assert.are.equal(403, status) - assert.are.equal("Invalid authentication credentials", body.message) - end) - - it("should return an error when a wrong access token is being sent", function() - local response, status = http_client.get(STUB_GET_URL, { access_token = "hello" }, {host = "oauth2.com"}) - local body = cjson.decode(response) - assert.are.equal(403, status) - assert.are.equal("Invalid authentication credentials", body.message) - end) - it("should work when a correct access_token is being sent in the querystring", function() local token = provision_token() local _, status = http_client.post(STUB_GET_URL, { access_token = token.access_token }, {host = "oauth2.com"}) @@ -694,15 +679,50 @@ describe("Authentication Plugin", function() assert.are.equal("userid123", body.headers["x-authenticated-userid"]) assert.are.equal("email", body.headers["x-authenticated-scope"]) end) + end) + + describe("Authentication challenge", function() + it("should return 401 Unauthorized without error if it lacks any authentication information", function() + local response, status, headers = http_client.post(STUB_GET_URL, { }, {host = "oauth2.com"}) + local body = cjson.decode(response) + assert.are.equal(401, status) + assert.are.equal('Bearer realm="service"', headers['www-authenticate']) + assert.are.equal(0, utils.table_size(body)) + end) - it("should not work when a correct access_token is being sent in an authorization header (bearer)", function() + it("should return 401 Unauthorized when a invalid access token is being sent via url parameter", function() + local response, status, headers = http_client.get(STUB_GET_URL, { access_token = "invalid" }, {host = "oauth2.com"}) + local body = cjson.decode(response) + assert.are.equal(401, status) + assert.are.equal('Bearer realm="service" error="invalid_token" error_description="The access token invalid"', headers['www-authenticate']) + assert.are.equal("invalid_token", body.error) + assert.are.equal("The access token invalid", body.error_description) + end) + + it("should return 401 Unauthorized when a invalid access_token is being sent via the Authorization header", function() local token = provision_token() - local response, status = http_client.post(STUB_POST_URL, { }, {host = "oauth2.com", authorization = "bearer "..token.access_token.."hello"}) + local response, status, headers = http_client.post(STUB_POST_URL, { }, {host = "oauth2.com", authorization = "bearer invalid"}) local body = cjson.decode(response) - assert.are.equal(403, status) - assert.are.equal("Invalid authentication credentials", body.message) + assert.are.equal(401, status) + assert.are.equal('Bearer realm="service" error="invalid_token" error_description="The access token invalid"', headers['www-authenticate']) + assert.are.equal("invalid_token", body.error) + assert.are.equal("The access token invalid", body.error_description) end) + it("should return 401 Unauthorized when token has expired", function() + local token = provision_token() + + -- Token expires in (5 seconds) + os.execute("sleep "..tonumber(6)) + + local response, status, headers = http_client.post(STUB_POST_URL, { }, {host = "oauth2.com", authorization = "bearer "..token.access_token}) + local body = cjson.decode(response) + assert.are.equal(401, status) + assert.are.equal(2, utils.table_size(body)) + assert.are.equal('Bearer realm="service" error="invalid_token" error_description="The access token expired"', headers['www-authenticate']) + assert.are.equal("invalid_token", body.error) + assert.are.equal("The access token expired", body.error_description) + end) end) describe("Refresh Token", function() @@ -741,10 +761,8 @@ describe("Authentication Plugin", function() local response, status = http_client.post(STUB_POST_URL, { }, {host = "oauth2.com", authorization = "bearer "..token.access_token}) local body = cjson.decode(response) - assert.are.equal(400, status) - assert.are.equal(2, utils.table_size(body)) - assert.are.equal("invalid_request", body.error) - assert.are.equal("access_token expired", body.error_description) + assert.are.equal(401, status) + assert.are.equal("The access token expired", body.error_description) -- Refreshing the token local response, status = http_client.post(PROXY_SSL_URL.."/oauth2/token", { refresh_token = token.refresh_token, client_id = "clientid123", client_secret = "secret123", grant_type = "refresh_token" }, {host = "oauth2.com"})