Skip to content

Commit

Permalink
fix: application:get_env/2 returns {ok, Value}
Browse files Browse the repository at this point in the history
  • Loading branch information
paulswartz committed Nov 21, 2023
1 parent 71896a0 commit 31a6dfb
Show file tree
Hide file tree
Showing 4 changed files with 285 additions and 4 deletions.
2 changes: 1 addition & 1 deletion src/oidcc_authorization.erl
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ attempt_request_object(QueryParams, #oidcc_client_context{
MaxClockSkew =
case application:get_env(oidcc, max_clock_skew) of
undefined -> 0;
ClockSkew -> ClockSkew
{ok, ClockSkew} -> ClockSkew
end,

Claims = maps:merge(
Expand Down
6 changes: 3 additions & 3 deletions src/oidcc_token.erl
Original file line number Diff line number Diff line change
Expand Up @@ -775,7 +775,7 @@ verify_exp_claim(#{<<"exp">> := Expiry}) ->
MaxClockSkew =
case application:get_env(oidcc, max_clock_skew) of
undefined -> 0;
ClockSkew -> ClockSkew
{ok, ClockSkew} -> ClockSkew
end,
case erlang:system_time(second) > Expiry + MaxClockSkew of
true -> {error, token_expired};
Expand All @@ -787,7 +787,7 @@ verify_nbf_claim(#{<<"nbf">> := Expiry}) ->
MaxClockSkew =
case application:get_env(oidcc, max_clock_skew) of
undefined -> 0;
ClockSkew -> ClockSkew
{ok, ClockSkew} -> ClockSkew
end,
case erlang:system_time(second) < Expiry - MaxClockSkew of
true -> {error, token_not_yet_valid};
Expand Down Expand Up @@ -1071,7 +1071,7 @@ token_request_claims(#oidcc_client_context{
MaxClockSkew =
case application:get_env(oidcc, max_clock_skew) of
undefined -> 0;
ClockSkew -> ClockSkew
{ok, ClockSkew} -> ClockSkew
end,

#{
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 @@ -252,6 +252,93 @@ create_redirect_url_with_request_object_test() ->

ok.

create_redirect_url_with_request_object_and_max_clock_skew_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, #oidcc_provider_configuration{} = Configuration0} = oidcc_provider_configuration:decode_configuration(
jose:decode(ValidConfigString)
),

Configuration = Configuration0#oidcc_provider_configuration{
request_parameter_supported = true,
request_object_signing_alg_values_supported = [
<<"none">>,
<<"HS256">>,
<<"RS256">>,
<<"PS256">>,
<<"ES256">>,
<<"EdDSA">>
],
request_object_encryption_alg_values_supported = [
<<"RSA1_5">>,
<<"RSA-OAEP">>,
<<"RSA-OAEP-256">>,
<<"RSA-OAEP-384">>,
<<"RSA-OAEP-512">>,
<<"ECDH-ES">>,
<<"ECDH-ES+A128KW">>,
<<"ECDH-ES+A192KW">>,
<<"ECDH-ES+A256KW">>,
<<"A128KW">>,
<<"A192KW">>,
<<"A256KW">>,
<<"A128GCMKW">>,
<<"A192GCMKW">>,
<<"A256GCMKW">>,
<<"dir">>
],
request_object_encryption_enc_values_supported = [
<<"A128CBC-HS256">>,
<<"A192CBC-HS384">>,
<<"A256CBC-HS512">>,
<<"A128GCM">>,
<<"A192GCM">>,
<<"A256GCM">>
]
},

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">> => <<"enc">>}},

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

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

application:set_env(oidcc, max_clock_skew, 10),
{ok, Url} = oidcc_authorization:create_redirect_url(ClientContext, #{
redirect_uri => RedirectUri
}),
application:unset_env(oidcc, max_clock_skew),

#{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),

{SignedToken, _} = jose_jwe:block_decrypt(Jwks, maps:get(<<"request">>, QueryParams)),

{true, Jwt, _} = jose_jwt:verify(jose_jwk:from_oct(ClientSecret), SignedToken),

#jose_jwt{
fields = #{
<<"nbf">> := ClientNbf
}
} = Jwt,

?assert(ClientNbf < os:system_time(seconds) - 5),

ok.

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

Expand Down
194 changes: 194 additions & 0 deletions test/oidcc_token_test.erl
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,101 @@ retrieve_hs256_test() ->

ok.

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

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

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

ClientId = <<"client_id">>,
ClientSecret = <<"at_least_32_character_client_secret">>,
LocalEndpoint = <<"https://my.server/auth">>,
AuthCode = <<"1234567890">>,
AccessToken = <<"access_token">>,
RefreshToken = <<"refresh_token">>,
Claims =
#{
<<"iss">> => Issuer,
<<"sub">> => <<"sub">>,
<<"aud">> => ClientId,
<<"nbf">> => erlang:system_time(second) + 5,
<<"iat">> => erlang:system_time(second) + 5,
<<"exp">> => erlang:system_time(second) + 15,
<<"at_hash">> => <<"hrOQHuo3oE6FR82RIiX1SA">>
},

Jwk = jose_jwk:from_oct(<<"at_least_32_character_client_secret">>),

Jwt = jose_jwt:from(Claims),
Jws = #{<<"alg">> => <<"HS256">>},
{_Jws, Token} = jose_jws:compact(jose_jwt:sign(Jwk, Jws, Jwt)),

OtherJwk = jose_jwk:from_file(PrivDir ++ "/test/fixtures/openid-certification-jwks.json"),

TokenData =
jsx:encode(#{
<<"access_token">> => AccessToken,
<<"token_type">> => <<"Bearer">>,
<<"id_token">> => Token,
<<"scope">> => <<"profile openid">>,
<<"refresh_token">> => RefreshToken
}),

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

ok = meck:new(httpc, [no_link]),
HttpFun =
fun(
post,
{ReqTokenEndpoint, _Header, "application/x-www-form-urlencoded", _Body},
_HttpOpts,
_Opts
) ->
TokenEndpoint = ReqTokenEndpoint,
{ok, {{"HTTP/1.1", 200, "OK"}, [{"content-type", "application/json"}], TokenData}}
end,
ok = meck:expect(httpc, request, HttpFun),

?assertMatch(
{error, token_not_yet_valid},
oidcc_token:retrieve(
AuthCode,
ClientContext,
#{redirect_uri => LocalEndpoint}
)
),

application:set_env(oidcc, max_clock_skew, 10),

?assertMatch(
{ok, #oidcc_token{
id = #oidcc_token_id{token = Token, claims = Claims},
access = #oidcc_token_access{token = AccessToken},
refresh = #oidcc_token_refresh{token = RefreshToken},
scope = [<<"profile">>, <<"openid">>]
}},
oidcc_token:retrieve(
AuthCode,
ClientContext,
#{redirect_uri => LocalEndpoint}
)
),

true = meck:validate(httpc),

meck:unload(httpc),

application:unset_env(oidcc, max_clock_skew),

ok.

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

Expand Down Expand Up @@ -454,6 +549,105 @@ auth_method_client_secret_jwt_test() ->

ok.

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

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

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

#oidcc_provider_configuration{token_endpoint = TokenEndpoint, issuer = Issuer} =
Configuration = Configuration0#oidcc_provider_configuration{
token_endpoint_auth_methods_supported = [
<<"client_secret_jwt">>, <<"client_secret_basic">>
],
token_endpoint_auth_signing_alg_values_supported = [<<"HS256">>]
},

ClientId = <<"client_id">>,
ClientSecret = <<"client_secret">>,
LocalEndpoint = <<"https://my.server/auth">>,
AuthCode = <<"1234567890">>,
AccessToken = <<"access_token">>,
RefreshToken = <<"refresh_token">>,
Claims =
#{
<<"iss">> => Issuer,
<<"sub">> => <<"sub">>,
<<"aud">> => ClientId,
<<"iat">> => erlang:system_time(second),
<<"exp">> => erlang:system_time(second) + 10,
<<"at_hash">> => <<"hrOQHuo3oE6FR82RIiX1SA">>
},

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

Jwt = jose_jwt:from(Claims),
Jws = #{<<"alg">> => <<"RS256">>},
{_Jws, Token} =
jose_jws:compact(
jose_jwt:sign(Jwk, Jws, Jwt)
),

TokenData =
jsx:encode(#{
<<"access_token">> => AccessToken,
<<"token_type">> => <<"Bearer">>,
<<"id_token">> => Token,
<<"scope">> => <<"profile openid">>,
<<"refresh_token">> => RefreshToken
}),

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

ok = meck:new(httpc, [no_link]),
HttpFun =
fun(
post,
{ReqTokenEndpoint, _, "application/x-www-form-urlencoded", Body},
_HttpOpts,
_Opts
) ->
TokenEndpoint = ReqTokenEndpoint,
BodyMap = maps:from_list(uri_string:dissect_query(Body)),

ClientAssertion = maps:get("client_assertion", BodyMap),

{true, ClientAssertionJwt, _} = jose_jwt:verify(
jose_jwk:from_oct(ClientSecret), binary:list_to_bin(ClientAssertion)
),

#jose_jwt{
fields = #{
<<"nbf">> := ClientTokenNbf
}
} = ClientAssertionJwt,

?assert(ClientTokenNbf < os:system_time(seconds) - 5),

{ok, {{"HTTP/1.1", 200, "OK"}, [{"content-type", "application/json"}], TokenData}}
end,
ok = meck:expect(httpc, request, HttpFun),

application:set_env(oidcc, max_clock_skew, 10),

oidcc_token:retrieve(
AuthCode,
ClientContext,
#{redirect_uri => LocalEndpoint}
),

true = meck:validate(httpc),

meck:unload(httpc),

application:unset_env(oidcc, max_clock_skew),

ok.

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

Expand Down

0 comments on commit 31a6dfb

Please sign in to comment.