diff --git a/CHANGES-4.3.md b/CHANGES-4.3.md index cbbfc8d8e..059375efc 100644 --- a/CHANGES-4.3.md +++ b/CHANGES-4.3.md @@ -32,7 +32,8 @@ File format: subscribed to. [#8288] - Ensuring that exhook dispatches the client events are sequential. [#8311] - Ensure start dashboard ok event if default_username is missing. -- Fix key update from JWKS server by JWT auth. +- Fix key update from JWKS server by JWT auth. [#8337] +- Better errors for JWT claim validations. [#8337] ## v4.3.15 diff --git a/apps/emqx_auth_jwt/src/emqx_auth_jwt.app.src b/apps/emqx_auth_jwt/src/emqx_auth_jwt.app.src index 3d84c5b54..32ca1fc14 100644 --- a/apps/emqx_auth_jwt/src/emqx_auth_jwt.app.src +++ b/apps/emqx_auth_jwt/src/emqx_auth_jwt.app.src @@ -1,6 +1,6 @@ {application, emqx_auth_jwt, [{description, "EMQ X Authentication with JWT"}, - {vsn, "4.3.3"}, % strict semver, bump manually! + {vsn, "4.3.4"}, % strict semver, bump manually! {modules, []}, {registered, [emqx_auth_jwt_sup]}, {applications, [kernel,stdlib,jose]}, diff --git a/apps/emqx_auth_jwt/src/emqx_auth_jwt.appup.src b/apps/emqx_auth_jwt/src/emqx_auth_jwt.appup.src index b94159225..2538187fd 100644 --- a/apps/emqx_auth_jwt/src/emqx_auth_jwt.appup.src +++ b/apps/emqx_auth_jwt/src/emqx_auth_jwt.appup.src @@ -1,9 +1,9 @@ %% -*- mode: erlang -*- %% Unless you know what you are doing, DO NOT edit manually!! {VSN, - [{<<"4\\.3\\.[0-2]">>, - [{restart_application,emqx_auth_jwt}]}, + [{"4.3.3",[{load_module,emqx_auth_jwt_svr,brutal_purge,soft_purge,[]}]}, + {<<"4\\.3\\.[0-2]">>,[{restart_application,emqx_auth_jwt}]}, {<<".*">>,[]}], - [{<<"4\\.3\\.[0-2]">>, - [{restart_application,emqx_auth_jwt}]}, + [{"4.3.3",[{load_module,emqx_auth_jwt_svr,brutal_purge,soft_purge,[]}]}, + {<<"4\\.3\\.[0-2]">>,[{restart_application,emqx_auth_jwt}]}, {<<".*">>,[]}]}. diff --git a/apps/emqx_auth_jwt/src/emqx_auth_jwt_svr.erl b/apps/emqx_auth_jwt/src/emqx_auth_jwt_svr.erl index b82ca0b48..08c60d8ed 100644 --- a/apps/emqx_auth_jwt/src/emqx_auth_jwt_svr.erl +++ b/apps/emqx_auth_jwt/src/emqx_auth_jwt_svr.erl @@ -198,7 +198,11 @@ do_verify(JwsCompacted, [Jwk|More]) -> case check_claims(Claims) of {false, <<"exp">>} -> {error, {invalid_signature, expired}}; - NClaims -> + {false, <<"iat">>} -> + {error, {invalid_signature, issued_in_future}}; + {false, <<"nbf">>} -> + {error, {invalid_signature, not_valid_yet}}; + {true, NClaims} -> {ok, NClaims} end; {false, _, _} -> @@ -234,7 +238,7 @@ with_int_value(Fun) -> end. do_check_claim([], Claims) -> - Claims; + {true, Claims}; do_check_claim([{K, F}|More], Claims) -> case Claims of #{K := V} -> diff --git a/apps/emqx_auth_jwt/test/emqx_auth_jwt_SUITE.erl b/apps/emqx_auth_jwt/test/emqx_auth_jwt_SUITE.erl index 1287f43e7..c8eed8b41 100644 --- a/apps/emqx_auth_jwt/test/emqx_auth_jwt_SUITE.erl +++ b/apps/emqx_auth_jwt/test/emqx_auth_jwt_SUITE.erl @@ -74,7 +74,7 @@ t_check_auth(_Config) -> Plain = #{clientid => <<"client1">>, username => <<"plain">>, zone => external}, Jwt = sign([{clientid, <<"client1">>}, {username, <<"plain">>}, - {exp, os:system_time(seconds) + 3}], <<"HS256">>, <<"emqxsecret">>), + {exp, os:system_time(seconds) + 2}], <<"HS256">>, <<"emqxsecret">>), ct:pal("Jwt: ~p~n", [Jwt]), Result0 = emqx_access_control:authenticate(Plain#{password => Jwt}), @@ -83,7 +83,7 @@ t_check_auth(_Config) -> ct:sleep(3100), Result1 = emqx_access_control:authenticate(Plain#{password => Jwt}), - ct:pal("Auth result after 1000ms: ~p~n", [Result1]), + ct:pal("Auth result after 3100ms: ~p~n", [Result1]), ?assertMatch({error, _}, Result1), Jwt_Error = sign([{client_id, <<"client1">>}, @@ -94,6 +94,32 @@ t_check_auth(_Config) -> ?assertEqual({error, invalid_signature}, Result2), ?assertMatch({error, _}, emqx_access_control:authenticate(Plain#{password => <<"asd">>})). +t_check_nbf(init, _Config) -> + application:unset_env(emqx_auth_jwt, verify_claims). +t_check_nbf(_Config) -> + Plain = #{clientid => <<"client1">>, username => <<"plain">>, zone => external}, + Jwt = sign([{clientid, <<"client1">>}, + {username, <<"plain">>}, + {nbf, os:system_time(seconds) + 3}], <<"HS256">>, <<"emqxsecret">>), + ct:pal("Jwt: ~p~n", [Jwt]), + + Result0 = emqx_access_control:authenticate(Plain#{password => Jwt}), + ct:pal("Auth result: ~p~n", [Result0]), + ?assertEqual({error, {invalid_signature, not_valid_yet}}, Result0). + +t_check_iat(init, _Config) -> + application:unset_env(emqx_auth_jwt, verify_claims). +t_check_iat(_Config) -> + Plain = #{clientid => <<"client1">>, username => <<"plain">>, zone => external}, + Jwt = sign([{clientid, <<"client1">>}, + {username, <<"plain">>}, + {iat, os:system_time(seconds) + 3}], <<"HS256">>, <<"emqxsecret">>), + ct:pal("Jwt: ~p~n", [Jwt]), + + Result0 = emqx_access_control:authenticate(Plain#{password => Jwt}), + ct:pal("Auth result: ~p~n", [Result0]), + ?assertEqual({error, {invalid_signature, issued_in_future}}, Result0). + t_check_auth_invalid_exp(init, _Config) -> application:unset_env(emqx_auth_jwt, verify_claims). t_check_auth_invalid_exp(_Config) ->