Skip to content

Commit 6c86442

Browse files
Merge pull request #11218 from rabbitmq/configure-oidc-endpoints
Configure OIDC end_session_endpoint
2 parents bd111f0 + 341f3da commit 6c86442

File tree

13 files changed

+270
-103
lines changed

13 files changed

+270
-103
lines changed

deps/oauth2_client/include/oauth2_client.hrl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
-define(RESPONSE_ISSUER, <<"issuer">>).
4242
-define(RESPONSE_TOKEN_ENDPOINT, <<"token_endpoint">>).
4343
-define(RESPONSE_AUTHORIZATION_ENDPOINT, <<"authorization_endpoint">>).
44+
-define(RESPONSE_END_SESSION_ENDPOINT, <<"end_session_endpoint">>).
4445
-define(RESPONSE_JWKS_URI, <<"jwks_uri">>).
4546
-define(RESPONSE_TLS_OPTIONS, <<"ssl_options">>).
4647

@@ -51,6 +52,7 @@
5152
issuer :: option(uri_string:uri_string()),
5253
token_endpoint :: option(uri_string:uri_string()),
5354
authorization_endpoint :: option(uri_string:uri_string()),
55+
end_session_endpoint :: option(uri_string:uri_string()),
5456
jwks_uri :: option(uri_string:uri_string()),
5557
ssl_options :: option(list())
5658
}).

deps/oauth2_client/src/oauth2_client.erl

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,13 @@
77
-module(oauth2_client).
88
-export([get_access_token/2, get_expiration_time/1,
99
refresh_access_token/2,
10-
get_oauth_provider/1, get_oauth_provider/2,
10+
get_oauth_provider/1, get_oauth_provider/2,
1111
extract_ssl_options_as_list/1
1212
]).
1313

1414
-include("oauth2_client.hrl").
15-
-spec get_access_token(oauth_provider_id() | oauth_provider(), access_token_request()) ->
15+
-spec get_access_token(oauth_provider(), access_token_request()) ->
1616
{ok, successful_access_token_response()} | {error, unsuccessful_access_token_response() | any()}.
17-
get_access_token(OAuth2ProviderId, Request) when is_binary(OAuth2ProviderId) ->
18-
rabbit_log:debug("get_access_token using OAuth2ProviderId:~p and client_id:~p",
19-
[OAuth2ProviderId, Request#access_token_request.client_id]),
20-
case get_oauth_provider(OAuth2ProviderId, [token_endpoint]) of
21-
{error, _Error } = Error0 -> Error0;
22-
{ok, Provider} -> get_access_token(Provider, Request)
23-
end;
24-
2517
get_access_token(OAuthProvider, Request) ->
2618
rabbit_log:debug("get_access_token using OAuthProvider:~p and client_id:~p",
2719
[OAuthProvider, Request#access_token_request.client_id]),
@@ -102,14 +94,20 @@ do_update_oauth_provider_endpoints_configuration(OAuthProvider) ->
10294
case OAuthProvider#oauth_provider.token_endpoint of
10395
undefined ->
10496
do_nothing;
105-
TokenEndPoint ->
106-
application:set_env(rabbitmq_auth_backend_oauth2, token_endpoint, TokenEndPoint)
97+
TokenEndpoint ->
98+
application:set_env(rabbitmq_auth_backend_oauth2, token_endpoint, TokenEndpoint)
10799
end,
108100
case OAuthProvider#oauth_provider.authorization_endpoint of
109101
undefined ->
110102
do_nothing;
111-
AuthzEndPoint ->
112-
application:set_env(rabbitmq_auth_backend_oauth2, authorization_endpoint, AuthzEndPoint)
103+
AuthzEndpoint ->
104+
application:set_env(rabbitmq_auth_backend_oauth2, authorization_endpoint, AuthzEndpoint)
105+
end,
106+
case OAuthProvider#oauth_provider.end_session_endpoint of
107+
undefined ->
108+
do_nothing;
109+
EndSessionEndpoint ->
110+
application:set_env(rabbitmq_auth_backend_oauth2, end_session_endpoint, EndSessionEndpoint)
113111
end,
114112
List = application:get_env(rabbitmq_auth_backend_oauth2, key_config, []),
115113
ModifiedList = case OAuthProvider#oauth_provider.jwks_uri of
@@ -125,17 +123,21 @@ do_update_oauth_provider_endpoints_configuration(OAuthProviderId, OAuthProvider)
125123
LookupProviderPropList = maps:get(OAuthProviderId, OAuthProviders),
126124
ModifiedList0 = case OAuthProvider#oauth_provider.token_endpoint of
127125
undefined -> LookupProviderPropList;
128-
TokenEndPoint -> [{token_endpoint, TokenEndPoint} | LookupProviderPropList]
126+
TokenEndpoint -> [{token_endpoint, TokenEndpoint} | LookupProviderPropList]
129127
end,
130128
ModifiedList1 = case OAuthProvider#oauth_provider.authorization_endpoint of
131129
undefined -> ModifiedList0;
132-
AuthzEndPoint -> [{authorization_endpoint, AuthzEndPoint} | ModifiedList0]
130+
AuthzEndpoint -> [{authorization_endpoint, AuthzEndpoint} | ModifiedList0]
133131
end,
134-
ModifiedList2 = case OAuthProvider#oauth_provider.jwks_uri of
132+
ModifiedList2 = case OAuthProvider#oauth_provider.end_session_endpoint of
135133
undefined -> ModifiedList1;
136-
JwksEndPoint -> [{jwks_uri, JwksEndPoint} | ModifiedList1]
134+
EndSessionEndpoint -> [{end_session_endpoint, EndSessionEndpoint} | ModifiedList1]
135+
end,
136+
ModifiedList3 = case OAuthProvider#oauth_provider.jwks_uri of
137+
undefined -> ModifiedList2;
138+
JwksEndPoint -> [{jwks_uri, JwksEndPoint} | ModifiedList2]
137139
end,
138-
ModifiedOAuthProviders = maps:put(OAuthProviderId, ModifiedList2, OAuthProviders),
140+
ModifiedOAuthProviders = maps:put(OAuthProviderId, ModifiedList3, OAuthProviders),
139141
application:set_env(rabbitmq_auth_backend_oauth2, oauth_providers, ModifiedOAuthProviders),
140142
rabbit_log:debug("Replacing oauth_providers ~p", [ ModifiedOAuthProviders]),
141143
OAuthProvider.
@@ -283,11 +285,15 @@ find_missing_attributes(#oauth_provider{} = OAuthProvider, RequiredAttributes) -
283285
lookup_oauth_provider_from_keyconfig() ->
284286
Issuer = application:get_env(rabbitmq_auth_backend_oauth2, issuer, undefined),
285287
TokenEndpoint = application:get_env(rabbitmq_auth_backend_oauth2, token_endpoint, undefined),
288+
AuthorizationEndpoint = application:get_env(rabbitmq_auth_backend_oauth2, authorization_endpoint, undefined),
289+
EndSessionEndpoint = application:get_env(rabbitmq_auth_backend_oauth2, end_session_endpoint, undefined),
286290
Map = maps:from_list(application:get_env(rabbitmq_auth_backend_oauth2, key_config, [])),
287291
#oauth_provider{
288292
issuer = Issuer,
289293
jwks_uri = maps:get(jwks_url, Map, undefined), %% jwks_url not uri . _url is the legacy name
290294
token_endpoint = TokenEndpoint,
295+
authorization_endpoint = AuthorizationEndpoint,
296+
end_session_endpoint = EndSessionEndpoint,
291297
ssl_options = extract_ssl_options_as_list(Map)
292298
}.
293299

@@ -445,6 +451,7 @@ map_to_oauth_provider(Map) when is_map(Map) ->
445451
issuer = maps:get(?RESPONSE_ISSUER, Map),
446452
token_endpoint = maps:get(?RESPONSE_TOKEN_ENDPOINT, Map, undefined),
447453
authorization_endpoint = maps:get(?RESPONSE_AUTHORIZATION_ENDPOINT, Map, undefined),
454+
end_session_endpoint = maps:get(?RESPONSE_END_SESSION_ENDPOINT, Map, undefined),
448455
jwks_uri = maps:get(?RESPONSE_JWKS_URI, Map, undefined)
449456
};
450457

@@ -453,6 +460,7 @@ map_to_oauth_provider(PropList) when is_list(PropList) ->
453460
issuer = proplists:get_value(issuer, PropList),
454461
token_endpoint = proplists:get_value(token_endpoint, PropList),
455462
authorization_endpoint = proplists:get_value(authorization_endpoint, PropList, undefined),
463+
end_session_endpoint = proplists:get_value(end_session_endpoint, PropList, undefined),
456464
jwks_uri = proplists:get_value(jwks_uri, PropList, undefined),
457465
ssl_options = extract_ssl_options_as_list(maps:from_list(proplists:get_value(https, PropList, [])))
458466
}.

deps/oauth2_client/test/system_SUITE.erl

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
{issuer, build_issuer("http") },
101101
{authorization_endpoint, <<"http://localhost:8000/authorize">>},
102102
{token_endpoint, build_token_endpoint_uri("http")},
103+
{end_session_endpoint, <<"http://localhost:8000/logout">>},
103104
{jwks_uri, build_jwks_uri("http")}
104105
]}
105106
]
@@ -117,6 +118,7 @@
117118
{issuer, build_issuer("https") },
118119
{authorization_endpoint, <<"https://localhost:8000/authorize">>},
119120
{token_endpoint, build_token_endpoint_uri("https")},
121+
{end_session_endpoint, <<"http://localhost:8000/logout">>},
120122
{jwks_uri, build_jwks_uri("https")}
121123
]}
122124
]
@@ -166,8 +168,7 @@ groups() ->
166168
denies_access_token,
167169
auth_server_error,
168170
non_json_payload,
169-
grants_refresh_token,
170-
grants_access_token_using_oauth_provider_id
171+
grants_refresh_token
171172
]},
172173
{verify_get_oauth_provider, [], [
173174
get_oauth_provider,
@@ -262,6 +263,8 @@ configure_all_oauth_provider_settings(Config) ->
262263
application:set_env(rabbitmq_auth_backend_oauth2, issuer, OAuthProvider#oauth_provider.issuer),
263264
application:set_env(rabbitmq_auth_backend_oauth2, oauth_providers, OAuthProviders),
264265
application:set_env(rabbitmq_auth_backend_oauth2, token_endpoint, OAuthProvider#oauth_provider.token_endpoint),
266+
application:set_env(rabbitmq_auth_backend_oauth2, end_sessione_endpoint, OAuthProvider#oauth_provider.end_session_endpoint),
267+
application:set_env(rabbitmq_auth_backend_oauth2, authorization_endpoint, OAuthProvider#oauth_provider.authorization_endpoint),
265268
KeyConfig = [ { jwks_url, OAuthProvider#oauth_provider.jwks_uri } ] ++
266269
case OAuthProvider#oauth_provider.ssl_options of
267270
undefined ->
@@ -313,6 +316,8 @@ end_per_testcase(_, Config) ->
313316
application:unset_env(rabbitmq_auth_backend_oauth2, oauth_providers),
314317
application:unset_env(rabbitmq_auth_backend_oauth2, issuer),
315318
application:unset_env(rabbitmq_auth_backend_oauth2, token_endpoint),
319+
application:unset_env(rabbitmq_auth_backend_oauth2, authorization_endpoint),
320+
application:unset_env(rabbitmq_auth_backend_oauth2, end_session_endpoint),
316321
application:unset_env(rabbitmq_auth_backend_oauth2, key_config),
317322
case ?config(group, Config) of
318323
http_up ->
@@ -340,15 +345,6 @@ grants_access_token_dynamically_resolving_oauth_provider(Config) ->
340345
?assertEqual(proplists:get_value(token_type, JsonPayload), TokenType),
341346
?assertEqual(proplists:get_value(access_token, JsonPayload), AccessToken).
342347

343-
grants_access_token_using_oauth_provider_id(Config) ->
344-
#{request := #{parameters := Parameters},
345-
response := [ {code, 200}, {content_type, _CT}, {payload, JsonPayload}] } = lookup_expectation(token_endpoint, Config),
346-
347-
{ok, #successful_access_token_response{access_token = AccessToken, token_type = TokenType} } =
348-
oauth2_client:get_access_token(?config(oauth_provider_id, Config), build_access_token_request(Parameters)),
349-
?assertEqual(proplists:get_value(token_type, JsonPayload), TokenType),
350-
?assertEqual(proplists:get_value(access_token, JsonPayload), AccessToken).
351-
352348
grants_access_token(Config) ->
353349
#{request := #{parameters := Parameters},
354350
response := [ {code, 200}, {content_type, _CT}, {payload, JsonPayload}] }
@@ -417,11 +413,19 @@ get_oauth_provider_given_oauth_provider_id(Config) ->
417413
= lookup_expectation(get_openid_configuration, Config),
418414

419415
ct:log("get_oauth_provider ~p", [?config(oauth_provider_id, Config)]),
420-
{ok, #oauth_provider{issuer = Issuer, token_endpoint = TokenEndPoint, jwks_uri = Jwks_uri}} =
421-
oauth2_client:get_oauth_provider(?config(oauth_provider_id, Config), [issuer, token_endpoint, jwks_uri]),
416+
{ok, #oauth_provider{
417+
issuer = Issuer,
418+
token_endpoint = TokenEndPoint,
419+
authorization_endpoint = AuthorizationEndpoint,
420+
end_session_endpoint = EndSessionEndpoint,
421+
jwks_uri = Jwks_uri}} =
422+
oauth2_client:get_oauth_provider(?config(oauth_provider_id, Config),
423+
[issuer, token_endpoint, jwks_uri, authorization_endpoint, end_session_endpoint]),
422424

423425
?assertEqual(proplists:get_value(issuer, JsonPayload), Issuer),
424426
?assertEqual(proplists:get_value(token_endpoint, JsonPayload), TokenEndPoint),
427+
?assertEqual(proplists:get_value(authorization_endpoint, JsonPayload), AuthorizationEndpoint),
428+
?assertEqual(proplists:get_value(end_session_endpoint, JsonPayload), EndSessionEndpoint),
425429
?assertEqual(proplists:get_value(jwks_uri, JsonPayload), Jwks_uri).
426430

427431

deps/rabbitmq_auth_backend_oauth2/priv/schema/rabbitmq_auth_backend_oauth2.schema

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,16 @@
163163
"rabbitmq_auth_backend_oauth2.key_config.jwks_url",
164164
[{datatype, string}, {validators, ["uri", "https_uri"]}]}.
165165

166+
{mapping,
167+
"auth_oauth2.end_session_endpoint",
168+
"rabbitmq_auth_backend_oauth2.end_session_endpoint",
169+
[{datatype, string}, {validators, ["uri", "https_uri"]}]}.
170+
171+
{mapping,
172+
"auth_oauth2.authorization_endpoint",
173+
"rabbitmq_auth_backend_oauth2.authorization_endpoint",
174+
[{datatype, string}, {validators, ["uri", "https_uri"]}]}.
175+
166176
{mapping,
167177
"auth_oauth2.https.peer_verification",
168178
"rabbitmq_auth_backend_oauth2.key_config.peer_verification",
@@ -240,6 +250,16 @@
240250
[{datatype, string}, {validators, ["uri", "https_uri"]}]
241251
}.
242252

253+
{mapping,
254+
"auth_oauth2.oauth_providers.$name.end_session_endpoint",
255+
"rabbitmq_auth_backend_oauth2.oauth_providers",
256+
[{datatype, string}, {validators, ["uri", "https_uri"]}]}.
257+
258+
{mapping,
259+
"auth_oauth2.oauth_providers.$name.authorization_endpoint",
260+
"rabbitmq_auth_backend_oauth2.oauth_providers",
261+
[{datatype, string}, {validators, ["uri", "https_uri"]}]}.
262+
243263
{mapping,
244264
"auth_oauth2.oauth_providers.$name.https.verify",
245265
"rabbitmq_auth_backend_oauth2.oauth_providers",

deps/rabbitmq_auth_backend_oauth2/test/config_schema_SUITE_data/rabbitmq_auth_backend_oauth2.snippets

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@
129129
auth_oauth2.oauth_providers.uaa.issuer = https://uaa
130130
auth_oauth2.oauth_providers.keycloak.token_endpoint = https://keycloak/token
131131
auth_oauth2.oauth_providers.keycloak.jwks_uri = https://keycloak/keys
132+
auth_oauth2.oauth_providers.keycloak.authorization_endpoint = https://keycloak/authorize
133+
auth_oauth2.oauth_providers.keycloak.end_session_endpoint = https://keycloak/logout
132134
auth_oauth2.oauth_providers.keycloak.https.cacertfile = /mnt/certs/ca_certificate.pem
133135
auth_oauth2.oauth_providers.keycloak.https.verify = verify_none",
134136
[
@@ -149,6 +151,8 @@
149151
{verify, verify_none},
150152
{cacertfile, "/mnt/certs/ca_certificate.pem"}
151153
]},
154+
{end_session_endpoint, <<"https://keycloak/logout">>},
155+
{authorization_endpoint, <<"https://keycloak/authorize">>},
152156
{token_endpoint, <<"https://keycloak/token">>},
153157
{jwks_uri, <<"https://keycloak/keys">>}
154158
]

deps/rabbitmq_management/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ define PROJECT_APP_EXTRA_KEYS
2222
endef
2323

2424
DEPS = rabbit_common rabbit amqp_client cowboy cowlib rabbitmq_web_dispatch rabbitmq_management_agent oauth2_client
25-
TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers proper
25+
TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers proper amqp10_client
2626
LOCAL_DEPS += ranch ssl crypto public_key
2727

2828
# FIXME: Add Ranch as a BUILD_DEPS to be sure the correct version is picked.

deps/rabbitmq_management/priv/www/js/oidc-oauth/helper.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ function oauth_initialize_user_manager(resource_server) {
103103
audience: resource_server.id, // required by oauth0
104104
},
105105
};
106+
if (resource_server.end_session_endpoint != "") {
107+
oidcSettings.metadataSeed = {
108+
end_session_endpoint: resource_server.end_session_endpoint
109+
}
110+
}
106111
if (resource_server.oauth_client_secret != "") {
107112
oidcSettings.client_secret = resource_server.oauth_client_secret;
108113
}

deps/rabbitmq_management/selenium/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
"description": "",
55
"main": "index.js",
66
"scripts": {
7-
"test": "mocha --recursive --trace-warnings --timeout 45000",
87
"fakeportal": "node fakeportal/app.js",
98
"fakeproxy": "node fakeportal/proxy.js",
109
"amqp10_roundtriptest": "eval $(cat $ENV_FILE ) &&./run-amqp10-roundtriptest",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export PUBLIC_RABBITMQ_HOST=proxy:9090
22
export RABBITMQ_HOST_FOR_PROXY=rabbitmq:15672
3+
export SELENIUM_INTERACTION_DELAY=250
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export PUBLIC_RABBITMQ_HOST=localhost:9090
22
export RABBITMQ_HOST_FOR_PROXY=host.docker.internal:15672
3+
export SELENIUM_INTERACTION_DELAY=250

deps/rabbitmq_management/selenium/test/pageobjects/BasePage.js

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,14 @@ module.exports = class BasePage {
1717
driver
1818
timeout
1919
polling
20+
interactionDelay
2021

2122
constructor (webdriver) {
2223
this.driver = webdriver
2324
this.timeout = parseInt(process.env.SELENIUM_TIMEOUT) || 1000 // max time waiting to locate an element. Should be less that test timeout
2425
this.polling = parseInt(process.env.SELENIUM_POLLING) || 500 // how frequent selenium searches for an element
26+
this.interactionDelay = parseInt(process.env.SELENIUM_INTERACTION_DELAY) || 0 // slow down interactions (when rabbit is behind a http proxy)
27+
console.log("Interaction Delay : " + this.interactionDelay)
2528
}
2629

2730

@@ -79,10 +82,11 @@ module.exports = class BasePage {
7982
return this.click(QUEUES_AND_STREAMS_TAB)
8083
}
8184
async waitForQueuesTab() {
85+
await this.driver.sleep(250)
8286
return this.waitForDisplayed(QUEUES_AND_STREAMS_TAB)
8387
}
8488

85-
async clickOnStreamTab () {
89+
async clickOnStreamTab () {
8690
return this.click(STREAM_CONNECTIONS_TAB)
8791
}
8892
async waitForStreamConnectionsTab() {
@@ -153,8 +157,8 @@ module.exports = class BasePage {
153157
async isDisplayed(locator) {
154158
try {
155159
element = await driver.findElement(locator)
156-
console.log("element:"+element)
157-
return this.driver.wait(until.elementIsVisible(element), this.timeout / 2,
160+
161+
return this.driver.wait(until.elementIsVisible(element), this.timeout,
158162
'Timed out after [timeout=' + this.timeout + ';polling=' + this.polling + '] awaiting till visible ' + element,
159163
this.polling / 2)
160164
}catch(error) {
@@ -186,7 +190,13 @@ module.exports = class BasePage {
186190

187191

188192
async waitForDisplayed (locator) {
189-
return this.waitForVisible(await this.waitForLocated(locator))
193+
if (this.interactionDelay && this.interactionDelay > 0) await this.driver.sleep(this.interactionDelay)
194+
try {
195+
return this.waitForVisible(await this.waitForLocated(locator))
196+
}catch(error) {
197+
console.error("Failed to waitForDisplayed for locator " + locator)
198+
throw error
199+
}
190200
}
191201

192202
async getText (locator) {
@@ -200,6 +210,8 @@ module.exports = class BasePage {
200210
}
201211

202212
async click (locator) {
213+
if (this.interactionDelay) await this.driver.sleep(this.interactionDelay)
214+
203215
const element = await this.waitForDisplayed(locator)
204216
try {
205217
return element.click()

0 commit comments

Comments
 (0)