diff --git a/deps/rabbitmq_auth_backend_oauth2/README.md b/deps/rabbitmq_auth_backend_oauth2/README.md index bb698ecc0953..b923a47fb60f 100644 --- a/deps/rabbitmq_auth_backend_oauth2/README.md +++ b/deps/rabbitmq_auth_backend_oauth2/README.md @@ -139,6 +139,46 @@ In that case, the configuration will look like this: NOTE: `jwks_url` takes precedence over `signing_keys` if both are provided. +### Variables Configurable in rabbitmq.conf + +| Key | Documentation +|------------------------------------------|----------- +| `auth_oauth2.resource_server_id` | [The Resource Server ID](#resource-server-id-and-scope-prefixes) +| `auth_oauth2.additional_scopes_key` | Configure the plugin to also look in other fields (maps to `additional_rabbitmq_scopes` in the old format). +| `auth_oauth2.default_key` | ID of the default signing key. +| `auth_oauth2.signing_keys` | Paths to signing key files. +| `auth_oauth2.jwks_url` | The URL of key server. According to the [JWT Specification](https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.2) key server URL must be https. +| `auth_oauth2.https.cacertfile` | Path to a file containing PEM-encoded CA certificates. The CA certificates are used during key server [peer verification](https://rabbitmq.com/ssl.html#peer-verification). +| `auth_oauth2.https.depth` | The maximum number of non-self-issued intermediate certificates that may follow the peer certificate in a valid [certification path](https://rabbitmq.com/ssl.html#peer-verification-depth). Default is 10. +| `auth_oauth2.https.peer_verification` | Should [peer verification](https://rabbitmq.com/ssl.html#peer-verification) be enabled. Available values: `verify_none`, `verify_peer`. Default is `verify_none`. It is recommended to configure `verify_peer`. Peer verification requires a certain amount of setup and is more secure. +| `auth_oauth2.https.fail_if_no_peer_cert` | Used together with `auth_oauth2.https.peer_verification = verify_peer`. When set to `true`, TLS connection will be rejected if client fails to provide a certificate. Default is `false`. +| `auth_oauth2.https.hostname_verification`| Enable wildcard-aware hostname verification for key server. Available values: `wildcard`, `none`. Default is `none`. +| `auth_oauth2.algorithms` | Restrict [the usable algorithms](https://github.com/potatosalad/erlang-jose#algorithm-support). + +For example: + +Configure with key files +``` +auth_oauth2.resource_server_id = new_resource_server_id +auth_oauth2.additional_scopes_key = my_custom_scope_key +auth_oauth2.default_key = id1 +auth_oauth2.signing_keys.id1 = test/config_schema_SUITE_data/certs/key.pem +auth_oauth2.signing_keys.id2 = test/config_schema_SUITE_data/certs/cert.pem +auth_oauth2.algorithms.1 = HS256 +auth_oauth2.algorithms.2 = RS256 +``` +Configure with key server +``` +auth_oauth2.resource_server_id = new_resource_server_id +auth_oauth2.jwks_url = https://my-jwt-issuer/jwks.json +auth_oauth2.https.cacertfile = test/config_schema_SUITE_data/certs/cacert.pem +auth_oauth2.https.peer_verification = verify_peer +auth_oauth2.https.depth = 5 +auth_oauth2.https.fail_if_no_peer_cert = true +auth_oauth2.https.hostname_verification = wildcard +auth_oauth2.algorithms.1 = HS256 +auth_oauth2.algorithms.2 = RS256 +``` ### Resource Server ID and Scope Prefixes OAuth 2.0 (and thus UAA-provided) tokens use scopes to communicate what set of permissions particular diff --git a/deps/rabbitmq_auth_backend_oauth2/priv/schema/rabbitmq_auth_backend_oauth2.schema b/deps/rabbitmq_auth_backend_oauth2/priv/schema/rabbitmq_auth_backend_oauth2.schema index 0feb73d9aaa5..1c8593e43430 100644 --- a/deps/rabbitmq_auth_backend_oauth2/priv/schema/rabbitmq_auth_backend_oauth2.schema +++ b/deps/rabbitmq_auth_backend_oauth2/priv/schema/rabbitmq_auth_backend_oauth2.schema @@ -77,3 +77,52 @@ end, Settings), maps:from_list(SigningKeys) end}. + +{mapping, + "auth_oauth2.jwks_url", + "rabbitmq_auth_backend_oauth2.key_config.jwks_url", + [{datatype, string}, {validators, ["uri", "https_uri"]}]}. + +{mapping, + "auth_oauth2.https.peer_verification", + "rabbitmq_auth_backend_oauth2.key_config.peer_verification", + [{datatype, {enum, [verify_peer, verify_none]}}]}. + +{mapping, + "auth_oauth2.https.cacertfile", + "rabbitmq_auth_backend_oauth2.key_config.cacertfile", + [{datatype, file}, {validators, ["file_accessible"]}]}. + +{mapping, + "auth_oauth2.https.depth", + "rabbitmq_auth_backend_oauth2.key_config.depth", + [{datatype, integer}]}. + +{mapping, + "auth_oauth2.https.hostname_verification", + "rabbitmq_auth_backend_oauth2.key_config.hostname_verification", + [{datatype, {enum, [wildcard, none]}}]}. + +{mapping, + "auth_oauth2.https.crl_check", + "rabbitmq_auth_backend_oauth2.key_config.crl_check", + [{datatype, {enum, [true, false, peer, best_effort]}}]}. + +{mapping, + "auth_oauth2.https.fail_if_no_peer_cert", + "rabbitmq_auth_backend_oauth2.key_config.fail_if_no_peer_cert", + [{datatype, {enum, [true, false]}}]}. + +{validator, "https_uri", "According to the JWT Specification, Key Server URL must be https.", + fun(Uri) -> string:nth_lexeme(Uri, 1, "://") == "https" end}. + +{mapping, + "auth_oauth2.algorithms.$algorithm", + "rabbitmq_auth_backend_oauth2.key_config.algorithms", + [{datatype, string}]}. + +{translation, "rabbitmq_auth_backend_oauth2.key_config.algorithms", + fun(Conf) -> + Settings = cuttlefish_variable:filter_by_prefix("auth_oauth2.algorithms", Conf), + [list_to_binary(V) || {_, V} <- Settings] + end}. diff --git a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwks.erl b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwks.erl new file mode 100644 index 000000000000..d34d9d5d99ae --- /dev/null +++ b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwks.erl @@ -0,0 +1,27 @@ +-module(uaa_jwks). +-export([get/1]). + +-spec get(string() | binary()) -> {ok, term()} | {error, term()}. +get(JwksUrl) -> + httpc:request(get, {JwksUrl, []}, [{ssl, ssl_options()}, {timeout, 60000}], []). + +-spec ssl_options() -> list(). +ssl_options() -> + UaaEnv = application:get_env(rabbitmq_auth_backend_oauth2, key_config, []), + PeerVerification = proplists:get_value(peer_verification, UaaEnv, verify_none), + CaCertFile = proplists:get_value(cacertfile, UaaEnv), + Depth = proplists:get_value(depth, UaaEnv, 10), + FailIfNoPeerCert = proplists:get_value(fail_if_no_peer_cert, UaaEnv, false), + CrlCheck = proplists:get_value(crl_check, UaaEnv, false), + SslOpts0 = [{verify, PeerVerification}, + {cacertfile, CaCertFile}, + {depth, Depth}, + {fail_if_no_peer_cert, FailIfNoPeerCert}, + {crl_check, CrlCheck}, + {crl_cache, {ssl_crl_cache, {internal, [{http, 10000}]}}}], + case proplists:get_value(hostname_verification, UaaEnv, none) of + wildcard -> + [{customize_hostname_check, [{match_fun, public_key:pkix_verify_hostname_match_fun(https)}]} | SslOpts0]; + none -> + SslOpts0 + end. \ No newline at end of file diff --git a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl index c004c6070c8c..6721bbf5e7ef 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl @@ -58,7 +58,7 @@ update_jwks_signing_keys() -> undefined -> {error, no_jwks_url}; JwksUrl -> - case httpc:request(JwksUrl) of + case uaa_jwks:get(JwksUrl) of {ok, {_, _, JwksBody}} -> KeyList = maps:get(<<"keys">>, jose:decode(erlang:iolist_to_binary(JwksBody)), []), Keys = maps:from_list(lists:map(fun(Key) -> {maps:get(<<"kid">>, Key, undefined), {json, Key}} end, KeyList)), diff --git a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt_jwt.erl b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt_jwt.erl index bb73cf9ae442..aa1cdd8241e5 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt_jwt.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt_jwt.erl @@ -24,7 +24,15 @@ decode(Token) -> end. decode_and_verify(Jwk, Token) -> - case jose_jwt:verify(Jwk, Token) of + UaaEnv = application:get_env(rabbitmq_auth_backend_oauth2, key_config, []), + Verify = + case proplists:get_value(algorithms, UaaEnv) of + undefined -> + jose_jwt:verify(Jwk, Token); + Algs -> + jose_jwt:verify_strict(Jwk, Algs, Token) + end, + case Verify of {true, #jose_jwt{fields = Fields}, _} -> {true, Fields}; {false, #jose_jwt{fields = Fields}, _} -> {false, Fields} end. diff --git a/deps/rabbitmq_auth_backend_oauth2/test/config_schema_SUITE_data/rabbitmq_auth_backend_oauth2.snippets b/deps/rabbitmq_auth_backend_oauth2/test/config_schema_SUITE_data/rabbitmq_auth_backend_oauth2.snippets index 2b7018fdd874..27976d3abc90 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/config_schema_SUITE_data/rabbitmq_auth_backend_oauth2.snippets +++ b/deps/rabbitmq_auth_backend_oauth2/test/config_schema_SUITE_data/rabbitmq_auth_backend_oauth2.snippets @@ -4,7 +4,16 @@ auth_oauth2.additional_scopes_key = my_custom_scope_key auth_oauth2.default_key = id1 auth_oauth2.signing_keys.id1 = test/config_schema_SUITE_data/certs/key.pem - auth_oauth2.signing_keys.id2 = test/config_schema_SUITE_data/certs/cert.pem", + auth_oauth2.signing_keys.id2 = test/config_schema_SUITE_data/certs/cert.pem + auth_oauth2.jwks_url = https://my-jwt-issuer/jwks.json + auth_oauth2.https.cacertfile = test/config_schema_SUITE_data/certs/cacert.pem + auth_oauth2.https.peer_verification = verify_none + auth_oauth2.https.depth = 5 + auth_oauth2.https.fail_if_no_peer_cert = false + auth_oauth2.https.hostname_verification = wildcard + auth_oauth2.https.crl_check = true + auth_oauth2.algorithms.1 = HS256 + auth_oauth2.algorithms.2 = RS256", [ {rabbitmq_auth_backend_oauth2, [ {resource_server_id,<<"new_resource_server_id">>}, @@ -16,7 +25,15 @@ <<"id1">> => {pem, <<"I'm not a certificate">>}, <<"id2">> => {pem, <<"I'm not a certificate">>} } - } + }, + {jwks_url, "https://my-jwt-issuer/jwks.json"}, + {cacertfile, "test/config_schema_SUITE_data/certs/cacert.pem"}, + {peer_verification, verify_none}, + {depth, 5}, + {fail_if_no_peer_cert, false}, + {hostname_verification, wildcard}, + {crl_check, true}, + {algorithms, [<<"HS256">>, <<"RS256">>]} ] } ]} diff --git a/deps/rabbitmq_auth_backend_oauth2/test/jwks_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/jwks_SUITE.erl index 4823a68e2756..2ae29a276123 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/jwks_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/jwks_SUITE.erl @@ -21,7 +21,9 @@ all() -> [ {group, happy_path}, - {group, unhappy_path} + {group, unhappy_path}, + {group, unvalidated_jwks_server}, + {group, no_peer_verification} ]. groups() -> @@ -34,6 +36,7 @@ groups() -> test_successful_connection_with_complex_claim_as_a_list, test_successful_connection_with_complex_claim_as_a_binary, test_successful_connection_with_keycloak_token, + test_successful_connection_with_algorithm_restriction, test_successful_token_refresh ]}, {unhappy_path, [], [ @@ -41,9 +44,12 @@ groups() -> test_failed_connection_with_a_non_token, test_failed_connection_with_a_token_with_insufficient_vhost_permission, test_failed_connection_with_a_token_with_insufficient_resource_permission, + test_failed_connection_with_algorithm_restriction, test_failed_token_refresh_case1, test_failed_token_refresh_case2 - ]} + ]}, + {unvalidated_jwks_server, [], [test_failed_connection_with_unvalidated_jwks_server]}, + {no_peer_verification, [], [{group, happy_path}, {group, unhappy_path}]} ]. %% @@ -69,23 +75,35 @@ end_per_suite(Config) -> fun stop_jwks_server/1 ] ++ rabbit_ct_broker_helpers:teardown_steps()). +init_per_group(no_peer_verification, Config) -> + add_vhosts(Config), + KeyConfig = rabbit_ct_helpers:set_config(?config(key_config, Config), [{jwks_url, ?config(non_strict_jwks_url, Config)}, {peer_verification, verify_none}]), + ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, [rabbitmq_auth_backend_oauth2, key_config, KeyConfig]), + rabbit_ct_helpers:set_config(Config, {key_config, KeyConfig}); init_per_group(_Group, Config) -> - %% The broker is managed by {init,end}_per_testcase(). - lists:foreach(fun(Value) -> - rabbit_ct_broker_helpers:add_vhost(Config, Value) - end, - [<<"vhost1">>, <<"vhost2">>, <<"vhost3">>, <<"vhost4">>]), + add_vhosts(Config), Config. +end_per_group(no_peer_verification, Config) -> + delete_vhosts(Config), + KeyConfig = rabbit_ct_helpers:set_config(?config(key_config, Config), [{jwks_url, ?config(strict_jwks_url, Config)}, {peer_verification, verify_peer}]), + ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, [rabbitmq_auth_backend_oauth2, key_config, KeyConfig]), + rabbit_ct_helpers:set_config(Config, {key_config, KeyConfig}); + end_per_group(_Group, Config) -> - %% The broker is managed by {init,end}_per_testcase(). - lists:foreach(fun(Value) -> - rabbit_ct_broker_helpers:delete_vhost(Config, Value) - end, - [<<"vhost1">>, <<"vhost2">>, <<"vhost3">>, <<"vhost4">>]), + delete_vhosts(Config), Config. +add_vhosts(Config) -> + %% The broker is managed by {init,end}_per_testcase(). + lists:foreach(fun(Value) -> rabbit_ct_broker_helpers:add_vhost(Config, Value) end, + [<<"vhost1">>, <<"vhost2">>, <<"vhost3">>, <<"vhost4">>]). + +delete_vhosts(Config) -> + %% The broker is managed by {init,end}_per_testcase(). + lists:foreach(fun(Value) -> rabbit_ct_broker_helpers:delete_vhost(Config, Value) end, + [<<"vhost1">>, <<"vhost2">>, <<"vhost3">>, <<"vhost4">>]). init_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection_with_a_full_permission_token_and_explicitly_configured_vhost orelse Testcase =:= test_successful_token_refresh -> @@ -107,6 +125,24 @@ init_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection rabbit_ct_helpers:testcase_started(Config, Testcase), Config; +init_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection_with_algorithm_restriction -> + KeyConfig = ?config(key_config, Config), + ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, [rabbitmq_auth_backend_oauth2, key_config, [{algorithms, [<<"HS256">>]} | KeyConfig]]), + rabbit_ct_helpers:testcase_started(Config, Testcase), + Config; + +init_per_testcase(Testcase, Config) when Testcase =:= test_failed_connection_with_algorithm_restriction -> + KeyConfig = ?config(key_config, Config), + ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, [rabbitmq_auth_backend_oauth2, key_config, [{algorithms, [<<"RS256">>]} | KeyConfig]]), + rabbit_ct_helpers:testcase_started(Config, Testcase), + Config; + +init_per_testcase(Testcase, Config) when Testcase =:= test_failed_connection_with_unvalidated_jwks_server -> + KeyConfig = rabbit_ct_helpers:set_config(?config(key_config, Config), {jwks_url, ?config(non_strict_jwks_url, Config)}), + ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, [rabbitmq_auth_backend_oauth2, key_config, KeyConfig]), + rabbit_ct_helpers:testcase_started(Config, Testcase), + Config; + init_per_testcase(Testcase, Config) -> rabbit_ct_helpers:testcase_started(Config, Testcase), Config. @@ -126,6 +162,14 @@ end_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection_ rabbit_ct_helpers:testcase_started(Config, Testcase), Config; +end_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection_with_algorithm_restriction orelse + Testcase =:= test_failed_connection_with_algorithm_restriction orelse + Testcase =:= test_failed_connection_with_unvalidated_jwks_server -> + rabbit_ct_broker_helpers:delete_vhost(Config, <<"vhost1">>), + ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, [rabbitmq_auth_backend_oauth2, key_config, ?config(key_config, Config)]), + rabbit_ct_helpers:testcase_finished(Config, Testcase), + Config; + end_per_testcase(Testcase, Config) -> rabbit_ct_broker_helpers:delete_vhost(Config, <<"vhost1">>), rabbit_ct_helpers:testcase_finished(Config, Testcase), @@ -143,13 +187,27 @@ start_jwks_server(Config) -> %% Assume we don't have more than 100 ports allocated for tests PortBase = rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_ports_base), JwksServerPort = PortBase + 100, + + %% Both URLs direct to the same JWKS server + %% The NonStrictJwksUrl identity cannot be validated while StrictJwksUrl identity can be validated + NonStrictJwksUrl = "https://127.0.0.1:" ++ integer_to_list(JwksServerPort) ++ "/jwks", + StrictJwksUrl = "https://localhost:" ++ integer_to_list(JwksServerPort) ++ "/jwks", + ok = application:set_env(jwks_http, keys, [Jwk]), + {ok, _} = application:ensure_all_started(ssl), {ok, _} = application:ensure_all_started(cowboy), - ok = jwks_http_app:start(JwksServerPort), - KeyConfig = [{jwks_url, "http://127.0.0.1:" ++ integer_to_list(JwksServerPort) ++ "/jwks"}], + CertsDir = ?config(rmq_certsdir, Config), + ok = jwks_http_app:start(JwksServerPort, CertsDir), + KeyConfig = [{jwks_url, StrictJwksUrl}, + {peer_verification, verify_peer}, + {cacertfile, filename:join([CertsDir, "testca", "cacert.pem"])}], ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, [rabbitmq_auth_backend_oauth2, key_config, KeyConfig]), - rabbit_ct_helpers:set_config(Config, {fixture_jwk, Jwk}). + rabbit_ct_helpers:set_config(Config, + [{non_strict_jwks_url, NonStrictJwksUrl}, + {strict_jwks_url, StrictJwksUrl}, + {key_config, KeyConfig}, + {fixture_jwk, Jwk}]). stop_jwks_server(Config) -> ok = jwks_http_app:stop(), @@ -305,7 +363,7 @@ test_successful_token_refresh(Config) -> Conn = open_unmanaged_connection(Config, 0, <<"vhost1">>, <<"username">>, Token), {ok, Ch} = amqp_connection:open_channel(Conn), - {_Algo, Token2} = generate_valid_token(Config, [<<"rabbitmq.configure:vhost1/*">>, + {_Algo2, Token2} = generate_valid_token(Config, [<<"rabbitmq.configure:vhost1/*">>, <<"rabbitmq.write:vhost1/*">>, <<"rabbitmq.read:vhost1/*">>]), ?UTIL_MOD:wait_for_token_to_expire(timer:seconds(Duration)), @@ -321,6 +379,13 @@ test_successful_token_refresh(Config) -> amqp_channel:close(Ch2), close_connection_and_channel(Conn, Ch). +test_successful_connection_with_algorithm_restriction(Config) -> + {_Algo, Token} = rabbit_ct_helpers:get_config(Config, fixture_jwt), + Conn = open_unmanaged_connection(Config, 0, <<"username">>, Token), + {ok, Ch} = amqp_connection:open_channel(Conn), + #'queue.declare_ok'{queue = _} = + amqp_channel:call(Ch, #'queue.declare'{exclusive = true}), + close_connection_and_channel(Conn, Ch). test_failed_connection_with_expired_token(Config) -> {_Algo, Token} = generate_expired_token(Config, [<<"rabbitmq.configure:vhost1/*">>, @@ -359,7 +424,7 @@ test_failed_token_refresh_case1(Config) -> #'queue.declare_ok'{queue = _} = amqp_channel:call(Ch, #'queue.declare'{exclusive = true}), - {_Algo, Token2} = generate_expired_token(Config, [<<"rabbitmq.configure:vhost4/*">>, + {_Algo2, Token2} = generate_expired_token(Config, [<<"rabbitmq.configure:vhost4/*">>, <<"rabbitmq.write:vhost4/*">>, <<"rabbitmq.read:vhost4/*">>]), %% the error is communicated asynchronously via a connection-level error @@ -387,3 +452,13 @@ test_failed_token_refresh_case2(Config) -> amqp_connection:open_channel(Conn)), close_connection(Conn). + +test_failed_connection_with_algorithm_restriction(Config) -> + {_Algo, Token} = rabbit_ct_helpers:get_config(Config, fixture_jwt), + ?assertMatch({error, {auth_failure, _}}, + open_unmanaged_connection(Config, 0, <<"username">>, Token)). + +test_failed_connection_with_unvalidated_jwks_server(Config) -> + {_Algo, Token} = rabbit_ct_helpers:get_config(Config, fixture_jwt), + ?assertMatch({error, {auth_failure, _}}, + open_unmanaged_connection(Config, 0, <<"username">>, Token)). diff --git a/deps/rabbitmq_auth_backend_oauth2/test/jwks_http_app.erl b/deps/rabbitmq_auth_backend_oauth2/test/jwks_http_app.erl index 16353e34f4ef..c745e436f64d 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/jwks_http_app.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/jwks_http_app.erl @@ -1,8 +1,8 @@ -module(jwks_http_app). --export([start/1, stop/0]). +-export([start/2, stop/0]). -start(Port) -> +start(Port, CertsDir) -> Dispatch = cowboy_router:compile( [ @@ -11,8 +11,10 @@ start(Port) -> ]} ] ), - {ok, _} = cowboy:start_clear(jwks_http_listener, - [{port, Port}], + {ok, _} = cowboy:start_tls(jwks_http_listener, + [{port, Port}, + {certfile, filename:join([CertsDir, "server", "cert.pem"])}, + {keyfile, filename:join([CertsDir, "server", "key.pem"])}], #{env => #{dispatch => Dispatch}}), ok. diff --git a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_auth_backend_oauth2_test_util.erl b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_auth_backend_oauth2_test_util.erl index e586ffca8c3d..80b6648352fd 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_auth_backend_oauth2_test_util.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_auth_backend_oauth2_test_util.erl @@ -73,7 +73,7 @@ expired_token_with_scopes(Scopes) -> token_with_scopes_and_expiration(Scopes, os:system_time(seconds) - 10). fixture_token_with_scopes(Scopes) -> - token_with_scopes_and_expiration(Scopes, os:system_time(seconds) + 10). + token_with_scopes_and_expiration(Scopes, os:system_time(seconds) + 30). token_with_scopes_and_expiration(Scopes, Expiration) -> %% expiration is a timestamp with precision in seconds