Skip to content

Commit

Permalink
Introduce quirks options (#278) (#282)
Browse files Browse the repository at this point in the history
  • Loading branch information
maennchen authored Oct 22, 2023
1 parent 90d9678 commit 09e449a
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 15 deletions.
6 changes: 3 additions & 3 deletions lib/oidcc/provider_configuration.ex
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,11 @@ defmodule Oidcc.ProviderConfiguration do
...> Oidcc.ProviderConfiguration.decode_configuration(decoded_json)
"""
@doc since: "3.0.0"
@spec decode_configuration(configuration :: map()) ::
@spec decode_configuration(configuration :: map(), opts :: :oidcc_provider_configuration.opts()) ::
{:ok, t()} | {:error, :oidcc_provider_configuration.error()}
def decode_configuration(configuration) do
def decode_configuration(configuration, opts \\ %{}) do
with {:ok, configuration} <-
:oidcc_provider_configuration.decode_configuration(configuration) do
:oidcc_provider_configuration.decode_configuration(configuration, opts) do
{:ok, record_to_struct(configuration)}
end
end
Expand Down
72 changes: 61 additions & 11 deletions src/oidcc_provider_configuration.erl
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,35 @@
-include("oidcc_provider_configuration.hrl").

-export([decode_configuration/1]).
-export([decode_configuration/2]).
-export([load_configuration/1]).
-export([load_configuration/2]).
-export([load_jwks/2]).

-export_type([error/0]).
-export_type([opts/0]).
-export_type([quirks/0]).
-export_type([t/0]).

-type quirks() :: #{
allow_issuer_mismatch => boolean(),
allow_unsafe_http => boolean()
}.
%% Allow Specification Non-compliance
%%
%% <h2>Exceptions</h2>
%%
%% <ul>
%% <li>`allow_issuer_mismatch' - Allow issuer mismatch between config issuer
%% and function parameter</li>
%% <li>`allow_unsafe_http' - Allow unsafe HTTP. Use this for development
%% providers and <strong>never in production</strong>.</li>
%% </ul>

-type opts() :: #{
fallback_expiry => timeout(),
request_opts => oidcc_http_util:request_opts()
request_opts => oidcc_http_util:request_opts(),
quirks => quirks()
}.
%% Configure configuration loading / parsing
%%
Expand Down Expand Up @@ -180,22 +199,37 @@ load_configuration(Issuer0, Opts) ->
RequestUrl = uri_string:resolve(".well-known/openid-configuration", Issuer),
Request = {RequestUrl, []},

Quirks = maps:get(quirks, Opts, #{}),
AllowIssuerMismatch = maps:get(allow_issuer_mismatch, Quirks, false),

maybe
{ok, {{json, ConfigurationMap}, Headers}} ?=
oidcc_http_util:request(get, Request, TelemetryOpts, RequestOpts),
Expiry = headers_to_deadline(Headers, Opts),
{ok, #oidcc_provider_configuration{issuer = Issuer} = Configuration} ?=
decode_configuration(ConfigurationMap),
{ok, {Configuration, Expiry}}
{ok, #oidcc_provider_configuration{issuer = ConfigIssuer} = Configuration} ?=
decode_configuration(ConfigurationMap, #{quirks => Quirks}),
case ConfigIssuer of
Issuer ->
{ok, {Configuration, Expiry}};
_DifferentIssuer when AllowIssuerMismatch -> {ok, {Configuration, Expiry}};
DifferentIssuer when not AllowIssuerMismatch ->
{error, {issuer_mismatch, DifferentIssuer}}
end
else
{ok, #oidcc_provider_configuration{issuer = DifferentIssuer}} ->
{error, {issuer_mismatch, DifferentIssuer}};
{error, Reason} ->
{error, Reason};
{ok, {{_Format, _Body}, _Headers}} ->
{error, invalid_content_type}
end.

%% @see load_configuration/2
%% @since 3.1.0
-spec load_configuration(Issuer) ->
{ok, {Configuration :: t(), Expiry :: pos_integer()}} | {error, error()}
when
Issuer :: uri_string:uri_string().
load_configuration(Issuer) -> load_configuration(Issuer, #{}).

%% @doc Load JWKs into a {@link jose_jwk:key()} record
%%
%% <h2>Examples</h2>
Expand Down Expand Up @@ -240,9 +274,13 @@ load_jwks(JwksUri, Opts) ->
%% oidcc_provider_configuration:decode_configuration(DecodedJson).
%% '''
%% @end
%% @since 3.0.0
-spec decode_configuration(Configuration :: map()) -> {ok, t()} | {error, error()}.
decode_configuration(Configuration) ->
%% @since 3.1.0
-spec decode_configuration(Configuration, Opts) -> {ok, t()} | {error, error()} when
Configuration :: map(), Opts :: opts().
decode_configuration(Configuration, Opts) ->
Quirks = maps:get(quirks, Opts, #{}),
AllowUnsafeHttp = maps:get(allow_unsafe_http, Quirks, false),

maybe
{ok, {
#{
Expand Down Expand Up @@ -309,7 +347,10 @@ decode_configuration(Configuration) ->
{optional, token_endpoint, undefined,
fun oidcc_decode_util:parse_setting_uri/2},
{optional, userinfo_endpoint, undefined,
fun oidcc_decode_util:parse_setting_uri_https/2},
case AllowUnsafeHttp of
true -> fun oidcc_decode_util:parse_setting_uri/2;
false -> fun oidcc_decode_util:parse_setting_uri_https/2
end},
{required, jwks_uri, fun oidcc_decode_util:parse_setting_uri/2},
{optional, registration_endpoint, undefined,
fun oidcc_decode_util:parse_setting_uri/2},
Expand Down Expand Up @@ -383,7 +424,10 @@ decode_configuration(Configuration) ->
{optional, code_challenge_methods_supported, undefined,
fun oidcc_decode_util:parse_setting_binary_list/2},
{optional, end_session_endpoint, undefined,
fun oidcc_decode_util:parse_setting_uri_https/2}
case AllowUnsafeHttp of
true -> fun oidcc_decode_util:parse_setting_uri/2;
false -> fun oidcc_decode_util:parse_setting_uri_https/2
end}
],
#{}
),
Expand Down Expand Up @@ -453,6 +497,12 @@ decode_configuration(Configuration) ->
}}
end.

%% @see decode_configuration/2
%% @since 3.0.0
-spec decode_configuration(Configuration) -> {ok, t()} | {error, error()} when
Configuration :: map().
decode_configuration(Configuration) -> decode_configuration(Configuration, #{}).

-spec headers_to_deadline(Headers, Opts) -> pos_integer() when
Headers :: [{Header :: binary(), Value :: binary()}], Opts :: opts().
headers_to_deadline(Headers, Opts) ->
Expand Down
17 changes: 16 additions & 1 deletion test/oidcc_provider_configuration_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
-export([load_well_known_openid_introspections/1]).
-export([reads_configuration_expiry/1]).

-include_lib("common_test/include/ct.hrl").
-include_lib("jose/include/jose_jwk.hrl").
-include_lib("oidcc/include/oidcc_provider_configuration.hrl").
-include_lib("stdlib/include/assert.hrl").
Expand Down Expand Up @@ -118,4 +117,20 @@ load_well_known_openid_introspections(_Config) ->
)
),

%% Microsoft
?assertMatch(
{error, {issuer_mismatch, _}},
oidcc_provider_configuration:load_configuration(
<<"https://login.microsoftonline.com/common/v2.0">>,
#{}
)
),
?assertMatch(
{ok, {#oidcc_provider_configuration{}, _}},
oidcc_provider_configuration:load_configuration(
<<"https://login.microsoftonline.com/common/v2.0">>,
#{quirks => #{allow_issuer_mismatch => true}}
)
),

ok.
23 changes: 23 additions & 0 deletions test/oidcc_provider_configuration_test.erl
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,29 @@ check_validations_test() ->

ok.

allow_unsafe_http_quirk_test() ->
?assertMatch(
{error, {invalid_config_property, {uri_https, userinfo_endpoint}}},
oidcc_provider_configuration:decode_configuration(
google_merge_json(#{
<<"userinfo_endpoint">> =>
<<"http://example.com">>
})
)
),
?assertMatch(
{ok, _},
oidcc_provider_configuration:decode_configuration(
google_merge_json(#{
<<"userinfo_endpoint">> =>
<<"http://example.com">>
}),
#{quirks => #{allow_unsafe_http => true}}
)
),

ok.

google_merge_json(Merge) ->
PrivDir = code:priv_dir(oidcc),
{ok, ValidConfigString} = file:read_file(PrivDir ++ "/test/fixtures/google-metadata.json"),
Expand Down

0 comments on commit 09e449a

Please sign in to comment.