Skip to content

Commit 101918a

Browse files
WIP Build discovery_endpoint
1 parent f5e200a commit 101918a

File tree

4 files changed

+214
-102
lines changed

4 files changed

+214
-102
lines changed

deps/oauth2_client/include/types.hrl

Lines changed: 34 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -11,64 +11,62 @@
1111
-type oauth_provider_id() :: root | binary().
1212

1313
-record(openid_configuration, {
14-
issuer :: option(uri_string:uri_string()),
15-
token_endpoint :: option(uri_string:uri_string()),
16-
authorization_endpoint :: option(uri_string:uri_string()),
17-
end_session_endpoint :: option(uri_string:uri_string()),
18-
jwks_uri :: option(uri_string:uri_string())
19-
}).
14+
issuer :: option(uri_string:uri_string()),
15+
token_endpoint :: option(uri_string:uri_string()),
16+
authorization_endpoint :: option(uri_string:uri_string()),
17+
end_session_endpoint :: option(uri_string:uri_string()),
18+
jwks_uri :: option(uri_string:uri_string())
19+
}).
2020
-type openid_configuration() :: #openid_configuration{}.
2121

2222
-record(oauth_provider, {
23-
id :: oauth_provider_id(),
24-
issuer :: option(uri_string:uri_string()),
25-
discovery_endpoint_path :: option(uri_string:uri_string()),
26-
discovery_endpoint_params :: option([tuple()]),
27-
token_endpoint :: option(uri_string:uri_string()),
28-
token_endpoint_params :: option([tuple()]),
29-
authorization_endpoint :: option(uri_string:uri_string()),
30-
authorization_endpoint_params :: option([tuple()]),
31-
end_session_endpoint :: option(uri_string:uri_string()),
32-
jwks_uri :: option(uri_string:uri_string()),
33-
jwks_uri_params :: option([tuple()]),
34-
ssl_options :: option(list())
35-
}).
23+
id :: oauth_provider_id(),
24+
issuer :: option(uri_string:uri_string()),
25+
discovery_endpoint :: option(uri_string:uri_string()),
26+
token_endpoint :: option(uri_string:uri_string()),
27+
authorization_endpoint :: option(uri_string:uri_string()),
28+
end_session_endpoint :: option(uri_string:uri_string()),
29+
jwks_uri :: option(uri_string:uri_string()),
30+
ssl_options :: option(list())
31+
}).
32+
33+
-type query_list() :: [{unicode:chardata(), unicode:chardata() | true}].
3634

3735
-type oauth_provider() :: #oauth_provider{}.
3836

3937
-record(access_token_request, {
40-
client_id :: string() | binary(),
41-
client_secret :: string() | binary(),
42-
scope :: string() | binary() | undefined,
43-
timeout :: option(integer())
44-
}).
38+
client_id :: string() | binary(),
39+
client_secret :: string() | binary(),
40+
scope :: string() | binary() | undefined,
41+
timeout :: option(integer())
42+
}).
4543

4644
-type access_token_request() :: #access_token_request{}.
4745

4846
-record(successful_access_token_response, {
49-
access_token :: binary(),
50-
token_type :: binary(),
51-
refresh_token :: option(binary()), % A refresh token SHOULD NOT be included
47+
access_token :: binary(),
48+
token_type :: binary(),
49+
refresh_token :: option(binary()), % A refresh token SHOULD NOT be included
5250
% .. for client-credentials flow.
5351
% https://www.rfc-editor.org/rfc/rfc6749#section-4.4.3
54-
expires_in :: option(integer())
52+
expires_in :: option(integer())
5553
}).
5654

5755
-type successful_access_token_response() :: #successful_access_token_response{}.
5856

5957
-record(unsuccessful_access_token_response, {
60-
error :: integer(),
61-
error_description :: binary() | string() | undefined
58+
error :: integer(),
59+
error_description :: binary() | string() | undefined
6260
}).
6361

6462
-type unsuccessful_access_token_response() :: #unsuccessful_access_token_response{}.
6563

6664
-record(refresh_token_request, {
67-
client_id :: string() | binary(),
68-
client_secret :: string() | binary(),
69-
scope :: string() | binary() | undefined,
70-
refresh_token :: binary(),
71-
timeout :: option(integer())
72-
}).
65+
client_id :: string() | binary(),
66+
client_secret :: string() | binary(),
67+
scope :: string() | binary() | undefined,
68+
refresh_token :: binary(),
69+
timeout :: option(integer())
70+
}).
7371

7472
-type refresh_token_request() :: #refresh_token_request{}.

deps/oauth2_client/src/oauth2_client.erl

Lines changed: 105 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
-export([get_access_token/2, get_expiration_time/1,
99
refresh_access_token/2,
1010
get_oauth_provider/1, get_oauth_provider/2,
11-
get_openid_configuration/2, get_openid_configuration/3,
11+
get_openid_configuration/2,
12+
build_openid_discovery_endpoint/3, build_openid_discovery_endpoint/1,
13+
build_openid_discovery_endpoint/2,
1214
merge_openid_configuration/2,
1315
merge_oauth_provider/2,
1416
extract_ssl_options_as_list/1,
@@ -47,28 +49,64 @@ refresh_access_token(OAuthProvider, Request) ->
4749
append_paths(Path1, Path2) ->
4850
erlang:iolist_to_binary([Path1, Path2]).
4951

50-
-spec get_openid_configuration(uri_string:uri_string(), erlang:iodata() | <<>>,
52+
-spec build_openid_discovery_endpoint(Issuer :: uri_string:uri_string())
53+
-> uri_string:uri_string().
54+
build_openid_discovery_endpoint(Issuer) ->
55+
build_openid_discovery_endpoint(Issuer, undefined, undefined).
56+
57+
-spec build_openid_discovery_endpoint(Issuer :: uri_string:uri_string(),
58+
OpenIdConfigurationPath :: uri_string:uri_string() | undefined)
59+
-> uri_string:uri_string().
60+
build_openid_discovery_endpoint(Issuer, OpenIdConfigurationPath) ->
61+
build_openid_discovery_endpoint(Issuer, OpenIdConfigurationPath, undefined).
62+
63+
-spec build_openid_discovery_endpoint(Issuer :: uri_string:uri_string(),
64+
OpenIdConfigurationPath :: uri_string:uri_string() | undefined,
65+
Params :: query_list()) -> uri_string:uri_string().
66+
67+
build_openid_discovery_endpoint(Issuer, undefined, Params) ->
68+
build_openid_discovery_endpoint(Issuer, ?DEFAULT_OPENID_CONFIGURATION_PATH,
69+
Params);
70+
build_openid_discovery_endpoint(Issuer, OpenIdConfigurationPath, Params) ->
71+
URLMap0 = uri_string:parse(Issuer),
72+
OpenIdPath = ensure_leading_path_separator(OpenIdConfigurationPath),
73+
URLMap1 = URLMap0#{
74+
path := case maps:get(path, URLMap0) of
75+
"/" -> OpenIdPath;
76+
"" -> OpenIdPath;
77+
[] -> OpenIdPath;
78+
P -> append_paths(drop_trailing_path_separator(P), OpenIdPath)
79+
end
80+
},
81+
uri_string:recompose(
82+
case {Params, maps:get(query, URLMap1, undefined)} of
83+
{undefined, undefined} ->
84+
URLMap1;
85+
{_, undefined} ->
86+
URLMap1#{query => uri_string:compose_query(Params)};
87+
{_, Q} ->
88+
URLMap1#{query => uri_string:compose_query(Q ++ Params)}
89+
end).
90+
ensure_leading_path_separator(Path) ->
91+
case string:slice(Path, 0, 1) of
92+
"/" -> Path;
93+
_ -> "/" ++ Path
94+
end.
95+
drop_trailing_path_separator(Path) ->
96+
case string:slice(Path, string:len(Path)-1, 1) of
97+
"/" -> lists:droplast(Path);
98+
_ -> Path
99+
end.
100+
101+
-spec get_openid_configuration(DiscoveryEndpoint :: uri_string:uri_string(),
51102
ssl:tls_option() | []) -> {ok, openid_configuration()} | {error, term()}.
52-
get_openid_configuration(IssuerURI, OpenIdConfigurationPath, TLSOptions) ->
53-
URLMap = uri_string:parse(IssuerURI),
54-
Path = case maps:get(path, URLMap) of
55-
"/" -> OpenIdConfigurationPath;
56-
"" -> OpenIdConfigurationPath;
57-
P -> append_paths(P, OpenIdConfigurationPath)
58-
end,
59-
URL = uri_string:resolve(Path, IssuerURI),
60-
rabbit_log:debug("get_openid_configuration issuer URL ~p (~p)", [URL,
103+
get_openid_configuration(DiscoverEndpoint, TLSOptions) ->
104+
rabbit_log:debug("get_openid_configuration from ~p (~p)", [DiscoverEndpoint,
61105
format_ssl_options(TLSOptions)]),
62106
Options = [],
63-
Response = httpc:request(get, {URL, []}, TLSOptions, Options),
107+
Response = httpc:request(get, {DiscoverEndpoint, []}, TLSOptions, Options),
64108
parse_openid_configuration_response(Response).
65109

66-
-spec get_openid_configuration(uri_string:uri_string(), ssl:tls_option() | []) ->
67-
{ok, openid_configuration()} | {error, term()}.
68-
get_openid_configuration(IssuerURI, TLSOptions) ->
69-
get_openid_configuration(IssuerURI, ?DEFAULT_OPENID_CONFIGURATION_PATH, TLSOptions).
70-
% Returns {ok, with_modidified_oauth_provider} or {ok} if oauth_provider was
71-
% not modified
72110
-spec merge_openid_configuration(openid_configuration(), oauth_provider()) ->
73111
oauth_provider().
74112
merge_openid_configuration(OpendIdConfiguration, OAuthProvider) ->
@@ -179,43 +217,37 @@ update_oauth_provider_endpoints_configuration(OAuthProviderId, OAuthProvider) ->
179217

180218
do_update_oauth_provider_endpoints_configuration(OAuthProvider) ->
181219
case OAuthProvider#oauth_provider.token_endpoint of
182-
undefined ->
183-
do_nothing;
184-
TokenEndpoint ->
185-
application:set_env(rabbitmq_auth_backend_oauth2, token_endpoint, TokenEndpoint)
220+
undefined -> do_nothing;
221+
TokenEndpoint -> set_env(token_endpoint, TokenEndpoint)
186222
end,
187223
case OAuthProvider#oauth_provider.authorization_endpoint of
188-
undefined ->
189-
do_nothing;
190-
AuthzEndpoint ->
191-
application:set_env(rabbitmq_auth_backend_oauth2, authorization_endpoint, AuthzEndpoint)
224+
undefined -> do_nothing;
225+
AuthzEndpoint -> set_env(authorization_endpoint, AuthzEndpoint)
192226
end,
193227
case OAuthProvider#oauth_provider.end_session_endpoint of
194-
undefined ->
195-
do_nothing;
196-
EndSessionEndpoint ->
197-
application:set_env(rabbitmq_auth_backend_oauth2, end_session_endpoint, EndSessionEndpoint)
228+
undefined -> do_nothing;
229+
EndSessionEndpoint -> set_env(end_session_endpoint, EndSessionEndpoint)
198230
end,
199-
List = application:get_env(rabbitmq_auth_backend_oauth2, key_config, []),
231+
List = get_env(key_config, []),
200232
ModifiedList = case OAuthProvider#oauth_provider.jwks_uri of
201233
undefined -> List;
202234
JwksEndPoint -> [{jwks_url, JwksEndPoint} | proplists:delete(jwks_url, List)]
203235
end,
204-
application:set_env(rabbitmq_auth_backend_oauth2, key_config, ModifiedList),
236+
set_env(key_config, ModifiedList),
205237
rabbit_log:debug("Updated oauth_provider details: ~p ", [ format_oauth_provider(OAuthProvider)]),
206238
OAuthProvider.
207239

208240
do_update_oauth_provider_endpoints_configuration(OAuthProviderId, OAuthProvider) ->
209-
OAuthProviders = application:get_env(rabbitmq_auth_backend_oauth2, oauth_providers, #{}),
241+
OAuthProviders = get_env(oauth_providers, #{}),
210242
Proplist = maps:get(OAuthProviderId, OAuthProviders),
211243
ModifiedOAuthProviders = maps:put(OAuthProviderId,
212244
merge_oauth_provider(OAuthProvider, Proplist), OAuthProviders),
213-
application:set_env(rabbitmq_auth_backend_oauth2, oauth_providers, ModifiedOAuthProviders),
245+
set_env(oauth_providers, ModifiedOAuthProviders),
214246
rabbit_log:debug("Replaced oauth_providers "),
215247
OAuthProvider.
216248

217249
use_global_locks_on_all_nodes() ->
218-
case application:get_env(rabbitmq_auth_backend_oauth2, use_global_locks, true) of
250+
case get_env(use_global_locks, true) of
219251
true -> {rabbit_nodes:list_running(), rabbit_nodes:lock_retries()};
220252
_ -> {}
221253
end.
@@ -246,7 +278,7 @@ unlock(LockId) ->
246278

247279
-spec get_oauth_provider(list()) -> {ok, oauth_provider()} | {error, any()}.
248280
get_oauth_provider(ListOfRequiredAttributes) ->
249-
case application:get_env(rabbitmq_auth_backend_oauth2, default_oauth_provider) of
281+
case get_env(default_oauth_provider) of
250282
undefined -> get_oauth_provider_from_keyconfig(ListOfRequiredAttributes);
251283
{ok, DefaultOauthProviderId} ->
252284
rabbit_log:debug("Using default_oauth_provider ~p", [DefaultOauthProviderId]),
@@ -359,18 +391,18 @@ find_missing_attributes(#oauth_provider{} = OAuthProvider, RequiredAttributes) -
359391
intersection(Filtered, RequiredAttributes).
360392

361393
lookup_oauth_provider_from_keyconfig() ->
362-
Issuer = application:get_env(rabbitmq_auth_backend_oauth2, issuer, undefined),
363-
TokenEndpoint = application:get_env(rabbitmq_auth_backend_oauth2, token_endpoint, undefined),
364-
AuthorizationEndpoint = application:get_env(rabbitmq_auth_backend_oauth2, authorization_endpoint, undefined),
365-
EndSessionEndpoint = application:get_env(rabbitmq_auth_backend_oauth2, end_session_endpoint, undefined),
366-
Map = maps:from_list(application:get_env(rabbitmq_auth_backend_oauth2, key_config, [])),
394+
Map = maps:from_list(get_env(key_config, [])),
395+
Issuer = get_env(issuer),
396+
DiscoverEndpoint = build_openid_discovery_endpoint(Issuer,
397+
get_env(discovery_endpoint_path), get_env(discovery_endpoint_params)),
367398
#oauth_provider{
368399
id = root,
369400
issuer = Issuer,
401+
discovery_endpoint = DiscoverEndpoint,
370402
jwks_uri = maps:get(jwks_url, Map, undefined), %% jwks_url not uri . _url is the legacy name
371-
token_endpoint = TokenEndpoint,
372-
authorization_endpoint = AuthorizationEndpoint,
373-
end_session_endpoint = EndSessionEndpoint,
403+
token_endpoint = get_env(token_endpoint),
404+
authorization_endpoint = get_env(authorization_endpoint),
405+
end_session_endpoint = get_env(end_session_endpoint),
374406
ssl_options = extract_ssl_options_as_list(Map)
375407
}.
376408

@@ -431,7 +463,7 @@ get_verify_or_peer_verification(Ssl_options, Default) ->
431463
end.
432464

433465
lookup_oauth_provider_config(OAuth2ProviderId) ->
434-
case application:get_env(rabbitmq_auth_backend_oauth2, oauth_providers) of
466+
case get_env(oauth_providers) of
435467
undefined -> {error, oauth_providers_not_found};
436468
{ok, MapOfProviders} when is_map(MapOfProviders) ->
437469
case maps:get(OAuth2ProviderId, MapOfProviders, undefined) of
@@ -522,14 +554,28 @@ map_to_unsuccessful_access_token_response(Map) ->
522554
error_description = maps:get(?RESPONSE_ERROR_DESCRIPTION, Map, undefined)
523555
}.
524556
map_to_oauth_provider(PropList) when is_list(PropList) ->
557+
Issuer = proplists:get_value(issuer, PropList),
558+
DiscoveryEndpoint = build_openid_discovery_endpoint(Issuer,
559+
proplists:get_value(discovery_endpoint_path, PropList),
560+
proplists:get_value(discovery_endpoint_params, PropList)),
525561
#oauth_provider{
526-
id = proplists:get_value(id, PropList),
527-
issuer = proplists:get_value(issuer, PropList),
528-
token_endpoint = proplists:get_value(token_endpoint, PropList),
529-
authorization_endpoint = proplists:get_value(authorization_endpoint, PropList, undefined),
530-
end_session_endpoint = proplists:get_value(end_session_endpoint, PropList, undefined),
531-
jwks_uri = proplists:get_value(jwks_uri, PropList, undefined),
532-
ssl_options = extract_ssl_options_as_list(maps:from_list(proplists:get_value(https, PropList, [])))
562+
id =
563+
proplists:get_value(id, PropList),
564+
issuer =
565+
Issuer,
566+
discovery_endpoint =
567+
DiscoveryEndpoint,
568+
token_endpoint =
569+
proplists:get_value(token_endpoint, PropList),
570+
authorization_endpoint =
571+
proplists:get_value(authorization_endpoint, PropList, undefined),
572+
end_session_endpoint =
573+
proplists:get_value(end_session_endpoint, PropList, undefined),
574+
jwks_uri =
575+
proplists:get_value(jwks_uri, PropList, undefined),
576+
ssl_options =
577+
extract_ssl_options_as_list(maps:from_list(
578+
proplists:get_value(https, PropList, [])))
533579
}.
534580
map_to_access_token_response(Code, Reason, Headers, Body) ->
535581
case decode_body(proplists:get_value("content-type", Headers, ?CONTENT_JSON), Body) of
@@ -581,3 +627,10 @@ format_oauth_provider(OAuthProvider) ->
581627
OAuthProvider#oauth_provider.end_session_endpoint,
582628
OAuthProvider#oauth_provider.jwks_uri,
583629
format_ssl_options(OAuthProvider#oauth_provider.ssl_options)])).
630+
631+
get_env(Par) ->
632+
application:get_env(rabbitmq_auth_backend_oauth2, Par, undefined).
633+
get_env(Par, Def) ->
634+
application:get_env(rabbitmq_auth_backend_oauth2, Par, Def).
635+
set_env(Par, Val) ->
636+
application:set_env(rabbitmq_auth_backend_oauth2, Par, Val).

0 commit comments

Comments
 (0)