fix(svr): avoid crashed on undefined remote jwks server (#4916)
This commit is contained in:
parent
bfb02fe8c3
commit
72602df511
|
@ -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]},
|
||||||
|
|
|
@ -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, []}
|
||||||
|
]},
|
||||||
|
{<<".*">>, []}
|
||||||
|
]
|
||||||
|
}.
|
|
@ -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.
|
||||||
|
|
|
@ -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).
|
||||||
|
|
Loading…
Reference in New Issue