Skip to content

Commit

Permalink
Improve Test Coverage (#256)
Browse files Browse the repository at this point in the history
  • Loading branch information
maennchen authored Sep 25, 2023
1 parent 4665d3c commit b81ba6c
Show file tree
Hide file tree
Showing 11 changed files with 759 additions and 26 deletions.
1 change: 1 addition & 0 deletions priv/test/fixtures/example-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
],
"userinfo_signing_alg_values_supported": [
"none",
"HS256",
"RS256",
"RS384",
"RS512",
Expand Down
7 changes: 5 additions & 2 deletions src/oidcc_client_registration.erl
Original file line number Diff line number Diff line change
Expand Up @@ -272,8 +272,11 @@ encode(#oidcc_client_registration{
tos_uri => TosUri,
jwks =>
case Jwks of
undefined -> undefined;
_ -> jose_jwk:to_map(Jwks)
undefined ->
undefined;
_ ->
{_KeyType, KeyMap} = jose_jwk:to_map(Jwks),
KeyMap
end,
sector_identifier_uri => SectorIdentifierUri,
subject_type => SubjectType,
Expand Down
12 changes: 9 additions & 3 deletions src/oidcc_jwt_util.erl
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,10 @@ verify_claims(Claims, ExpClaims) ->

%% @private
-spec client_secret_oct_keys(AllowedAlgorithms, ClientSecret) -> jose_jwk:key() | none when
AllowedAlgorithms :: [binary()],
AllowedAlgorithms :: [binary()] | undefined,
ClientSecret :: binary().
client_secret_oct_keys(undefined, _ClientSecret) ->
none;
client_secret_oct_keys(AllowedAlgorithms, ClientSecret) ->
case
lists:member(<<"HS256">>, AllowedAlgorithms) or
Expand Down Expand Up @@ -193,10 +195,14 @@ sign(Jwt, Jwk, [Algorithm | RestAlgorithms]) ->
-spec encrypt(
Jwt :: binary(),
Jwk :: jose_jwk:key(),
SupportedAlgorithms :: [binary()],
SupportedEncValues :: [binary()]
SupportedAlgorithms :: [binary()] | undefined,
SupportedEncValues :: [binary()] | undefined
) ->
{ok, binary()} | {error, no_supported_alg_or_key}.
encrypt(_Jwt, _Jwk, undefined, _SupportedEncValues) ->
{error, no_supported_alg_or_key};
encrypt(_Jwt, _Jwk, _SupportedAlgorithms, undefined) ->
{error, no_supported_alg_or_key};
encrypt(Jwt, Jwk, SupportedAlgorithms, SupportedEncValues) ->
encrypt(Jwt, Jwk, SupportedAlgorithms, SupportedEncValues, SupportedEncValues).

Expand Down
19 changes: 10 additions & 9 deletions src/oidcc_userinfo.erl
Original file line number Diff line number Diff line change
Expand Up @@ -156,18 +156,19 @@ validate_userinfo_body({jwt, UserinfoBody}, ClientContext, Opts) ->
ClientContext,
#oidcc_provider_configuration{issuer = Issuer} = Configuration,
ExpectedSubject = maps:get(expected_subject, Opts),
ExpectedClaims0 = [
{<<"aud">>, ClientId},
{<<"iss">>, Issuer}
],
ExpectedClaims =
case maps:get(expected_subject, Opts) of
any -> ExpectedClaims0;
ExpectedSubject -> [{<<"sub">>, ExpectedSubject} | ExpectedClaims0]
end,
validate_userinfo_token(
UserinfoBody,
ClientContext,
maps:put(
expected_claims,
[
{<<"aud">>, ClientId},
{<<"iss">>, Issuer},
{<<"sub">>, ExpectedSubject}
],
Opts
)
maps:put(expected_claims, ExpectedClaims, Opts)
).

-spec validate_userinfo_token(Token, ClientContext, Opts) ->
Expand Down
7 changes: 7 additions & 0 deletions test/oidcc/client_registration/response_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
defmodule Oidcc.ClientRegistration.ResponseTest do
use ExUnit.Case, async: true

alias Oidcc.ClientRegistration.Response

doctest Response
end
13 changes: 13 additions & 0 deletions test/oidcc_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
-export([retrieve_userinfo/1]).

-include_lib("common_test/include/ct.hrl").
-include_lib("oidcc/include/oidcc_token.hrl").
-include_lib("stdlib/include/assert.hrl").

all() ->
Expand Down Expand Up @@ -110,6 +111,18 @@ refresh_token(_Config) ->
#{expected_subject => <<"some sub">>}
),

{error, Reason} =
oidcc:refresh_token(
#oidcc_token{
refresh = #oidcc_token_refresh{token = <<"invalid_refresh_token">>},
id = #oidcc_token_id{claims = #{<<"sub">> => <<"some sub">>}}
},
ConfigurationPid,
<<"client_id">>,
<<"client_secret">>,
#{}
),

?assertMatch({http_error, 400, _}, Reason),

ok.
Expand Down
87 changes: 87 additions & 0 deletions test/oidcc_authorization_test.erl
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,39 @@ create_redirect_url_test() ->

ok.

unsupported_grant_type_test() ->
PrivDir = code:priv_dir(oidcc),

{ok, ValidConfigString} = file:read_file(PrivDir ++ "/test/fixtures/example-metadata.json"),
{ok, Configuration0} = oidcc_provider_configuration:decode_configuration(
jose:decode(ValidConfigString)
),
Configuration = Configuration0#oidcc_provider_configuration{
grant_types_supported = []
},

Jwks = jose_jwk:from_pem_file(PrivDir ++ "/test/fixtures/jwk.pem"),

ClientId = <<"client_id">>,
RedirectUri = <<"https://my.server/return">>,

ClientContext =
oidcc_client_context:from_manual(Configuration, Jwks, ClientId, <<"client_secret">>),

Opts =
#{
redirect_uri => RedirectUri,
client_id => ClientId,
url_extension => [{<<"test">>, <<"id">>}]
},

?assertMatch(
{error, {grant_type_not_supported, authorization_code}},
oidcc_authorization:create_redirect_url(ClientContext, Opts)
),

ok.

create_redirect_url_with_request_object_test() ->
PrivDir = code:priv_dir(oidcc),

Expand Down Expand Up @@ -218,3 +251,57 @@ create_redirect_url_with_request_object_test() ->
),

ok.

create_redirect_url_with_invalid_request_object_test() ->
PrivDir = code:priv_dir(oidcc),

%% Enable none algorithm for test
jose:unsecured_signing(true),

{ok, ValidConfigString} = file:read_file(PrivDir ++ "/test/fixtures/example-metadata.json"),
{ok, Configuration0} = oidcc_provider_configuration:decode_configuration(
jose:decode(ValidConfigString)
),

Configuration = Configuration0#oidcc_provider_configuration{
request_parameter_supported = true,
request_object_signing_alg_values_supported = [
<<"unknown">>
]
},

ClientId = <<"client_id">>,
ClientSecret = <<"at_least_32_character_client_secret">>,

Jwks0 = jose_jwk:from_pem_file(PrivDir ++ "/test/fixtures/jwk.pem"),
Jwks = Jwks0#jose_jwk{fields = #{<<"use">> => <<"sig">>}},

RedirectUri = <<"https://my.server/return">>,

ClientContext =
oidcc_client_context:from_manual(Configuration, Jwks, ClientId, ClientSecret),

{ok, Url} = oidcc_authorization:create_redirect_url(ClientContext, #{
redirect_uri => RedirectUri
}),

?assertMatch(<<"https://my.provider/auth?scope=", _/binary>>, iolist_to_binary(Url)),

#{query := QueryString} = uri_string:parse(Url),
QueryParams0 = uri_string:dissect_query(QueryString),
QueryParams1 = lists:map(
fun({Key, Value}) -> {list_to_binary(Key), list_to_binary(Value)} end, QueryParams0
),
QueryParams = maps:from_list(QueryParams1),

?assertEqual(
#{
<<"client_id">> => <<"client_id">>,
<<"redirect_uri">> => <<"https://my.server/return">>,
<<"response_type">> => <<"code">>,
<<"scope">> => <<"openid">>
},
QueryParams
),

ok.
141 changes: 139 additions & 2 deletions test/oidcc_client_registration_test.erl
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,20 @@ register_test() ->
Configuration} =
oidcc_provider_configuration:decode_configuration(jose:decode(ConfigurationBinary)),

Jwks = jose_jwk:from_pem_file(PrivDir ++ "/test/fixtures/jwk.pem"),

RedirectUri = <<"https://example.com/oidcc/callback">>,

Registration = #oidcc_client_registration{
redirect_uris = [RedirectUri]
redirect_uris = [RedirectUri],
jwks = Jwks
},

ClientId = <<"client_id">>,

ResponseJson = jose:encode(#{
client_id => ClientId
client_id => ClientId,
client_id_issued_at => erlang:system_time(second)
}),

TelemetryRef =
Expand All @@ -45,6 +49,7 @@ register_test() ->
_Opts
) ->
RegistrationEndpoint = ReqEndpoint,

?assertMatch(
#{
<<"application_type">> := <<"web">>,
Expand All @@ -59,6 +64,9 @@ register_test() ->
end,
ok = meck:expect(httpc, request, HttpFun),

{ok, Response} = oidcc_client_registration:register(Configuration, Registration, #{
initial_access_token => <<"token">>
}),
{ok, Response} = oidcc_client_registration:register(Configuration, Registration, #{}),

?assertMatch(#oidcc_client_registration_response{client_id = ClientId}, Response),
Expand Down Expand Up @@ -86,3 +94,132 @@ register_test() ->
meck:unload(httpc),

ok.

registration_not_supported_test() ->
{ok, _} = application:ensure_all_started(oidcc),

PrivDir = code:priv_dir(oidcc),

{ok, ConfigurationBinary} = file:read_file(PrivDir ++ "/test/fixtures/example-metadata.json"),
{ok, Configuration0} =
oidcc_provider_configuration:decode_configuration(jose:decode(ConfigurationBinary)),

Configuration = Configuration0#oidcc_provider_configuration{registration_endpoint = undefined},

RedirectUri = <<"https://example.com/oidcc/callback">>,

Registration = #oidcc_client_registration{
redirect_uris = [RedirectUri]
},

?assertMatch(
{error, registration_not_supported},
oidcc_client_registration:register(Configuration, Registration, #{})
),

ok.

registration_invalid_response_test() ->
{ok, _} = application:ensure_all_started(oidcc),

PrivDir = code:priv_dir(oidcc),

{ok, ConfigurationBinary} = file:read_file(PrivDir ++ "/test/fixtures/example-metadata.json"),
{ok,
#oidcc_provider_configuration{registration_endpoint = RegistrationEndpoint} =
Configuration} =
oidcc_provider_configuration:decode_configuration(jose:decode(ConfigurationBinary)),

RedirectUri = <<"https://example.com/oidcc/callback">>,

ClientId = <<"client_id">>,

JwtRegistration = #oidcc_client_registration{
redirect_uris = [RedirectUri],
client_name = <<"jwt">>
},

ErrorRegistration = #oidcc_client_registration{
redirect_uris = [RedirectUri],
client_name = <<"error">>
},

InvalidFieldsRegistration = #oidcc_client_registration{
redirect_uris = [RedirectUri],
client_name = <<"invalid_fields">>
},

InvalidIssuedAtRegistration = #oidcc_client_registration{
redirect_uris = [RedirectUri],
client_name = <<"invalid_client_id_issued_at">>
},

InvalidClientIdRegistration = #oidcc_client_registration{
redirect_uris = [RedirectUri],
client_name = <<"invalid_client_id">>
},

ok = meck:new(httpc, [no_link]),
HttpFun =
fun(
post,
{ReqEndpoint, _Header, "application/json", Body},
_HttpOpts,
_Opts
) ->
RegistrationEndpoint = ReqEndpoint,

case jose:decode(Body) of
#{<<"client_name">> := <<"jwt">>} ->
{ok, {
{"HTTP/1.1", 200, "OK"},
[{"content-type", "application/jwt"}],
<<"irrelevant">>
}};
#{<<"client_name">> := <<"error">>} ->
{error, reason};
#{<<"client_name">> := <<"invalid_fields">>} ->
{ok, {
{"HTTP/1.1", 200, "OK"}, [{"content-type", "application/json"}], <<"{}">>
}};
#{<<"client_name">> := <<"invalid_client_id_issued_at">>} ->
{ok, {
{"HTTP/1.1", 200, "OK"},
[{"content-type", "application/json"}],
jose:encode(#{client_id => ClientId, client_id_issued_at => <<"invalid">>})
}};
#{<<"client_name">> := <<"invalid_client_id">>} ->
{ok, {
{"HTTP/1.1", 200, "OK"},
[{"content-type", "application/json"}],
jose:encode(#{client_id => 7})
}}
end
end,
ok = meck:expect(httpc, request, HttpFun),

?assertMatch(
{error, invalid_content_type},
oidcc_client_registration:register(Configuration, JwtRegistration, #{})
),
?assertMatch(
{error, reason}, oidcc_client_registration:register(Configuration, ErrorRegistration, #{})
),
?assertMatch(
{error, {missing_config_property, client_id}},
oidcc_client_registration:register(Configuration, InvalidFieldsRegistration, #{})
),
?assertMatch(
{error, {invalid_config_property, {number, client_id_issued_at}}},
oidcc_client_registration:register(Configuration, InvalidIssuedAtRegistration, #{})
),
?assertMatch(
{error, {invalid_config_property, {binary, client_id}}},
oidcc_client_registration:register(Configuration, InvalidClientIdRegistration, #{})
),

true = meck:validate(httpc),

meck:unload(httpc),

ok.
Loading

0 comments on commit b81ba6c

Please sign in to comment.