@@ -40,7 +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_expiration_with_float_exp ,
43+ test_token_expiry_with_float_exp ,
44+ test_token_expiry_with_non_numeric_exp ,
4445 test_invalid_signature ,
4546 test_incorrect_kid ,
4647 normalize_token_scope_using_multiple_scopes_key ,
@@ -1242,6 +1243,40 @@ test_token_expiration(_) ->
12421243 ? assertMatch ({refused , _ , _ },
12431244 user_login_authentication (Username , [{password , Token }])).
12441245
1246+ test_token_expiry_with_float_exp (_ ) ->
1247+ Username = <<" username" >>,
1248+ set_env (resource_server_id , <<" rabbitmq" >>),
1249+
1250+ % % A valid, future float exp must be accepted and expiry_timestamp must
1251+ % % return the truncated integer — not the atom 'never'.
1252+ FutureFloatExp = float (os :system_time (seconds ) + 600 ),
1253+ FutureToken = (? UTIL_MOD :token_with_sub (? UTIL_MOD :expirable_token (), Username ))
1254+ #{<<" exp" >> := FutureFloatExp },
1255+ {ok , # auth_user {username = Username } = User } =
1256+ user_login_authentication (Username , [{password , FutureToken }]),
1257+ ? assertEqual (trunc (FutureFloatExp ), rabbit_auth_backend_oauth2 :expiry_timestamp (User )),
1258+
1259+ % % An already-expired float exp must be refused, not silently accepted.
1260+ PastFloatExp = float (os :system_time (seconds ) - 10 ),
1261+ ExpiredToken = (? UTIL_MOD :token_with_sub (? UTIL_MOD :expirable_token (), Username ))
1262+ #{<<" exp" >> := PastFloatExp },
1263+ ? assertMatch ({refused , _ , _ },
1264+ user_login_authentication (Username , [{password , ExpiredToken }])).
1265+
1266+ test_token_expiry_with_non_numeric_exp (_ ) ->
1267+ Username = <<" username" >>,
1268+ set_env (resource_server_id , <<" rabbitmq" >>),
1269+
1270+ % % A token whose exp claim is not a number (e.g. a string) must be refused,
1271+ % % not silently accepted because the is_number guard falls through to the
1272+ % % no-exp-field catch-all clause.
1273+ lists :foreach (fun (NonNumericExp ) ->
1274+ InvalidToken = (? UTIL_MOD :token_with_sub (? UTIL_MOD :expirable_token (), Username ))
1275+ #{<<" exp" >> := NonNumericExp },
1276+ ? assertMatch ({refused , _ , _ },
1277+ user_login_authentication (Username , [{password , InvalidToken }]))
1278+ end , [<<" 1700000300" >>, true , false , null ]).
1279+
12451280test_incorrect_kid (_ ) ->
12461281 AltKid = <<" other-token-key" >>,
12471282 Username = <<" username" >>,
0 commit comments