From 8074a32f173b683a902d5c5f92115d434b3be262 Mon Sep 17 00:00:00 2001 From: Robert Newson Date: Thu, 19 Mar 2020 10:54:40 +0000 Subject: no need to deduplicate this list --- src/couch/src/couch_httpd_auth.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/couch/src/couch_httpd_auth.erl b/src/couch/src/couch_httpd_auth.erl index 7c55f390e..6b85a02cc 100644 --- a/src/couch/src/couch_httpd_auth.erl +++ b/src/couch/src/couch_httpd_auth.erl @@ -211,7 +211,7 @@ get_configured_algorithms() -> re:split(config:get("jwt_auth", "allowed_algorithms", "HS256"), "\s*,\s*", [{return, binary}]). get_configured_claims() -> - lists:usort(re:split(config:get("jwt_auth", "required_claims", ""), "\s*,\s*", [{return, binary}])). + re:split(config:get("jwt_auth", "required_claims", ""), "\s*,\s*", [{return, binary}]). cookie_authentication_handler(Req) -> cookie_authentication_handler(Req, couch_auth_cache). -- cgit v1.2.1 From bb86d0478412e525e810abbb4cecbdd32c6d3e11 Mon Sep 17 00:00:00 2001 From: Robert Newson Date: Thu, 19 Mar 2020 16:03:16 +0000 Subject: generate JWT token ourselves --- mix.exs | 2 ++ test/elixir/test/jwtauth_test.exs | 9 +++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/mix.exs b/mix.exs index 2e4a7aa85..bab22f12f 100644 --- a/mix.exs +++ b/mix.exs @@ -65,7 +65,9 @@ defmodule CouchDBTest.Mixfile do {:junit_formatter, "~> 3.0", only: [:dev, :test, :integration]}, {:httpotion, ">= 3.1.3", only: [:dev, :test, :integration], runtime: false}, {:excoveralls, "~> 0.12", only: :test}, + {:b64url, path: Path.expand("src/b64url", __DIR__)}, {:jiffy, path: Path.expand("src/jiffy", __DIR__)}, + {:jwtf, path: Path.expand("src/jwtf", __DIR__)}, {:ibrowse, path: Path.expand("src/ibrowse", __DIR__), override: true, compile: false}, {:credo, "~> 1.3.1", only: [:dev, :test, :integration], runtime: false} diff --git a/test/elixir/test/jwtauth_test.exs b/test/elixir/test/jwtauth_test.exs index 2e78ee989..9f2074ccf 100644 --- a/test/elixir/test/jwtauth_test.exs +++ b/test/elixir/test/jwtauth_test.exs @@ -3,7 +3,7 @@ defmodule JwtAuthTest do @moduletag :authentication - test "jwt auth with secret", _context do + test "jwt auth with HS256 secret", _context do secret = "zxczxc12zxczxc12" @@ -16,13 +16,14 @@ defmodule JwtAuthTest do ] run_on_modified_server(server_config, fn -> - test_fun() + test_fun("HS256", secret) end) end - def test_fun() do + def test_fun(alg, key) do + {:ok, token} = :jwtf.encode({[{"alg", alg}, {"typ", "JWT"}]}, {[{"sub", "couch@apache.org"}]}, key) resp = Couch.get("/_session", - headers: [authorization: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJjb3VjaEBhcGFjaGUub3JnIn0.KYHmGXWj0HNHzZCjfOfsIfZWdguEBSn31jUdDUA9118"] + headers: [authorization: "Bearer #{token}"] ) assert resp.body["userCtx"]["name"] == "couch@apache.org" -- cgit v1.2.1 From 5c77ef0b9cf3be98db3da692527e4c8726b2fc78 Mon Sep 17 00:00:00 2001 From: Robert Newson Date: Thu, 19 Mar 2020 16:16:05 +0000 Subject: test all variants of jwt hmac --- test/elixir/test/jwtauth_test.exs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/test/elixir/test/jwtauth_test.exs b/test/elixir/test/jwtauth_test.exs index 9f2074ccf..aee14b3c5 100644 --- a/test/elixir/test/jwtauth_test.exs +++ b/test/elixir/test/jwtauth_test.exs @@ -3,7 +3,7 @@ defmodule JwtAuthTest do @moduletag :authentication - test "jwt auth with HS256 secret", _context do + test "jwt auth with HMAC secret", _context do secret = "zxczxc12zxczxc12" @@ -12,12 +12,17 @@ defmodule JwtAuthTest do :section => "jwt_auth", :key => "secret", :value => secret + }, + %{ + :section => "jwt_auth", + :key => "allowed_algorithms", + :value => "HS256, HS384, HS512" } ] - run_on_modified_server(server_config, fn -> - test_fun("HS256", secret) - end) + run_on_modified_server(server_config, fn -> test_fun("HS256", secret) end) + run_on_modified_server(server_config, fn -> test_fun("HS384", secret) end) + run_on_modified_server(server_config, fn -> test_fun("HS512", secret) end) end def test_fun(alg, key) do -- cgit v1.2.1 From db21eda6f423e34944344ead346d63a4350918d4 Mon Sep 17 00:00:00 2001 From: Robert Newson Date: Thu, 19 Mar 2020 19:06:23 +0000 Subject: support RSA for JWT auth --- rel/overlay/etc/default.ini | 16 ++++++++++++-- src/couch/src/couch_httpd_auth.erl | 21 ++++++++++++++---- test/elixir/test/jwtauth_test.exs | 44 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 73 insertions(+), 8 deletions(-) diff --git a/rel/overlay/etc/default.ini b/rel/overlay/etc/default.ini index 82a56590f..25daa4813 100644 --- a/rel/overlay/etc/default.ini +++ b/rel/overlay/etc/default.ini @@ -141,12 +141,24 @@ max_db_number_for_dbs_info_req = 100 ; admin_only_all_dbs = true ;[jwt_auth] -; Symmetric secret to be used when checking JWT token signatures -; secret = ; List of claims to validate ; required_claims = exp ; List of algorithms to accept during checks ; allowed_algorithms = HS256 +; +; [jwt_keys] +; Configure at least one key here if using the JWT auth handler. +; If your JWT tokens do not include a "kid" attribute, use "_default" +; as the config key, otherwise use the kid as the config key. +; Examples +; _default = aGVsbG8= +; foo = aGVsbG8= +; The config values can represent symmetric and asymmetrics keys. +; For symmetrics keys, the value is base64 encoded; +; _default = aGVsbG8= # base64-encoded form of "hello" +; For asymmetric keys, the value is the PEM encoding of the public +; key with newlines replaced with the escape sequence \n. +; foo = -----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEDsr0lz/Dg3luarb+Kua0Wcj9WrfR23os\nwHzakglb8GhWRDn+oZT0Bt/26sX8uB4/ij9PEOLHPo+IHBtX4ELFFVr5GTzlqcJe\nyctaTDd1OOAPXYuc67EWtGZ3pDAzztRs\n-----END PUBLIC KEY-----\n\n [couch_peruser] ; If enabled, couch_peruser ensures that a private per-user database diff --git a/src/couch/src/couch_httpd_auth.erl b/src/couch/src/couch_httpd_auth.erl index 6b85a02cc..62fc694e1 100644 --- a/src/couch/src/couch_httpd_auth.erl +++ b/src/couch/src/couch_httpd_auth.erl @@ -189,11 +189,11 @@ proxy_auth_user(Req) -> end. jwt_authentication_handler(Req) -> - case {config:get("jwt_auth", "secret"), header_value(Req, "Authorization")} of - {Secret, "Bearer " ++ Jwt} when Secret /= undefined -> + case header_value(Req, "Authorization") of + "Bearer " ++ Jwt -> RequiredClaims = get_configured_claims(), AllowedAlgorithms = get_configured_algorithms(), - case jwtf:decode(?l2b(Jwt), [{alg, AllowedAlgorithms} | RequiredClaims], fun(_,_) -> Secret end) of + case jwtf:decode(?l2b(Jwt), [{alg, AllowedAlgorithms} | RequiredClaims], fun jwt_keystore/2) of {ok, {Claims}} -> case lists:keyfind(<<"sub">>, 1, Claims) of false -> throw({unauthorized, <<"Token missing sub claim.">>}); @@ -204,7 +204,7 @@ jwt_authentication_handler(Req) -> {error, Reason} -> throw({unauthorized, Reason}) end; - {_, _} -> Req + _ -> Req end. get_configured_algorithms() -> @@ -213,6 +213,19 @@ get_configured_algorithms() -> get_configured_claims() -> re:split(config:get("jwt_auth", "required_claims", ""), "\s*,\s*", [{return, binary}]). +jwt_keystore(Alg, undefined) -> + jwt_keystore(Alg, "_default"); +jwt_keystore(Alg, KID) -> + Key = config:get("jwt_keys", KID), + case jwtf:verification_algorithm(Alg) of + {hmac, _} -> + Key; + {public_key, _} -> + BinKey = ?l2b(string:replace(Key, "\\n", "\n", all)), + [PEMEntry] = public_key:pem_decode(BinKey), + public_key:pem_entry_decode(PEMEntry) + end. + cookie_authentication_handler(Req) -> cookie_authentication_handler(Req, couch_auth_cache). diff --git a/test/elixir/test/jwtauth_test.exs b/test/elixir/test/jwtauth_test.exs index aee14b3c5..6b3da9a71 100644 --- a/test/elixir/test/jwtauth_test.exs +++ b/test/elixir/test/jwtauth_test.exs @@ -9,8 +9,8 @@ defmodule JwtAuthTest do server_config = [ %{ - :section => "jwt_auth", - :key => "secret", + :section => "jwt_keys", + :key => "_default", :value => secret }, %{ @@ -25,8 +25,48 @@ defmodule JwtAuthTest do run_on_modified_server(server_config, fn -> test_fun("HS512", secret) end) end + defmodule RSA do + require Record + Record.defrecord :public, :RSAPublicKey, + Record.extract(:RSAPublicKey, from_lib: "public_key/include/public_key.hrl") + Record.defrecord :private, :RSAPrivateKey, + Record.extract(:RSAPrivateKey, from_lib: "public_key/include/public_key.hrl") + end + + test "jwt auth with RSA secret", _context do + require JwtAuthTest.RSA + + private_key = :public_key.generate_key({:rsa, 2048, 17}) + public_key = RSA.public( + modulus: RSA.private(private_key, :modulus), + publicExponent: RSA.private(private_key, :publicExponent)) + + public_pem = :public_key.pem_encode( + [:public_key.pem_entry_encode( + :SubjectPublicKeyInfo, public_key)]) + public_pem = String.replace(public_pem, "\n", "\\n") + + server_config = [ + %{ + :section => "jwt_keys", + :key => "_default", + :value => public_pem + }, + %{ + :section => "jwt_auth", + :key => "allowed_algorithms", + :value => "RS256, RS384, RS512" + } + ] + + run_on_modified_server(server_config, fn -> test_fun("RS256", private_key) end) + run_on_modified_server(server_config, fn -> test_fun("RS384", private_key) end) + run_on_modified_server(server_config, fn -> test_fun("RS512", private_key) end) + end + def test_fun(alg, key) do {:ok, token} = :jwtf.encode({[{"alg", alg}, {"typ", "JWT"}]}, {[{"sub", "couch@apache.org"}]}, key) + resp = Couch.get("/_session", headers: [authorization: "Bearer #{token}"] ) -- cgit v1.2.1 From 623ae9acbed5f60244cde30fc969e0ffb2792abf Mon Sep 17 00:00:00 2001 From: Robert Newson Date: Fri, 20 Mar 2020 11:19:44 +0000 Subject: add EC tests --- test/elixir/test/jwtauth_test.exs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/test/elixir/test/jwtauth_test.exs b/test/elixir/test/jwtauth_test.exs index 6b3da9a71..a8f9c50e0 100644 --- a/test/elixir/test/jwtauth_test.exs +++ b/test/elixir/test/jwtauth_test.exs @@ -64,6 +64,44 @@ defmodule JwtAuthTest do run_on_modified_server(server_config, fn -> test_fun("RS512", private_key) end) end + defmodule EC do + require Record + Record.defrecord :point, :ECPoint, + Record.extract(:ECPoint, from_lib: "public_key/include/public_key.hrl") + Record.defrecord :private, :ECPrivateKey, + Record.extract(:ECPrivateKey, from_lib: "public_key/include/public_key.hrl") + end + + test "jwt auth with EC secret", _context do + require JwtAuthTest.EC + + private_key = :public_key.generate_key({:namedCurve, :secp384r1}) + point = EC.point(point: EC.private(private_key, :publicKey)) + public_key = {point, EC.private(private_key, :parameters)} + + public_pem = :public_key.pem_encode( + [:public_key.pem_entry_encode( + :SubjectPublicKeyInfo, public_key)]) + public_pem = String.replace(public_pem, "\n", "\\n") + + server_config = [ + %{ + :section => "jwt_keys", + :key => "_default", + :value => public_pem + }, + %{ + :section => "jwt_auth", + :key => "allowed_algorithms", + :value => "ES256, ES384, ES512" + } + ] + + run_on_modified_server(server_config, fn -> test_fun("ES256", private_key) end) + run_on_modified_server(server_config, fn -> test_fun("ES384", private_key) end) + run_on_modified_server(server_config, fn -> test_fun("ES512", private_key) end) + end + def test_fun(alg, key) do {:ok, token} = :jwtf.encode({[{"alg", alg}, {"typ", "JWT"}]}, {[{"sub", "couch@apache.org"}]}, key) -- cgit v1.2.1 From c1e7c5ac2c754a342fb5fd7dc6473c1630ce422c Mon Sep 17 00:00:00 2001 From: Robert Newson Date: Fri, 20 Mar 2020 12:32:16 +0000 Subject: Create in-memory cache of JWT keys Decoding RSA and EC keys is a little expensive and we don't want to do it for every single request. Add a cache that is invalidated on config change. --- src/couch/src/couch_httpd_auth.erl | 15 +---- src/jwtf/src/jwtf.app.src | 2 + src/jwtf/src/jwtf_app.erl | 28 +++++++++ src/jwtf/src/jwtf_keystore.erl | 118 +++++++++++++++++++++++++++++++++++++ src/jwtf/src/jwtf_sup.erl | 38 ++++++++++++ 5 files changed, 187 insertions(+), 14 deletions(-) create mode 100644 src/jwtf/src/jwtf_app.erl create mode 100644 src/jwtf/src/jwtf_keystore.erl create mode 100644 src/jwtf/src/jwtf_sup.erl diff --git a/src/couch/src/couch_httpd_auth.erl b/src/couch/src/couch_httpd_auth.erl index 62fc694e1..86d583c56 100644 --- a/src/couch/src/couch_httpd_auth.erl +++ b/src/couch/src/couch_httpd_auth.erl @@ -193,7 +193,7 @@ jwt_authentication_handler(Req) -> "Bearer " ++ Jwt -> RequiredClaims = get_configured_claims(), AllowedAlgorithms = get_configured_algorithms(), - case jwtf:decode(?l2b(Jwt), [{alg, AllowedAlgorithms} | RequiredClaims], fun jwt_keystore/2) of + case jwtf:decode(?l2b(Jwt), [{alg, AllowedAlgorithms} | RequiredClaims], fun jwtf_keystore:get/2) of {ok, {Claims}} -> case lists:keyfind(<<"sub">>, 1, Claims) of false -> throw({unauthorized, <<"Token missing sub claim.">>}); @@ -213,19 +213,6 @@ get_configured_algorithms() -> get_configured_claims() -> re:split(config:get("jwt_auth", "required_claims", ""), "\s*,\s*", [{return, binary}]). -jwt_keystore(Alg, undefined) -> - jwt_keystore(Alg, "_default"); -jwt_keystore(Alg, KID) -> - Key = config:get("jwt_keys", KID), - case jwtf:verification_algorithm(Alg) of - {hmac, _} -> - Key; - {public_key, _} -> - BinKey = ?l2b(string:replace(Key, "\\n", "\n", all)), - [PEMEntry] = public_key:pem_decode(BinKey), - public_key:pem_entry_decode(PEMEntry) - end. - cookie_authentication_handler(Req) -> cookie_authentication_handler(Req, couch_auth_cache). diff --git a/src/jwtf/src/jwtf.app.src b/src/jwtf/src/jwtf.app.src index 304bb9e0a..24081bf6f 100644 --- a/src/jwtf/src/jwtf.app.src +++ b/src/jwtf/src/jwtf.app.src @@ -18,10 +18,12 @@ kernel, stdlib, b64url, + config, crypto, jiffy, public_key ]}, + {mod, {jwtf_app, []}}, {env,[]}, {modules, []}, {maintainers, []}, diff --git a/src/jwtf/src/jwtf_app.erl b/src/jwtf/src/jwtf_app.erl new file mode 100644 index 000000000..bd708e2a3 --- /dev/null +++ b/src/jwtf/src/jwtf_app.erl @@ -0,0 +1,28 @@ +% Licensed 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. + +-module(jwtf_app). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). + +%% =================================================================== +%% Application callbacks +%% =================================================================== + +start(_StartType, _StartArgs) -> + jwtf_sup:start_link(). + +stop(_State) -> + ok. diff --git a/src/jwtf/src/jwtf_keystore.erl b/src/jwtf/src/jwtf_keystore.erl new file mode 100644 index 000000000..82df54e5b --- /dev/null +++ b/src/jwtf/src/jwtf_keystore.erl @@ -0,0 +1,118 @@ +% Licensed 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. + +-module(jwtf_keystore). +-behaviour(gen_server). +-behaviour(config_listener). + +% public api. +-export([ + get/2, + start_link/0 +]). + +% gen_server api. +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + code_change/3, terminate/2]). + +% config_listener api +-export([handle_config_change/5, handle_config_terminate/3]). + +% public functions + +get(Alg, undefined) -> + get(Alg, "_default"); + +get(Alg, KID) when is_binary(KID) -> + get(Alg, binary_to_list(KID)); + +get(Alg, KID) -> + case ets:lookup(?MODULE, KID) of + [] -> + Key = get_from_config(Alg, KID), + ok = gen_server:call(?MODULE, {set, KID, Key}), + Key; + [{KID, Key}] -> + Key + end. + + +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +% gen_server functions + +init(_) -> + ok = config:listen_for_changes(?MODULE, nil), + ets:new(?MODULE, [public, named_table]), + {ok, nil}. + + +handle_call({set, KID, Key}, _From, State) -> + true = ets:insert(?MODULE, {KID, Key}), + {reply, ok, State}. + + +handle_cast({delete, KID}, State) -> + true = ets:delete(?MODULE, KID), + {noreply, State}; + +handle_cast(_Msg, State) -> + {noreply, State}. + + +handle_info(restart_config_listener, State) -> + ok = config:listen_for_changes(?MODULE, nil), + {noreply, State}; + +handle_info(_Msg, State) -> + {noreply, State}. + + +terminate(_Reason, _State) -> + ok. + + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + + +% config listener callback + +handle_config_change("jwt_keys", KID, _Value, _, _) -> + {ok, gen_server:cast(?MODULE, {delete, KID})}; + +handle_config_change(_, _, _, _, _) -> + {ok, nil}. + +handle_config_terminate(_Server, stop, _State) -> + ok; + +handle_config_terminate(_Server, _Reason, _State) -> + erlang:send_after(100, whereis(?MODULE), restart_config_listener). + +% private functions + +get_from_config(Alg, KID) -> + case config:get("jwt_keys", KID) of + undefined -> + throw({bad_request, <<"Unknown kid">>}); + Key -> + case jwtf:verification_algorithm(Alg) of + {hmac, _} -> + list_to_binary(Key); + {public_key, _} -> + BinKey = iolist_to_binary(string:replace(Key, "\\n", "\n", all)), + [PEMEntry] = public_key:pem_decode(BinKey), + public_key:pem_entry_decode(PEMEntry) + end + end. diff --git a/src/jwtf/src/jwtf_sup.erl b/src/jwtf/src/jwtf_sup.erl new file mode 100644 index 000000000..6f44808de --- /dev/null +++ b/src/jwtf/src/jwtf_sup.erl @@ -0,0 +1,38 @@ +% Licensed 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. + +-module(jwtf_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +%% Helper macro for declaring children of supervisor +-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). + +%% =================================================================== +%% API functions +%% =================================================================== + +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +%% =================================================================== +%% Supervisor callbacks +%% =================================================================== + +init([]) -> + {ok, { {one_for_one, 5, 10}, [?CHILD(jwtf_keystore, worker)]} }. -- cgit v1.2.1 From dc88e3623f839246028b722dbe3b4235c27dc69e Mon Sep 17 00:00:00 2001 From: Robert Newson Date: Fri, 20 Mar 2020 13:43:33 +0000 Subject: throw Reason directly so we send good http error responses --- src/couch/src/couch_httpd_auth.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/couch/src/couch_httpd_auth.erl b/src/couch/src/couch_httpd_auth.erl index 86d583c56..f5387d18f 100644 --- a/src/couch/src/couch_httpd_auth.erl +++ b/src/couch/src/couch_httpd_auth.erl @@ -202,7 +202,7 @@ jwt_authentication_handler(Req) -> }} end; {error, Reason} -> - throw({unauthorized, Reason}) + throw(Reason) end; _ -> Req end. -- cgit v1.2.1 From 16b3c8d6e1c39e2bd0b0bb8524e3b28ce5457973 Mon Sep 17 00:00:00 2001 From: Robert Newson Date: Fri, 20 Mar 2020 19:07:51 +0000 Subject: base64 the symmetric jwt keys --- src/jwtf/src/jwtf_keystore.erl | 2 +- test/elixir/test/jwtauth_test.exs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jwtf/src/jwtf_keystore.erl b/src/jwtf/src/jwtf_keystore.erl index 82df54e5b..2f2f24744 100644 --- a/src/jwtf/src/jwtf_keystore.erl +++ b/src/jwtf/src/jwtf_keystore.erl @@ -109,7 +109,7 @@ get_from_config(Alg, KID) -> Key -> case jwtf:verification_algorithm(Alg) of {hmac, _} -> - list_to_binary(Key); + base64:decode(Key); {public_key, _} -> BinKey = iolist_to_binary(string:replace(Key, "\\n", "\n", all)), [PEMEntry] = public_key:pem_decode(BinKey), diff --git a/test/elixir/test/jwtauth_test.exs b/test/elixir/test/jwtauth_test.exs index a8f9c50e0..3f26e1eaf 100644 --- a/test/elixir/test/jwtauth_test.exs +++ b/test/elixir/test/jwtauth_test.exs @@ -11,7 +11,7 @@ defmodule JwtAuthTest do %{ :section => "jwt_keys", :key => "_default", - :value => secret + :value => :base64.encode(secret) }, %{ :section => "jwt_auth", -- cgit v1.2.1