fix(svr): avoid crashed on undefined remote jwks server (#4916)

This commit is contained in:
JianBo He 2021-06-03 14:16:58 +08:00 committed by GitHub
parent bfb02fe8c3
commit 72602df511
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 42 additions and 6 deletions

View File

@ -1,6 +1,6 @@
{application, emqx_auth_jwt, {application, emqx_auth_jwt,
[{description, "EMQ X Authentication with JWT"}, [{description, "EMQ X Authentication with JWT"},
{vsn, "4.3.0"}, % strict semver, bump manually! {vsn, "4.3.1"}, % strict semver, bump manually!
{modules, []}, {modules, []},
{registered, [emqx_auth_jwt_sup]}, {registered, [emqx_auth_jwt_sup]},
{applications, [kernel,stdlib,jose]}, {applications, [kernel,stdlib,jose]},

View File

@ -0,0 +1,15 @@
%% -*-: erlang -*-
{VSN,
[
{"4.3.0", [
{load_module, emqx_auth_jwt_svr, brutal_purge, soft_purge, []}
]},
{<<".*">>, []}
],
[
{"4.3.0", [
{load_module, emqx_auth_jwt_svr, brutal_purge, soft_purge, []}
]},
{<<".*">>, []}
]
}.

View File

@ -140,7 +140,7 @@ handle_verify(JwsCompacted,
State = #state{static = Static, remote = Remote}) -> State = #state{static = Static, remote = Remote}) ->
try try
Jwks = case emqx_json:decode(jose_jws:peek_protected(JwsCompacted), [return_maps]) of Jwks = case emqx_json:decode(jose_jws:peek_protected(JwsCompacted), [return_maps]) of
#{<<"kid">> := Kid} -> #{<<"kid">> := Kid} when Remote /= undefined ->
[J || J <- Remote, maps:get(<<"kid">>, J#jose_jwk.fields, undefined) =:= Kid]; [J || J <- Remote, maps:get(<<"kid">>, J#jose_jwk.fields, undefined) =:= Kid];
_ -> Static _ -> Static
end, end,
@ -150,7 +150,9 @@ handle_verify(JwsCompacted,
{reply, do_verify(JwsCompacted, Jwks), State} {reply, do_verify(JwsCompacted, Jwks), State}
end end
catch catch
_:_ -> Class : Reason : Stk ->
?LOG(error, "Handle JWK crashed: ~p, ~p, stacktrace: ~p~n",
[Class, Reason, Stk]),
{reply, {error, invalid_signature}, State} {reply, {error, invalid_signature}, State}
end. end.
@ -186,8 +188,8 @@ do_verify(JwsCompacted, [Jwk|More]) ->
{true, Payload, _Jws} -> {true, Payload, _Jws} ->
Claims = emqx_json:decode(Payload, [return_maps]), Claims = emqx_json:decode(Payload, [return_maps]),
case check_claims(Claims) of case check_claims(Claims) of
false -> {false, <<"exp">>} ->
{error, invalid_signature}; {error, {invalid_signature, expired}};
NClaims -> NClaims ->
{ok, NClaims} {ok, NClaims}
end; end;
@ -217,6 +219,6 @@ do_check_claim([{K, F}|More], Claims) ->
{V, NClaims} -> {V, NClaims} ->
case F(V) of case F(V) of
true -> do_check_claim(More, NClaims); true -> do_check_claim(More, NClaims);
_ -> false _ -> {false, K}
end end
end. end.

View File

@ -33,6 +33,7 @@ groups() ->
, t_check_claims , t_check_claims
, t_check_claims_clientid , t_check_claims_clientid
, t_check_claims_username , t_check_claims_username
, t_check_claims_kid_in_header
]} ]}
]. ].
@ -61,6 +62,12 @@ set_special_configs(emqx_auth_jwt) ->
set_special_configs(_) -> set_special_configs(_) ->
ok. ok.
sign(Payload, Header, Key) when is_map(Header) ->
Jwk = jose_jwk:from_oct(Key),
Jwt = emqx_json:encode(Payload),
{_, Token} = jose_jws:compact(jose_jwt:sign(Jwk, Header, Jwt)),
Token;
sign(Payload, Alg, Key) -> sign(Payload, Alg, Key) ->
Jwk = jose_jwk:from_oct(Key), Jwk = jose_jwk:from_oct(Key),
Jwt = emqx_json:encode(Payload), Jwt = emqx_json:encode(Payload),
@ -145,3 +152,15 @@ t_check_claims_username(_) ->
Result3 = emqx_access_control:authenticate(Plain#{password => Jwt_Error}), Result3 = emqx_access_control:authenticate(Plain#{password => Jwt_Error}),
ct:pal("Auth result for the invalid jwt: ~p~n", [Result3]), ct:pal("Auth result for the invalid jwt: ~p~n", [Result3]),
?assertEqual({error, invalid_signature}, Result3). ?assertEqual({error, invalid_signature}, Result3).
t_check_claims_kid_in_header(_) ->
application:set_env(emqx_auth_jwt, verify_claims, []),
Plain = #{clientid => <<"client23">>, username => <<"plain">>, zone => external},
Jwt = sign([{clientid, <<"client23">>},
{username, <<"plain">>},
{exp, os:system_time(seconds) + 3}],
#{<<"alg">> => <<"HS256">>,
<<"kid">> => <<"a_kid_str">>}, <<"emqxsecret">>),
Result0 = emqx_access_control:authenticate(Plain#{password => Jwt}),
ct:pal("Auth result: ~p~n", [Result0]),
?assertMatch({ok, #{auth_result := success, jwt_claims := _}}, Result0).