Skip to content

Commit 2838025

Browse files
Accept float also for token expiry
1 parent 1bfd08f commit 2838025

3 files changed

Lines changed: 46 additions & 6 deletions

File tree

deps/oauth2_client/src/jwt_helper.erl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ decode(Token) ->
1818
{error, {invalid_token, Type, Err, Stacktrace}}
1919
end.
2020

21-
get_expiration_time(#{<<"exp">> := Exp}) when is_integer(Exp) -> {ok, Exp};
21+
get_expiration_time(#{<<"exp">> := Exp}) when is_number(Exp) -> {ok, trunc(Exp)};
22+
get_expiration_time(#{<<"exp">> := _}) -> {error, invalid_exp_field};
2223
get_expiration_time(#{}) -> {error, missing_exp_field}.

deps/rabbitmq_auth_backend_oauth2/src/rabbit_auth_backend_oauth2.erl

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,8 @@ update_state(AuthUser, NewToken) ->
160160
-spec expiry_timestamp(rabbit_types:auth_user()) -> integer() | never.
161161
expiry_timestamp(#auth_user{impl = DecodedTokenFun}) ->
162162
case DecodedTokenFun() of
163-
#{<<"exp">> := Exp} when is_integer(Exp) ->
164-
Exp;
163+
#{<<"exp">> := Exp} when is_number(Exp) ->
164+
trunc(Exp);
165165
_ ->
166166
never
167167
end.
@@ -235,12 +235,15 @@ ensure_same_username(PreferredUsernameClaims, CurrentDecodedToken, NewDecodedTok
235235
_ -> {error, mismatch_username_after_token_refresh}
236236
end.
237237

238-
validate_token_expiry(#{<<"exp">> := Exp}) when is_integer(Exp) ->
238+
validate_token_expiry(#{<<"exp">> := Exp}) when is_number(Exp) ->
239239
Now = os:system_time(seconds),
240-
case Exp =< Now of
241-
true -> {error, rabbit_misc:format("Provided JWT token has expired at timestamp ~tp (validated at ~tp)", [Exp, Now])};
240+
ExpTs = trunc(Exp),
241+
case ExpTs =< Now of
242+
true -> {error, rabbit_misc:format("Provided JWT token has expired at timestamp ~tp (validated at ~tp)", [ExpTs, Now])};
242243
false -> ok
243244
end;
245+
validate_token_expiry(#{<<"exp">> := _}) ->
246+
{error, "Provided JWT token has an invalid exp claim: value is not a number"};
244247
validate_token_expiry(#{}) -> ok.
245248

246249
-spec check_token(raw_jwt_token(), {resource_server(), internal_oauth_provider()}) ->

deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ all() ->
4040
test_restricted_vhost_access_with_a_valid_token,
4141
test_insufficient_permissions_in_a_valid_token,
4242
test_token_expiration,
43+
test_token_expiry_with_float_exp,
44+
test_token_expiry_with_non_numeric_exp,
4345
test_invalid_signature,
4446
test_incorrect_kid,
4547
normalize_token_scope_using_multiple_scopes_key,
@@ -1216,6 +1218,40 @@ test_token_expiration(_) ->
12161218
?assertMatch({refused, _, _},
12171219
user_login_authentication(Username, [{password, Token}])).
12181220

1221+
test_token_expiry_with_float_exp(_) ->
1222+
Username = <<"username">>,
1223+
set_env(resource_server_id, <<"rabbitmq">>),
1224+
1225+
%% A valid, future float exp must be accepted and expiry_timestamp must
1226+
%% return the truncated integer — not the atom 'never'.
1227+
FutureFloatExp = float(os:system_time(seconds) + 600),
1228+
FutureToken = (?UTIL_MOD:token_with_sub(?UTIL_MOD:expirable_token(), Username))
1229+
#{<<"exp">> := FutureFloatExp},
1230+
{ok, #auth_user{username = Username} = User} =
1231+
user_login_authentication(Username, [{password, FutureToken}]),
1232+
?assertEqual(trunc(FutureFloatExp), rabbit_auth_backend_oauth2:expiry_timestamp(User)),
1233+
1234+
%% An already-expired float exp must be refused, not silently accepted.
1235+
PastFloatExp = float(os:system_time(seconds) - 10),
1236+
ExpiredToken = (?UTIL_MOD:token_with_sub(?UTIL_MOD:expirable_token(), Username))
1237+
#{<<"exp">> := PastFloatExp},
1238+
?assertMatch({refused, _, _},
1239+
user_login_authentication(Username, [{password, ExpiredToken}])).
1240+
1241+
test_token_expiry_with_non_numeric_exp(_) ->
1242+
Username = <<"username">>,
1243+
set_env(resource_server_id, <<"rabbitmq">>),
1244+
1245+
%% A token whose exp claim is not a number (e.g. a string) must be refused,
1246+
%% not silently accepted because the is_number guard falls through to the
1247+
%% no-exp-field catch-all clause.
1248+
lists:foreach(fun(NonNumericExp) ->
1249+
InvalidToken = (?UTIL_MOD:token_with_sub(?UTIL_MOD:expirable_token(), Username))
1250+
#{<<"exp">> := NonNumericExp},
1251+
?assertMatch({refused, _, _},
1252+
user_login_authentication(Username, [{password, InvalidToken}]))
1253+
end, [<<"1700000300">>, true, false, null]).
1254+
12191255
test_incorrect_kid(_) ->
12201256
AltKid = <<"other-token-key">>,
12211257
Username = <<"username">>,

0 commit comments

Comments
 (0)