kong/spec/03-plugins/16-jwt/03-access_spec.lua (1,090 lines of code) (raw):

local cjson = require "cjson" local helpers = require "spec.helpers" local fixtures = require "spec.03-plugins.16-jwt.fixtures" local jwt_encoder = require "kong.plugins.jwt.jwt_parser" local utils = require "kong.tools.utils" local PAYLOAD = { iss = nil, nbf = os.time(), iat = os.time(), exp = os.time() + 3600 } for _, strategy in helpers.each_strategy() do describe("Plugin: jwt (access) [#" .. strategy .. "]", function() local jwt_secret local jwt_secret_2 local base64_jwt_secret local rsa_jwt_secret_1 local rsa_jwt_secret_2 local rsa_jwt_secret_3 local rsa_jwt_secret_4 local rsa_jwt_secret_5 local hs_jwt_secret_1 local hs_jwt_secret_2 local proxy_client local admin_client lazy_setup(function() local bp = helpers.get_db_utils(strategy, { "routes", "services", "plugins", "consumers", "jwt_secrets", }, { "ctx-checker", }) local routes = {} for i = 1, 13 do routes[i] = bp.routes:insert { hosts = { "jwt" .. i .. ".com" }, } end local route_grpc = assert(bp.routes:insert { protocols = { "grpc" }, paths = { "/hello.HelloService/" }, service = assert(bp.services:insert { name = "grpc", url = helpers.grpcbin_url, }), }) local consumers = bp.consumers local consumer1 = consumers:insert({ username = "jwt_tests_consumer" }) local consumer2 = consumers:insert({ username = "jwt_tests_base64_consumer" }) local consumer3 = consumers:insert({ username = "jwt_tests_rsa_consumer_1" }) local consumer4 = consumers:insert({ username = "jwt_tests_rsa_consumer_2" }) local consumer5 = consumers:insert({ username = "jwt_tests_rsa_consumer_5" }) local consumer6 = consumers:insert({ username = "jwt_tests_consumer_6" }) local consumer7 = consumers:insert({ username = "jwt_tests_hs_consumer_7" }) local consumer8 = consumers:insert({ username = "jwt_tests_hs_consumer_8" }) local consumer9 = consumers:insert({ username = "jwt_tests_rsa_consumer_9" }) local consumer10 = consumers:insert({ username = "jwt_tests_rsa_consumer_10"}) local anonymous_user = consumers:insert({ username = "no-body" }) local plugins = bp.plugins plugins:insert({ name = "jwt", route = { id = routes[1].id }, config = {}, }) plugins:insert({ name = "jwt", route = { id = routes[2].id }, config = { uri_param_names = { "token", "jwt" } }, }) plugins:insert({ name = "jwt", route = { id = routes[3].id }, config = { claims_to_verify = {"nbf", "exp"} }, }) plugins:insert({ name = "jwt", route = { id = routes[4].id }, config = { key_claim_name = "aud" }, }) plugins:insert({ name = "jwt", route = { id = routes[5].id }, config = { secret_is_base64 = true }, }) plugins:insert({ name = "jwt", route = { id = routes[6].id }, config = { anonymous = anonymous_user.id }, }) plugins:insert({ name = "jwt", route = { id = routes[7].id }, config = { anonymous = utils.uuid() }, }) plugins:insert({ name = "jwt", route = { id = routes[8].id }, config = { run_on_preflight = false }, }) plugins:insert({ name = "jwt", route = { id = routes[9].id }, config = { cookie_names = { "silly", "crumble" } }, }) plugins:insert({ name = "jwt", route = { id = routes[10].id }, config = { key_claim_name = "kid" }, }) plugins:insert({ name = "jwt", route = { id = routes[11].id }, config = { claims_to_verify = {"nbf", "exp"}, maximum_expiration = 300 }, }) plugins:insert({ name = "jwt", route = { id = routes[12].id }, config = { header_names = { "CustomAuthorization" } }, }) plugins:insert({ name = "jwt", route = { id = routes[13].id }, config = { anonymous = anonymous_user.username }, }) plugins:insert({ name = "ctx-checker", route = { id = routes[1].id }, config = { ctx_kind = "kong.ctx.shared", ctx_check_field = "authenticated_jwt_token" }, }) plugins:insert({ name = "jwt", route = { id = route_grpc.id }, config = {}, }) plugins:insert({ name = "ctx-checker", route = { id = route_grpc.id }, config = { ctx_kind = "kong.ctx.shared", ctx_check_field = "authenticated_jwt_token" }, }) jwt_secret = bp.jwt_secrets:insert { consumer = { id = consumer1.id } } jwt_secret_2 = bp.jwt_secrets:insert { consumer = { id = consumer6.id } } base64_jwt_secret = bp.jwt_secrets:insert { consumer = { id = consumer2.id } } rsa_jwt_secret_1 = bp.jwt_secrets:insert { consumer = { id = consumer3.id }, algorithm = "RS256", rsa_public_key = fixtures.rs256_public_key } rsa_jwt_secret_2 = bp.jwt_secrets:insert { consumer = { id = consumer4.id }, algorithm = "RS256", rsa_public_key = fixtures.rs256_public_key } rsa_jwt_secret_3 = bp.jwt_secrets:insert { consumer = { id = consumer5.id }, algorithm = "RS512", rsa_public_key = fixtures.rs512_public_key } rsa_jwt_secret_4 = bp.jwt_secrets:insert { consumer = { id = consumer9.id }, algorithm = "RS384", rsa_public_key = fixtures.rs384_public_key } rsa_jwt_secret_5 = bp.jwt_secrets:insert { consumer = { id = consumer10.id }, algorithm = "ES384", rsa_public_key = fixtures.es384_public_key } hs_jwt_secret_1 = bp.jwt_secrets:insert { consumer = { id = consumer7.id }, algorithm = "HS384", secret = fixtures.hs384_secret } hs_jwt_secret_2 = bp.jwt_secrets:insert { consumer = { id = consumer8.id }, algorithm = "HS512", secret = fixtures.hs512_secret } assert(helpers.start_kong { database = strategy, plugins = "bundled, ctx-checker", real_ip_header = "X-Forwarded-For", real_ip_recursive = "on", trusted_ips = "0.0.0.0/0, ::/0", nginx_conf = "spec/fixtures/custom_nginx.template", }) proxy_client = helpers.proxy_client() admin_client = helpers.admin_client() end) lazy_teardown(function() if proxy_client then proxy_client:close() end if admin_client then admin_client:close() end helpers.stop_kong() end) describe("refusals", function() it("returns 401 Unauthorized if no JWT is found in the request", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "jwt1.com", } }) assert.res_status(401, res) end) it("returns 401 if the claims do not contain the key to identify a secret", function() PAYLOAD.iss = nil local jwt = jwt_encoder.encode(PAYLOAD, "foo") local authorization = "Bearer " .. jwt local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = authorization, ["Host"] = "jwt1.com", } }) local body = assert.res_status(401, res) local json = cjson.decode(body) assert.same({ message = "No mandatory 'iss' in claims" }, json) end) it("returns 401 if the claims do not contain a valid key to identify a secret", function() PAYLOAD.iss = "" local jwt = jwt_encoder.encode(PAYLOAD, "foo") local authorization = "Bearer " .. jwt local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = authorization, ["Host"] = "jwt1.com", } }) local body = assert.res_status(401, res) local json = cjson.decode(body) assert.same({ message = "Invalid 'iss' in claims" }, json) end) it("returns 401 Unauthorized if the iss does not match a credential", function() PAYLOAD.iss = "123456789" local jwt = jwt_encoder.encode(PAYLOAD, jwt_secret.secret) local authorization = "Bearer " .. jwt local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = authorization, ["Host"] = "jwt1.com", } }) local body = assert.res_status(401, res) local json = cjson.decode(body) assert.same({ message = "No credentials found for given 'iss'" }, json) end) it("returns 401 Unauthorized if the signature is invalid", function() PAYLOAD.iss = jwt_secret.key local jwt = jwt_encoder.encode(PAYLOAD, "foo") local authorization = "Bearer " .. jwt local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = authorization, ["Host"] = "jwt1.com", } }) local body = assert.res_status(401, res) local json = cjson.decode(body) assert.same({ message = "Invalid signature" }, json) end) it("returns 401 Unauthorized if the alg does not match the credential", function() local header = {typ = "JWT", alg = 'RS256'} local jwt = jwt_encoder.encode(PAYLOAD, jwt_secret.secret, 'HS256', header) local authorization = "Bearer " .. jwt local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = authorization, ["Host"] = "jwt1.com", } }) local body = assert.res_status(401, res) local json = cjson.decode(body) assert.same({ message = "Invalid algorithm" }, json) end) it("returns 200 on OPTIONS requests if run_on_preflight is false", function() local res = assert(proxy_client:send { method = "OPTIONS", path = "/request", headers = { ["Host"] = "jwt8.com" } }) assert.res_status(200, res) end) it("returns Unauthorized on OPTIONS requests if run_on_preflight is true", function() local res = assert(proxy_client:send { method = "OPTIONS", path = "/request", headers = { ["Host"] = "jwt1.com" } }) local body = assert.res_status(401, res) assert.equal([[{"message":"Unauthorized"}]], body) end) it("returns 401 if the token exceeds the maximum allowed expiration limit", function() local payload = { iss = jwt_secret.key, exp = os.time() + 3600, nbf = os.time() - 30 } local jwt = jwt_encoder.encode(payload, jwt_secret.secret) local res = assert(proxy_client:send { method = "GET", path = "/request/?jwt=" .. jwt, headers = { ["Host"] = "jwt11.com" } }) local body = assert.res_status(401, res) assert.equal('{"exp":"exceeds maximum allowed expiration"}', body) end) it("accepts a JWT token within the maximum allowed expiration limit", function() local payload = { iss = jwt_secret.key, exp = os.time() + 270, nbf = os.time() - 30 } local jwt = jwt_encoder.encode(payload, jwt_secret.secret) local res = assert(proxy_client:send { method = "GET", path = "/request/?jwt=" .. jwt, headers = { ["Host"] = "jwt11.com" } }) assert.res_status(200, res) end) it("rejects gRPC call without credentials", function() local ok, err = helpers.proxy_client_grpc(){ service = "hello.HelloService.SayHello", opts = {}, } assert.falsy(ok) assert.match("Code: Unauthenticated", err) end) end) describe("HS256", function() it("proxies the request with token and consumer headers if it was verified", function() PAYLOAD.iss = jwt_secret.key local jwt = jwt_encoder.encode(PAYLOAD, jwt_secret.secret) local authorization = "Bearer " .. jwt local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = authorization, ["Host"] = "jwt1.com", } }) local body = cjson.decode(assert.res_status(200, res)) assert.equal(authorization, body.headers.authorization) assert.equal("jwt_tests_consumer", body.headers["x-consumer-username"]) assert.equal(jwt_secret.key, body.headers["x-credential-identifier"]) assert.is_nil(body.headers["x-anonymous-consumer"]) end) it("accepts gRPC call with credentials", function() PAYLOAD.iss = jwt_secret.key local jwt = jwt_encoder.encode(PAYLOAD, jwt_secret.secret) local authorization = "Bearer " .. jwt local ok, res = helpers.proxy_client_grpc(){ service = "hello.HelloService.SayHello", opts = { ["-H"] = ("'Authorization: %s'"):format(authorization), }, } assert.truthy(ok) assert.same({ reply = "hello noname" }, cjson.decode(res)) end) it("proxies the request if the key is found in headers", function() local header = {typ = "JWT", alg = "HS256", kid = jwt_secret_2.key} local jwt = jwt_encoder.encode(PAYLOAD, jwt_secret_2.secret, "HS256", header) local authorization = "Bearer " .. jwt local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = authorization, ["Host"] = "jwt10.com", } }) assert.res_status(200, res) end) it("proxies the request if secret key is stored in a field other than iss", function() PAYLOAD.aud = jwt_secret.key local jwt = jwt_encoder.encode(PAYLOAD, jwt_secret.secret) local authorization = "Bearer " .. jwt local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = authorization, ["Host"] = "jwt4.com" } }) local body = cjson.decode(assert.res_status(200, res)) assert.equal(authorization, body.headers.authorization) assert.equal("jwt_tests_consumer", body.headers["x-consumer-username"]) assert.equal(jwt_secret.key, body.headers["x-credential-identifier"]) end) it("proxies the request if secret is base64", function() PAYLOAD.iss = base64_jwt_secret.key local original_secret = base64_jwt_secret.secret local base64_secret = ngx.encode_base64(base64_jwt_secret.secret) local res = assert(admin_client:send { method = "PATCH", path = "/consumers/jwt_tests_base64_consumer/jwt/" .. base64_jwt_secret.id, body = { key = base64_jwt_secret.key, secret = base64_secret}, headers = { ["Content-Type"] = "application/json" } }) assert.res_status(200, res) local jwt = jwt_encoder.encode(PAYLOAD, original_secret) local authorization = "Bearer " .. jwt local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = authorization, ["Host"] = "jwt5.com" } }) local body = cjson.decode(assert.res_status(200, res)) assert.equal(authorization, body.headers.authorization) assert.equal("jwt_tests_base64_consumer", body.headers["x-consumer-username"]) assert.equal(base64_jwt_secret.key, body.headers["x-credential-identifier"]) end) it("returns 200 the JWT is found in the cookie crumble", function() PAYLOAD.iss = jwt_secret.key local jwt = jwt_encoder.encode(PAYLOAD, jwt_secret.secret) local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "jwt9.com", ["Cookie"] = "crumble=" .. jwt .. "; path=/;domain=.jwt9.com", } }) assert.res_status(200, res) end) it("returns 200 if the JWT is found in the cookie silly", function() PAYLOAD.iss = jwt_secret.key local jwt = jwt_encoder.encode(PAYLOAD, jwt_secret.secret) local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "jwt9.com", ["Cookie"] = "silly=" .. jwt .. "; path=/;domain=.jwt9.com", } }) assert.res_status(200, res) end) it("returns 401 if the JWT found in the cookie does not match a credential", function() PAYLOAD.iss = "incorrect-issuer" local jwt = jwt_encoder.encode(PAYLOAD, jwt_secret.secret) local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "jwt9.com", ["Cookie"] = "silly=" .. jwt .. "; path=/;domain=.jwt9.com", } }) local body = assert.res_status(401, res) local json = cjson.decode(body) assert.same({ message = "No credentials found for given 'iss'" }, json) end) it("returns a 401 if the JWT in the cookie is corrupted", function() PAYLOAD.iss = jwt_secret.key local jwt = "no-way-this-works" .. jwt_encoder.encode(PAYLOAD, jwt_secret.secret) local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "jwt9.com", ["Cookie"] = "silly=" .. jwt .. "; path=/;domain=.jwt9.com", } }) local body = assert.res_status(401, res) assert.equal([[{"message":"Bad token; invalid JSON"}]], body) end) it("reports a 200 without cookies but with a JWT token in the Authorization header", function() PAYLOAD.iss = jwt_secret.key local jwt = jwt_encoder.encode(PAYLOAD, jwt_secret.secret) local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "jwt9.com", ["Authorization"] = "Bearer " .. jwt, } }) assert.res_status(200, res) end) it("returns 401 if no JWT tokens are found in cookies or Authorization header", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "jwt9.com", } }) assert.res_status(401, res) end) it("returns 200 without cookies but with a JWT token in the CustomAuthorization header", function() PAYLOAD.iss = jwt_secret.key local jwt = jwt_encoder.encode(PAYLOAD, jwt_secret.secret) local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "jwt12.com", ["CustomAuthorization"] = "Bearer " .. jwt, } }) assert.res_status(200, res) end) it("finds the JWT in the first header occurrence of a duplicated custom authorization header", function() PAYLOAD.iss = jwt_secret.key local jwt = jwt_encoder.encode(PAYLOAD, jwt_secret.secret) local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "jwt12.com", ["CustomAuthorization"] = {"Bearer " .. jwt, "Bearer other-token"} } }) assert.res_status(200, res) end) it("finds the JWT if given in URL parameters", function() PAYLOAD.iss = jwt_secret.key local jwt = jwt_encoder.encode(PAYLOAD, jwt_secret.secret) local res = assert(proxy_client:send { method = "GET", path = "/request/?jwt=" .. jwt, headers = { ["Host"] = "jwt1.com", } }) assert.res_status(200, res) end) it("finds the JWT if given in a custom URL parameter", function() PAYLOAD.iss = jwt_secret.key local jwt = jwt_encoder.encode(PAYLOAD, jwt_secret.secret) local res = assert(proxy_client:send { method = "GET", path = "/request/?token=" .. jwt, headers = { ["Host"] = "jwt2.com", } }) assert.res_status(200, res) end) end) describe("RS256", function() it("verifies JWT", function() PAYLOAD.iss = rsa_jwt_secret_1.key local jwt = jwt_encoder.encode(PAYLOAD, fixtures.rs256_private_key, 'RS256') local authorization = "Bearer " .. jwt local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = authorization, ["Host"] = "jwt1.com" } }) local body = cjson.decode(assert.res_status(200, res)) assert.equal(authorization, body.headers.authorization) assert.equal("jwt_tests_rsa_consumer_1", body.headers["x-consumer-username"]) assert.equal(rsa_jwt_secret_1.key, body.headers["x-credential-identifier"]) end) it("identifies Consumer", function() PAYLOAD.iss = rsa_jwt_secret_2.key local jwt = jwt_encoder.encode(PAYLOAD, fixtures.rs256_private_key, 'RS256') local authorization = "Bearer " .. jwt local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = authorization, ["Host"] = "jwt1.com" } }) local body = cjson.decode(assert.res_status(200, res)) assert.equal(authorization, body.headers.authorization) assert.equal("jwt_tests_rsa_consumer_2", body.headers["x-consumer-username"]) assert.equal(rsa_jwt_secret_2.key, body.headers["x-credential-identifier"]) end) end) describe("RS512", function() it("verifies JWT", function() PAYLOAD.iss = rsa_jwt_secret_3.key local jwt = jwt_encoder.encode(PAYLOAD, fixtures.rs512_private_key, "RS512") local authorization = "Bearer " .. jwt local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = authorization, ["Host"] = "jwt1.com", } }) local body = cjson.decode(assert.res_status(200, res)) assert.equal(authorization, body.headers.authorization) assert.equal("jwt_tests_rsa_consumer_5", body.headers["x-consumer-username"]) assert.equal(rsa_jwt_secret_3.key, body.headers["x-credential-identifier"]) end) it("identifies Consumer", function() PAYLOAD.iss = rsa_jwt_secret_3.key local jwt = jwt_encoder.encode(PAYLOAD, fixtures.rs512_private_key, "RS512") local authorization = "Bearer " .. jwt local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = authorization, ["Host"] = "jwt1.com", } }) local body = cjson.decode(assert.res_status(200, res)) assert.equal(authorization, body.headers.authorization) assert.equal("jwt_tests_rsa_consumer_5", body.headers["x-consumer-username"]) assert.equal(rsa_jwt_secret_3.key, body.headers["x-credential-identifier"]) end) end) describe("RS384", function() it("verifies JWT", function() PAYLOAD.iss = rsa_jwt_secret_4.key local jwt = jwt_encoder.encode(PAYLOAD, fixtures.rs384_private_key, "RS384") local authorization = "Bearer " .. jwt local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = authorization, ["Host"] = "jwt1.com", } }) local body = cjson.decode(assert.res_status(200, res)) assert.equal(authorization, body.headers.authorization) assert.equal("jwt_tests_rsa_consumer_9", body.headers["x-consumer-username"]) assert.equal(rsa_jwt_secret_4.key, body.headers["x-credential-identifier"]) end) it("identifies Consumer", function() PAYLOAD.iss = rsa_jwt_secret_4.key local jwt = jwt_encoder.encode(PAYLOAD, fixtures.rs384_private_key, "RS384") local authorization = "Bearer " .. jwt local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = authorization, ["Host"] = "jwt1.com", } }) local body = cjson.decode(assert.res_status(200, res)) assert.equal(authorization, body.headers.authorization) assert.equal("jwt_tests_rsa_consumer_9", body.headers["x-consumer-username"]) assert.equal(rsa_jwt_secret_4.key, body.headers["x-credential-identifier"]) end) end) describe("ES384", function() it("verifies JWT", function() PAYLOAD.iss = rsa_jwt_secret_5.key local jwt = jwt_encoder.encode(PAYLOAD, fixtures.es384_private_key, "ES384") local authorization = "Bearer " .. jwt local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = authorization, ["Host"] = "jwt1.com", } }) local body = cjson.decode(assert.res_status(200, res)) assert.equal(authorization, body.headers.authorization) assert.equal("jwt_tests_rsa_consumer_10", body.headers["x-consumer-username"]) assert.equal(rsa_jwt_secret_5.key, body.headers["x-credential-identifier"]) end) it("identifies Consumer", function() PAYLOAD.iss = rsa_jwt_secret_5.key local jwt = jwt_encoder.encode(PAYLOAD, fixtures.es384_private_key, "ES384") local authorization = "Bearer " .. jwt local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = authorization, ["Host"] = "jwt1.com", } }) local body = cjson.decode(assert.res_status(200, res)) assert.equal(authorization, body.headers.authorization) assert.equal("jwt_tests_rsa_consumer_10", body.headers["x-consumer-username"]) assert.equal(rsa_jwt_secret_5.key, body.headers["x-credential-identifier"]) end) end) describe("HS386", function() it("proxies the request with token and consumer headers if it was verified", function() PAYLOAD.iss = hs_jwt_secret_1.key local jwt = jwt_encoder.encode(PAYLOAD, hs_jwt_secret_1.secret, "HS384") local authorization = "Bearer " .. jwt local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = authorization, ["Host"] = "jwt1.com", } }) local body = cjson.decode(assert.res_status(200, res)) assert.equal(authorization, body.headers.authorization) assert.equal("jwt_tests_hs_consumer_7", body.headers["x-consumer-username"]) assert.equal(hs_jwt_secret_1.key, body.headers["x-credential-identifier"]) assert.is_nil(body.headers["x-anonymous-consumer"]) end) end) describe("HS512", function() it("proxies the request with token and consumer headers if it was verified", function() PAYLOAD.iss = hs_jwt_secret_2.key local jwt = jwt_encoder.encode(PAYLOAD, hs_jwt_secret_2.secret, "HS512") local authorization = "Bearer " .. jwt local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = authorization, ["Host"] = "jwt1.com", } }) local body = cjson.decode(assert.res_status(200, res)) assert.equal(authorization, body.headers.authorization) assert.equal("jwt_tests_hs_consumer_8", body.headers["x-consumer-username"]) assert.equal(hs_jwt_secret_2.key, body.headers["x-credential-identifier"]) assert.is_nil(body.headers["x-anonymous-consumer"]) end) end) describe("JWT private claims checks", function() it("requires the checked fields to be in the claims", function() local payload = { iss = jwt_secret.key } local jwt = jwt_encoder.encode(payload, jwt_secret.secret) local res = assert(proxy_client:send { method = "GET", path = "/request/?jwt=" .. jwt, headers = { ["Host"] = "jwt3.com" } }) local body = cjson.decode(assert.res_status(401, res)) assert.same({ nbf="must be a number", exp="must be a number" }, body) end) it("checks if the fields are valid: `exp` claim", function() local payload = { iss = jwt_secret.key, exp = os.time() - 10, nbf = os.time() - 10 } local jwt = jwt_encoder.encode(payload, jwt_secret.secret) local res = assert(proxy_client:send { method = "GET", path = "/request/?jwt=" .. jwt, headers = { ["Host"] = "jwt3.com" } }) local body = assert.res_status(401, res) assert.equal('{"exp":"token expired"}', body) end) it("checks if the fields are valid: `nbf` claim", function() local payload = { iss = jwt_secret.key, exp = os.time() + 10, nbf = os.time() + 5 } local jwt = jwt_encoder.encode(payload, jwt_secret.secret) local res = assert(proxy_client:send { method = "GET", path = "/request/?jwt=" .. jwt, headers = { ["Host"] = "jwt3.com" } }) local body = assert.res_status(401, res) assert.equal('{"nbf":"token not valid yet"}', body) end) end) describe("ctx.authenticated_jwt_token", function() it("is added to kong.ctx.shared when authenticated", function() PAYLOAD.iss = jwt_secret.key local jwt = jwt_encoder.encode(PAYLOAD, jwt_secret.secret) local authorization = "Bearer " .. jwt local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = authorization, ["Host"] = "jwt1.com", } }) assert.res_status(200, res) local header = assert.header("ctx-checker-authenticated-jwt-token", res) assert.equal(jwt, header) end) end) describe("config.anonymous", function() it("works with right credentials and anonymous", function() PAYLOAD.iss = jwt_secret.key local jwt = jwt_encoder.encode(PAYLOAD, jwt_secret.secret) local authorization = "Bearer " .. jwt local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Authorization"] = authorization, ["Host"] = "jwt6.com" } }) local body = cjson.decode(assert.res_status(200, res)) assert.equal('jwt_tests_consumer', body.headers["x-consumer-username"]) assert.equal(jwt_secret.key, body.headers["x-credential-identifier"]) assert.is_nil(body.headers["x-anonymous-consumer"]) end) it("works with wrong credentials and anonymous", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "jwt6.com" } }) local body = cjson.decode(assert.res_status(200, res)) assert.equal('true', body.headers["x-anonymous-consumer"]) assert.equal('no-body', body.headers["x-consumer-username"]) assert.equal(nil, body.headers["x-credential-identifier"]) end) it("works with wrong credentials and username in anonymous", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "jwt13.com" } }) local body = cjson.decode(assert.res_status(200, res)) assert.equal('true', body.headers["x-anonymous-consumer"]) assert.equal('no-body', body.headers["x-consumer-username"]) assert.equal(nil, body.headers["x-credential-identifier"]) end) it("errors when anonymous user doesn't exist", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "jwt7.com" } }) assert.response(res).has.status(500) end) end) end) describe("Plugin: jwt (access) [#" .. strategy .. "]", function() local client local user1 local user2 local anonymous local jwt_token local key_auth lazy_setup(function() local bp = helpers.get_db_utils(strategy, { "routes", "services", "plugins", "consumers", "jwt_secrets", "keyauth_credentials", }) local service1 = bp.services:insert({ path = "/request" }) local route1 = bp.routes:insert { hosts = { "logical-and.com" }, service = service1, } bp.plugins:insert { name = "jwt", route = { id = route1.id }, } bp.plugins:insert { name = "key-auth", route = { id = route1.id }, } anonymous = bp.consumers:insert { username = "Anonymous", } user1 = bp.consumers:insert { username = "Mickey", } user2 = bp.consumers:insert { username = "Aladdin", } local service2 = bp.services:insert({ path = "/request" }) local route2 = bp.routes:insert { hosts = { "logical-or.com" }, service = service2, } bp.plugins:insert { name = "jwt", route = { id = route2.id }, config = { anonymous = anonymous.id, }, } bp.plugins:insert { name = "key-auth", route = { id = route2.id }, config = { anonymous = anonymous.id, }, } key_auth = bp.keyauth_credentials:insert { key = "Mouse", consumer = { id = user1.id }, } local jwt_secret = bp.jwt_secrets:insert { consumer = { id = user2.id }, } PAYLOAD.iss = jwt_secret.key jwt_token = "Bearer " .. jwt_encoder.encode(PAYLOAD, jwt_secret.secret) assert(helpers.start_kong({ database = strategy, nginx_conf = "spec/fixtures/custom_nginx.template", })) client = helpers.proxy_client() end) lazy_teardown(function() if client then client:close() end helpers.stop_kong() end) describe("multiple auth without anonymous, logical AND", function() it("passes with all credentials provided", function() local res = assert(client:send { method = "GET", path = "/request", headers = { ["Host"] = "logical-and.com", ["apikey"] = "Mouse", ["Authorization"] = jwt_token, } }) assert.response(res).has.status(200) assert.request(res).has.no.header("x-anonymous-consumer") local id = assert.request(res).has.header("x-consumer-id") local key = assert.request(res).has.header("x-credential-identifier") assert.not_equal(id, anonymous.id) assert(id == user1.id or id == user2.id) assert.equal(key_auth.id, key) end) it("fails 401, with only the first credential provided", function() local res = assert(client:send { method = "GET", path = "/request", headers = { ["Host"] = "logical-and.com", ["apikey"] = "Mouse", } }) assert.response(res).has.status(401) end) it("fails 401, with only the second credential provided", function() local res = assert(client:send { method = "GET", path = "/request", headers = { ["Host"] = "logical-and.com", ["Authorization"] = jwt_token, } }) assert.response(res).has.status(401) end) it("fails 401, with no credential provided", function() local res = assert(client:send { method = "GET", path = "/request", headers = { ["Host"] = "logical-and.com", } }) assert.response(res).has.status(401) end) end) describe("multiple auth with anonymous, logical OR", function() it("passes with all credentials provided", function() local res = assert(client:send { method = "GET", path = "/request", headers = { ["Host"] = "logical-or.com", ["apikey"] = "Mouse", ["Authorization"] = jwt_token, } }) assert.response(res).has.status(200) assert.request(res).has.no.header("x-anonymous-consumer") local id = assert.request(res).has.header("x-consumer-id") local key = assert.request(res).has.header("x-credential-identifier") assert.not_equal(id, anonymous.id) assert(id == user1.id or id == user2.id) assert.equal(PAYLOAD.iss, key) end) it("passes with only the first credential provided", function() local res = assert(client:send { method = "GET", path = "/request", headers = { ["Host"] = "logical-or.com", ["apikey"] = "Mouse", } }) assert.response(res).has.status(200) assert.request(res).has.no.header("x-anonymous-consumer") local id = assert.request(res).has.header("x-consumer-id") assert.not_equal(id, anonymous.id) assert.equal(user1.id, id) assert.not_equal(PAYLOAD.iss, res.headers["x-credential-identifier"]) end) it("passes with only the second credential provided", function() local res = assert(client:send { method = "GET", path = "/request", headers = { ["Host"] = "logical-or.com", ["Authorization"] = jwt_token, } }) assert.response(res).has.status(200) assert.request(res).has.no.header("x-anonymous-consumer") local id = assert.request(res).has.header("x-consumer-id") local key = assert.request(res).has.header("x-credential-identifier") assert.not_equal(id, anonymous.id) assert.equal(user2.id, id) assert.equal(PAYLOAD.iss, key) end) it("passes with no credential provided", function() local res = assert(client:send { method = "GET", path = "/request", headers = { ["Host"] = "logical-or.com", } }) assert.response(res).has.status(200) assert.request(res).has.header("x-anonymous-consumer") local id = assert.request(res).has.header("x-consumer-id") assert.not_equal(PAYLOAD.iss, res.headers["x-credential-identifier"]) assert.equal(id, anonymous.id) end) end) end) end