diff --git a/changelog/unreleased/kong/plugins-remote-auth.yml b/changelog/unreleased/kong/plugins-remote-auth.yml new file mode 100644 index 000000000000..6bd5c1b832af --- /dev/null +++ b/changelog/unreleased/kong/plugins-remote-auth.yml @@ -0,0 +1,4 @@ +message: | + "**remote-auth**: Add a new plugin to authenticate requests via remote server +type: "feature" +scope: "Plugin" diff --git a/kong-3.10.0-0.rockspec b/kong-3.10.0-0.rockspec index 22e19b4fefe9..6d57e8ba2659 100644 --- a/kong-3.10.0-0.rockspec +++ b/kong-3.10.0-0.rockspec @@ -680,6 +680,10 @@ build = { ["kong.plugins.redirect.handler"] = "kong/plugins/redirect/handler.lua", ["kong.plugins.redirect.schema"] = "kong/plugins/redirect/schema.lua", + ["kong.plugins.remote-auth.access"] = "kong/plugins/remote-auth/access.lua", + ["kong.plugins.remote-auth.handler"] = "kong/plugins/remote-auth/handler.lua", + ["kong.plugins.remote-auth.schema"] = "kong/plugins/remote-auth/schema.lua", + ["kong.vaults.env"] = "kong/vaults/env/init.lua", ["kong.vaults.env.schema"] = "kong/vaults/env/schema.lua", diff --git a/kong/constants.lua b/kong/constants.lua index 4b04b01ce3fa..0b710b088718 100644 --- a/kong/constants.lua +++ b/kong/constants.lua @@ -43,7 +43,8 @@ local plugins = { "ai-request-transformer", "ai-response-transformer", "standard-webhooks", - "redirect" + "redirect", + "remote-auth", } local plugin_map = {} diff --git a/kong/plugins/remote-auth/access.lua b/kong/plugins/remote-auth/access.lua new file mode 100644 index 000000000000..4e734ac3dba1 --- /dev/null +++ b/kong/plugins/remote-auth/access.lua @@ -0,0 +1,173 @@ +local http = require "resty.http" +local url = require "socket.url" +local jwt_decoder = require "kong.plugins.jwt.jwt_parser" + + +local kong = kong +local _M = {} +local fmt = string.format + +local function unauthorized(message) + return { status = 401, message = message } +end + +local function bad_gateway(message) + return { status = 502, message = message } +end + +local parsed_urls_cache = {} +local function parse_url(host_url) + local parsed_url = parsed_urls_cache[host_url] + + if parsed_url then + return parsed_url + end + + parsed_url = url.parse(host_url) + if not parsed_url.port then + if parsed_url.scheme == "http" then + parsed_url.port = 80 + elseif parsed_url.scheme == "https" then + parsed_url.port = 443 + end + end + if not parsed_url.path then + parsed_url.path = "/" + end + + parsed_urls_cache[host_url] = parsed_url + + return parsed_url +end + +local function request_auth(conf, request_token) + local method = conf.auth_request_method + local timeout = conf.auth_request_timeout + local keepalive = conf.auth_request_keepalive + local parsed_url = parse_url(conf.auth_request_url) + local request_header = conf.auth_request_token_header + local response_token_header = conf.auth_response_token_header + local host = parsed_url.host + local port = tonumber(parsed_url.port) + + local httpc = http.new() + httpc:set_timeout(timeout) + + local headers = { + [request_header] = request_token + } + + if conf.auth_request_headers then + for h, v in pairs(conf.headers) do + headers[h] = headers[h] or v + end + end + + local auth_server_url = fmt("%s://%s:%d%s", parsed_url.scheme, host, port, parsed_url.path) + local res, err = httpc:request_uri(auth_server_url, { + method = method, + headers = headers, + keepalive_timeout = keepalive, + }) + if not res then + return nil, "failed request to " .. host .. ":" .. tostring(port) .. ": " .. err + end + + if res.status >= 300 then + return nil, "authentication failed with status: " .. res.status + end + + local token = res.headers[response_token_header] + return token, nil +end + +local function validate_token(token, public_key, max_expiration) + if not token then + return false, nil + end + + local jwt, err = jwt_decoder:new(token) + if err then + return false, unauthorized("JWT - Bad token; " .. tostring(err)) + end + + -- Verify JWT signature + if not jwt:verify_signature(public_key) then + return false, unauthorized("JWT - Invalid signature") + end + + -- Verify the JWT expiration + if max_expiration ~= nil and max_expiration > 0 then + local _, errs = jwt:verify_registered_claims({ "exp" }) + if errs then + return false, unauthorized("JWT - Token Expired") + end + _, errs = jwt:check_maximum_expiration(max_expiration) + if errs then + return false, unauthorized("JWT - Token Expiry Exceeds Maximum - " .. tostring(errs)) + end + end + + return true, nil +end + +local function authenticate(conf) + local request_header = conf.consumer_auth_header + local request_token = kong.request.get_header(request_header) + + -- If the header is missing, then reject the request + if not request_token then + return unauthorized("Missing Token, Unauthorized") + end + + -- Make remote request to check credentials + local auth_token, err = request_auth(conf, request_token) + if err then + return unauthorized("Unauthorized: " .. err) + end + + -- set header in forwarded request + if auth_token then + _, err = validate_token(auth_token, conf.jwt_public_key, conf.jwt_max_expiration) + if err then + return bad_gateway(err.message) + end + + local service_auth_header = conf.service_auth_header + local service_token_prefix = conf.service_auth_header_value_prefix + local header_value = auth_token + if service_token_prefix then + header_value = service_token_prefix .. auth_token + end + kong.service.request.set_header(service_auth_header, header_value) + kong.response.set_header(conf.auth_response_token_header, auth_token) + else + return bad_gateway("Upsteam Authentication server returned an empty response") + end +end + + +function _M.authenticate(conf) + -- Check if the request has a valid JWT + local authenticated, err = validate_token( + kong.request.get_header(conf.request_authentication_header), + conf.jwt_public_key, + conf.jwt_max_expiration + ) + if err then + kong.response.error(err.status, err.message, err.headers) + return + end + -- If the request is authenticated, then we don't need to re-authenticate + if authenticated then + return + end + + -- Unauthenticated request needs to be authenticated. + err = authenticate(conf) + if err then + kong.response.error(err.status, err.message, err.headers) + end +end + +return _M diff --git a/kong/plugins/remote-auth/handler.lua b/kong/plugins/remote-auth/handler.lua new file mode 100644 index 000000000000..3fb1bd2625da --- /dev/null +++ b/kong/plugins/remote-auth/handler.lua @@ -0,0 +1,12 @@ +local access = require "kong.plugins.remote-auth.access" + +local plugin = { + PRIORITY = 1100, + VERSION = "0.1.0", +} + +function plugin:access(plugin_conf) + access.authenticate(plugin_conf) +end + +return plugin diff --git a/kong/plugins/remote-auth/schema.lua b/kong/plugins/remote-auth/schema.lua new file mode 100644 index 000000000000..a8afa0f4e6b9 --- /dev/null +++ b/kong/plugins/remote-auth/schema.lua @@ -0,0 +1,132 @@ +local typedefs = require "kong.db.schema.typedefs" + +local PLUGIN_NAME = "remote-auth" + + +local schema = { + name = PLUGIN_NAME, + fields = { + { consumer = typedefs.no_consumer }, -- this plugin cannot be configured on a consumer (typical for auth plugins) + { protocols = typedefs.protocols_http }, -- http protocols only + { + config = { + type = "record", + fields = { + { + auth_request_url = typedefs.url { + required = true, + } + }, + { + consumer_auth_header = typedefs.header_name { + required = true, + default = "Authorization", + } + }, + { + auth_request_method = typedefs.http_method { + required = true, + default = "POST", + } + }, + { + auth_request_timeout = typedefs.timeout { + required = true, + default = 10000, + } + }, + { + auth_request_keepalive = { + type = "number", + default = 60000, + required = true, + description = + "A value in milliseconds that defines how long an idle connection will live before being closed.", + } + }, + { + auth_request_token_header = typedefs.header_name { + required = true, + default = "Authorization", + } + }, + { + auth_response_token_header = typedefs.header_name { + required = true, + default = "X-Token" + } + }, + { + auth_request_headers = { + description = + "An optional table of headers included in the HTTP message to the upstream server. Values are indexed by header name, and each header name accepts a single string.", + type = "map", + required = false, + keys = typedefs.header_name { + match_none = { + { + pattern = "^[Hh][Oo][Ss][Tt]$", + err = "cannot contain 'Host' header", + }, + { + pattern = "^[Cc][Oo][Nn][Tt][Ee][Nn][Tt]%-[Ll][Ee][nn][Gg][Tt][Hh]$", + err = "cannot contain 'Content-Length' header", + }, + { + pattern = "^[Cc][Oo][Nn][Tt][Ee][Nn][Tt]%-[Tt][Yy][Pp][Ee]$", + err = "cannot contain 'Content-Type' header", + }, + }, + }, + values = { + type = "string", + referenceable = true, + }, + } + }, + { + service_auth_header = typedefs.header_name { + required = true, + default = "Authorization", + } + }, + { + service_auth_header_value_prefix = { + type = "string", + default = "bearer ", + required = true, + description = "A header value prefix for the upstream service request header value.", + } + }, + { + request_authentication_header = typedefs.header_name { + required = true, + default = "X-Token", + } + }, + { + jwt_public_key = { + type = "string", + required = true, + description = "The public key used to verify the siguration of issued JWT tokens", + } + }, + { + jwt_max_expiration = { + description = + "A value between 0 and 31536000 (365 days) limiting the lifetime of the JWT to maximum_expiration seconds in the future.", + type = "number", + between = { 0, 31536000 }, + required = false, + default = 0, + } + }, + + }, + entity_checks = {}, + }, + }, + }, +} + +return schema diff --git a/spec/03-plugins/46-remote-auth/01-schema_spec.lua b/spec/03-plugins/46-remote-auth/01-schema_spec.lua new file mode 100644 index 000000000000..dafd802d2181 --- /dev/null +++ b/spec/03-plugins/46-remote-auth/01-schema_spec.lua @@ -0,0 +1,83 @@ +local PLUGIN_NAME = "remote-auth" + + +-- helper function to validate data against a schema +local validate +do + local validate_entity = require("spec.helpers").validate_plugin_config_schema + local plugin_schema = require("kong.plugins." .. PLUGIN_NAME .. ".schema") + + function validate(data) + return validate_entity(data, plugin_schema) + end +end + + +describe(PLUGIN_NAME .. ": (schema)", function() + it("accepts minimal required configuration", function() + local ok, err = validate({ + auth_request_url = "http://example.com/auth", + jwt_public_key = "sample-public-key", + }) + assert.is_nil(err) + assert.is_truthy(ok) + end) + + it("accepts all configuration values", function() + local ok, err = validate({ + auth_request_url = "http://example.com/auth", + consumer_auth_header = "X-Test-Header", + auth_request_method = "FOO", + auth_request_timeout = 100, + auth_request_keepalive = 20000, + auth_request_token_header = "Authorization", + auth_response_token_header = "X-Auth", + auth_request_headers = { + ["X-Who-Am-I"] = "remote-auth", + }, + service_auth_header = "Example-Header", + service_auth_header_value_prefix = "token ", + jwt_public_key = "foobarbaz", + jwt_max_expiration = 100000, + request_authentication_header = "X-Testing", + }) + assert.is_nil(err) + assert.is_truthy(ok) + end) + + it("rejects empty header values", function() + local ok, err = validate({ + auth_request_url = "http://example.com/auth", + auth_request_headers = { + ["X-Who-Am-I"] = "", + }, + jwt_public_key = "testing", + }) + assert.same({ + config = { + auth_request_headers = "length must be at least 1", + } + }, err) + assert.is_falsy(ok) + end) + + local blacklisted_headers = { "Host", "Content-Type", "Content-Length" } + for _, header in pairs(blacklisted_headers) do + it("rejects blacklisted Header (" .. header .. ")", function() + local ok, err = validate({ + auth_request_url = "http://example.com/auth", + auth_request_headers = { + ["X-Who-Am-I"] = "123", + [header] = "FooBar", + }, + jwt_public_key = "testing", + }) + assert.same({ + config = { + auth_request_headers = "cannot contain '" .. header .. "' header" + } + }, err) + assert.is_falsy(ok) + end) + end +end) diff --git a/spec/03-plugins/46-remote-auth/02-unit_spec.lua b/spec/03-plugins/46-remote-auth/02-unit_spec.lua new file mode 100644 index 000000000000..25dbfc73a836 --- /dev/null +++ b/spec/03-plugins/46-remote-auth/02-unit_spec.lua @@ -0,0 +1,180 @@ +local jwt_parser = require "kong.plugins.jwt.jwt_parser" +local fixtures = require "spec.remote-auth.fixtures" + + +local PLUGIN_NAME = "remote-auth" + +describe(PLUGIN_NAME .. ": (unit) ", function() + local plugin, config + local actual_require + local mock_auth_response_status + local mock_calls + local mock_request_header_value + local mock_auth_response_headers + local request_header_name + + local function append_call(key, item) + mock_calls[key][#mock_calls[key] + 1] = item + end + + setup(function() + mock_calls = { + response_error = {}, + response_set_headers = {}, + service_request_set_headers = {}, + auth_request = {}, + auth_set_timeout = {}, + } + mock_auth_response_headers = {} + actual_require = _G.require + _G.kong = { -- mock the basic Kong function we use in our plugin + request = { + get_header = function(name) + if name == request_header_name then + return mock_request_header_value + end + return nil + end, + }, + response = { + error = function(status, message, headers) + append_call( + "response_error", + { status = status, message = message, headers = headers } + ) + end, + set_header = function(name, value) + append_call( + "response_set_headers", + { name = name, value = value } + ) + end, + }, + service = { + request = { + set_header = function(name, value) + append_call("service_request_set_headers", { name = name, value = value }) + end, + } + } + } + _G.require = function(modname) + if modname == "resty.http" then + return { + new = function() + local http = {} + http.set_timeout = function(_, timeout) + append_call("auth_set_timeout", { timeout = timeout }) + end + http.request_uri = function(_, url, opts) + append_call("auth_request", { url = url, opts = opts }) + + if mock_auth_response_status then + return { + status = mock_auth_response_status, + headers = mock_auth_response_headers + } + else + return nil + end + end + return http + end + } + else + -- For anything else, return actual. + return actual_require(modname) + end + end + + -- load the plugin code + plugin = require("kong.plugins." .. PLUGIN_NAME .. ".handler") + end) + + teardown(function() + _G.require = actual_require + end) + + + before_each(function() + request_header_name = "X-Auth" + -- clear the upvalues to prevent test results mixing between tests + config = { + auth_request_url = "http://127.0.0.1:2101/auth", + consumer_auth_header = "X-Auth", + auth_response_token_header = "X-Token", + auth_request_token_header = "Authorization", + auth_request_method = "POST", + auth_request_keepalive = 10000, + auth_request_timeout = 2000, + service_auth_header = "X-Auth", + jwt_public_key = fixtures.es512_public_key, + request_authentication_header = "X-Token", + } + end) + + after_each(function() + mock_calls = { + response_error = {}, + response_set_headers = {}, + service_request_set_headers = {}, + auth_request = {}, + auth_set_timeout = {}, + } + mock_auth_response_status = nil + mock_request_header_value = nil + mock_auth_response_headers = {} + end) + + describe("Success -", function() + local token = jwt_parser.encode({ + name = "foobar", + }, fixtures.es512_private_key, 'ES512') + + before_each(function() + mock_auth_response_status = 200 + mock_request_header_value = "asdf1234" + mock_auth_response_headers = { + ["X-Token"] = token + } + end) + it("calls authentication api when token exists", function() + plugin:access(config) + assert.same( + { { + url = "http://127.0.0.1:2101/auth", + opts = { + method = "POST", + headers = { Authorization = "asdf1234" }, + keepalive_timeout = 10000, + } + } }, + mock_calls["auth_request"] + ) + assert.same(mock_calls["response_error"], {}) + end) + + it("sets headers on successful auth call", function() + plugin:access(config) + assert.same(mock_calls["service_request_set_headers"], { { name = "X-Auth", value = token } }) + assert.same({}, mock_calls["response_error"]) + assert.same(mock_calls["response_set_headers"], { { name = "X-Token", value = token } }) + end) + + it("sets timeout on auth call", function() + plugin:access(config) + assert.same({ { timeout = 2000 } }, mock_calls["auth_set_timeout"]) + assert.same({}, mock_calls["response_error"]) + end) + end) + describe("Failure:", function() + it("rejects missing token header", function() + plugin:access(config) + assert.same( + { { status = 401, message = "Missing Token, Unauthorized" } }, + mock_calls["response_error"] + ) + assert.same({}, mock_calls["response_set_headers"]) + end) + end) +end) diff --git a/spec/03-plugins/46-remote-auth/10-integration_spec.lua b/spec/03-plugins/46-remote-auth/10-integration_spec.lua new file mode 100644 index 000000000000..3b3dd3c02384 --- /dev/null +++ b/spec/03-plugins/46-remote-auth/10-integration_spec.lua @@ -0,0 +1,290 @@ +local cjson = require "cjson" +local jwt_parser = require "kong.plugins.jwt.jwt_parser" +local helpers = require "spec.helpers" +local http_mock = require "spec.helpers.http_mock" +local fixtures = require "spec.remote-auth.fixtures" + + +local PLUGIN_NAME = "remote-auth" + + +local mock_http_server_port = helpers.get_available_port() +local port_missing_auth_server = helpers.get_available_port() + +local mock = http_mock.new("127.0.0.1:" .. mock_http_server_port, { + ["/auth"] = { + access = [[ + local jwt_parser = require "kong.plugins.jwt.jwt_parser" + local fixtures = require "spec.remote-auth.fixtures" + local json = require "cjson" + local method = ngx.req.get_method() + local uri = ngx.var.request_uri + local headers = ngx.req.get_headers(nil, true) + local token_header = headers["Authorization"] + ngx.header["X-Token"] = jwt_parser.encode({ + name = "foobar", + exp = os.time() + 1000, + }, fixtures.es512_private_key, 'ES512') + ngx.say(json.encode({ + uri = uri, + method = method, + headers = headers, + body = body, + status = 200, + })) + ]] + }, + ["/auth-reject"] = { + access = [[ + ngx.status = 401 + ngx.say("unauthorized") + ]] + }, + ["/auth-missing"] = { + access = [[ + ngx.status = 200 + ngx.say("Ok") + ]] + }, + ["/auth-expired"] = { + access = [[ + local jwt_parser = require "kong.plugins.jwt.jwt_parser" + local fixtures = require "spec.remote-auth.fixtures" + + ngx.status = 200 + ngx.header["X-Token"] = jwt_parser.encode({ + name = "foobar", + exp = os.time() - 100, + }, fixtures.es512_private_key, 'ES512') + ngx.say("Success") + ]] + }, + }, + nil, + { + log_opts = { + req = true, + req_body = true, + req_body_large = true, + } + } +) + + +for _, strategy in helpers.all_strategies() do + describe(PLUGIN_NAME .. ": (access) [#" .. strategy .. "]", function() + local client + + lazy_setup(function() + local bp = helpers.get_db_utils(strategy, { "routes", "services", "plugins" }, { PLUGIN_NAME }) + -- Inject a test route. No need to create a service, there is a default + -- service which will echo the request. + local route1 = bp.routes:insert({ + hosts = { "test1.com" }, + }) + local route2 = bp.routes:insert({ + hosts = { "test2.com" }, + }) + local route3 = bp.routes:insert({ + hosts = { "test3.com" }, + }) + local route4 = bp.routes:insert({ + hosts = { "test4.com" }, + }) + local route5 = bp.routes:insert({ + hosts = { "test5.com" }, + }) + local route6 = bp.routes:insert({ + hosts = { "test6.com" }, + }) + + -- add the plugin to test to the route we created + bp.plugins:insert { + name = PLUGIN_NAME, + route = { id = route1.id }, + config = { + auth_request_url = "http://127.0.0.1:" .. mock_http_server_port .. "/auth", + jwt_public_key = fixtures.es512_public_key, + }, + } + bp.plugins:insert { + name = PLUGIN_NAME, + route = { id = route2.id }, + config = { + auth_request_url = "http://127.0.0.1:" .. mock_http_server_port .. "/auth-reject", + jwt_public_key = fixtures.es512_public_key, + }, + } + bp.plugins:insert { + name = PLUGIN_NAME, + route = { id = route3.id }, + config = { + auth_request_url = "http://127.0.0.1:" .. port_missing_auth_server .. "/auth-reject", + jwt_public_key = fixtures.es512_public_key, + }, + } + bp.plugins:insert { + name = PLUGIN_NAME, + route = { id = route4.id }, + config = { + auth_request_url = "http://127.0.0.1:" .. mock_http_server_port .. "/auth-missing", + jwt_public_key = fixtures.es512_public_key, + }, + } + bp.plugins:insert { + name = PLUGIN_NAME, + route = { id = route5.id }, + config = { + auth_request_url = "http://127.0.0.1:" .. mock_http_server_port .. "/auth", + jwt_public_key = fixtures.rs256_public_key, + } + } + bp.plugins:insert { + name = PLUGIN_NAME, + route = { id = route6.id }, + config = { + auth_request_url = "http://127.0.0.1:" .. mock_http_server_port .. "/auth-expired", + jwt_public_key = fixtures.es512_public_key, + jwt_max_expiration = 100, + } + } + assert(helpers.start_kong({ + database = strategy, + nginx_conf = "spec/fixtures/custom_nginx.template", + declarative_config = strategy == "off" and helpers.make_yaml_file() or nil, + plugins = PLUGIN_NAME, + })) + assert(mock:start()) + end) + + lazy_teardown(function() + helpers.stop_kong(nil, true) + assert(mock:stop()) + end) + + before_each(function() + client = helpers.proxy_client() + end) + + after_each(function() + mock:clean() + if client then client:close() end + mock.client = nil + end) + + + describe("Authorized: ", function() + it("Can get token and validate it", function() + local res = client:get("/", { + headers = { + Host = "test1.com", + Authorization = "test", + } + }) + + assert.response(res).has.status(200) + + -- assert that the mock got the right header + local logs = mock:retrieve_mocking_logs() + local req = assert(logs[#logs].req) + assert.same(req.headers["Authorization"], "test") + + -- assert that the response contains the jwt token + local header_value = assert.response(res).has.header("X-Token") + local jwt = assert(jwt_parser:new(header_value)) + assert.True(jwt:verify_signature(fixtures.es512_public_key)) + end) + end) + + + + describe("Unauthorized:", function() + it("Rejects a missing token", function() + local r = client:get("/", { + headers = { + host = "test1.com", + } + }) + -- validate that the request succeeded, response status 200 + local body = assert.response(r).has.status(401) + local json = cjson.decode(body) + + assert.same("Missing Token, Unauthorized", json.message) + end) + + it("Rejects an invalid token", function() + local r = client:get("/", { + headers = { + host = "test2.com", + Authorization = "test", + } + }) + -- validate that the request succeeded, response status 200 + local body = assert.response(r).has.status(401) + local json = cjson.decode(body) + + assert.same("Unauthorized: authentication failed with status: 401", json.message) + end) + + it("Rejects when authentication server is unavailable", function() + local r = client:get("/", { + headers = { + host = "test3.com", + Authorization = "test", + } + }) + -- validate that the request succeeded, response status 200 + local body = assert.response(r).has.status(401) + local json = cjson.decode(body) + + assert.same( + "Unauthorized: failed request to 127.0.0.1:" .. port_missing_auth_server .. ": connection refused", + json.message + ) + end) + + it("Rejects when authentication server provides empty token", function() + local r = client:get("/", { + headers = { + host = "test4.com", + Authorization = "test", + } + }) + -- validate that the request succeeded, response status 200 + local body = assert.response(r).has.status(502) + local json = cjson.decode(body) + + assert.same( + "Upsteam Authentication server returned an empty response", + json.message + ) + end) + + it("Rejects with invalid public_key", function() + local r = client:get("/", { + headers = { + host = "test5.com", + Authorization = "test", + } + }) + -- validate that the request succeeded, response status 200 + local body = assert.response(r).has.status(502) + local json = cjson.decode(body) + assert.same("JWT - Invalid signature", json.message) + end) + + it("Rejects with invalid expireation time", function() + local r = client:get("/", { + headers = { + host = "test6.com", + Authorization = "test", + } + }) + -- validate that the request succeeded, response status 200 + local body = assert.response(r).has.status(502) + local json = cjson.decode(body) + assert.same("JWT - Token Expired", json.message) + end) + end) + end) +end diff --git a/spec/03-plugins/46-remote-auth/fixtures.lua b/spec/03-plugins/46-remote-auth/fixtures.lua new file mode 100644 index 000000000000..acc4d15defdc --- /dev/null +++ b/spec/03-plugins/46-remote-auth/fixtures.lua @@ -0,0 +1,31 @@ +return { + rs256_public_key = [[ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw5mp3MS3hVLkHwB9lMrE +x34MjYCmKeH/XeMLexNpTd1FzuNv6rArovTY763CDo1Tp0xHz0LPlDJJtpqAgsnf +DwCcgn6ddZTo1u7XYzgEDfS8J4SYdcKxZiSdVTpb9k7pByXfnwK/fwq5oeBAJXIS +v5ZLB1IEVZHhUvGCH0udlJ2vadquR03phBHcvlNmMbJGWAetkdcKyi+7TaW7OUSj +lge4WYERgYzBB6eJH+UfPjmw3aSPZcNXt2RckPXEbNrL8TVXYdEvwLJoJv9/I8JP +FLiGOm5uTMEk8S4txs2efueg1XyymilCKzzuXlJvrvPA4u6HI7qNvuvkvUjQmwBH +gwIDAQAB +-----END PUBLIC KEY----- +]], + + es512_private_key = [[ +-----BEGIN EC PRIVATE KEY----- +MIHcAgEBBEIAWT73PVm/Ry1jd3pM2VFD9neWfLhs1PBYU8UmCrj2mMUXwk8FQy+X +QVdIdwjpYnDgrxEdBbiuSDWxQq3LbNnnJzagBwYFK4EEACOhgYkDgYYABAGzP5K5 +cY2xWPv0KMDNKoxRmX/TJVFH9VHoLBmj9H6/gDLtYQ/plQVuDLX/QPeXug4CgsPX +28p7G0/JOQoKeP423ABYSBOf5RZoV3OE3miHh2fd0nf7T5khZEhkHj6twR2swADe +U2RCz4If+3hk3cKhAr01B2XYRgI3FFx8hV4wParxLQ== +-----END EC PRIVATE KEY----- +]], + es512_public_key = [[ +-----BEGIN PUBLIC KEY----- +MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBsz+SuXGNsVj79CjAzSqMUZl/0yVR +R/VR6CwZo/R+v4Ay7WEP6ZUFbgy1/0D3l7oOAoLD19vKextPyTkKCnj+NtwAWEgT +n+UWaFdzhN5oh4dn3dJ3+0+ZIWRIZB4+rcEdrMAA3lNkQs+CH/t4ZN3CoQK9NQdl +2EYCNxRcfIVeMD2q8S0= +-----END PUBLIC KEY----- +]], +}