@@ -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+
12191255test_incorrect_kid (_ ) ->
12201256 AltKid = <<" other-token-key" >>,
12211257 Username = <<" username" >>,
0 commit comments