Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FAPI2 profile support #317

Merged
merged 11 commits into from
Dec 27, 2023
38 changes: 38 additions & 0 deletions lib/oidcc/client_context.ex
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,44 @@ defmodule Oidcc.ClientContext do
|> record_to_struct()
end

@doc """
Apply OpenID Connect / OAuth2 Profiles to the context

See `:oidcc_client_context.apply_profiles/2` for more.

## Examples

iex> {:ok, _pid} =
...> Oidcc.ProviderConfiguration.Worker.start_link(%{
...> issuer: "https://accounts.google.com",
...> name: __MODULE__.GoogleConfigProvider
...> })
...>
...> {:ok, client_context} =
...> Oidcc.ClientContext.from_configuration_worker(
...> __MODULE__.GoogleConfigProvider,
...> "client_id",
...> "client_Secret"
...> )
...>
...> {:ok, %Oidcc.ClientContext{}, %{}} =
...> Oidcc.ClientContext.apply_profiles(
...> client_context,
...> %{profiles: [:fapi2]}
...> )
"""
@doc since: "3.2.0"
@spec apply_profiles(t(), map()) :: {:ok, t(), map()} | {:error, :oidcc_client_context.error()}
paulswartz marked this conversation as resolved.
Show resolved Hide resolved
def apply_profiles(client_context, opts) do
case :oidcc_client_context.apply_profiles(struct_to_record(client_context), opts) do
{:ok, context_record, opts} ->
{:ok, record_to_struct(context_record), opts}

{:error, reason} ->
{:error, reason}
end
end

@impl Oidcc.RecordStruct
def record_to_struct(record) do
record
Expand Down
106 changes: 106 additions & 0 deletions priv/test/fixtures/fapi2-metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
{
"issuer": "https://my.provider",
"authorization_endpoint": "https://my.provider/auth",
"registration_endpoint": "https://my.provider/register",
"device_authorization_endpoint": "https://my.provider/device/code",
"token_endpoint": "https://my.provider/token",
"introspection_endpoint": "https://my.provider/introspection",
"userinfo_endpoint": "https://my.provider/userinfo",
"revocation_endpoint": "https://my.provider/revoke",
"jwks_uri": "https://my.provider/jwks",
"response_types_supported": [
"code",
"token",
"id_token",
"code token",
"code id_token",
"token id_token",
"code token id_token",
"none"
],
"subject_types_supported": [
"public"
],
"id_token_signing_alg_values_supported": [
"none",
"HS256",
"RS256",
"EdDSA"
],
"scopes_supported": [
"openid",
"email",
"profile"
],
"token_endpoint_auth_methods_supported": [
"client_secret_post",
"client_secret_basic",
"private_key_jwt",
"unsupporeted_auth"
],
"claims_supported": [
"aud",
"email",
"email_verified",
"exp",
"family_name",
"given_name",
"iat",
"iss",
"locale",
"name",
"picture",
"sub"
],
"code_challenge_methods_supported": [
"plain",
"S256"
],
"grant_types_supported": [
"authorization_code",
"refresh_token",
"urn:ietf:params:oauth:grant-type:device_code",
"urn:ietf:params:oauth:grant-type:jwt-bearer"
],
"userinfo_signing_alg_values_supported": [
"none",
"HS256",
"RS256",
"RS384",
"RS512",
"PS256",
"PS384",
"PS512",
"ES256",
"ES256K",
"ES384",
"ES512",
"EdDSA"
],
"userinfo_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"
],
"userinfo_encryption_enc_values_supported": [
"A128CBC-HS256",
"A192CBC-HS384",
"A256CBC-HS512",
"A128GCM",
"A192GCM",
"A256GCM"
]
}
3 changes: 3 additions & 0 deletions priv/test/fixtures/jwk-ed25519.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIK2JHugMJkquFakgWVf2McF16O6Vkf3rA3PAcutRFmbK
-----END PRIVATE KEY-----
48 changes: 32 additions & 16 deletions src/oidcc.erl
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,16 @@ when
Opts :: oidcc_authorization:opts() | oidcc_client_context:opts(),
Uri :: uri_string:uri_string().
create_redirect_url(ProviderConfigurationWorkerName, ClientId, ClientSecret, Opts) ->
{ClientContextOpts, OtherOpts} = extract_client_context_opts(Opts),
{ClientContextOpts, OtherOpts0} = extract_client_context_opts(Opts),
maybe
{ok, ClientContext} ?=
{ok, ClientContext0} ?=
oidcc_client_context:from_configuration_worker(
ProviderConfigurationWorkerName,
ClientId,
ClientSecret,
ClientContextOpts
),
{ok, ClientContext, OtherOpts} = oidcc_profile:apply_profiles(ClientContext0, OtherOpts0),
oidcc_authorization:create_redirect_url(ClientContext, OtherOpts)
end.

Expand Down Expand Up @@ -128,16 +129,19 @@ retrieve_token(
{ClientContextOpts, OtherOpts} = extract_client_context_opts(Opts),

RefreshJwksFun = oidcc_jwt_util:refresh_jwks_fun(ProviderConfigurationWorkerName),
OptsWithRefresh = maps_put_new(refresh_jwks, RefreshJwksFun, OtherOpts),
OptsWithRefresh0 = maps_put_new(refresh_jwks, RefreshJwksFun, OtherOpts),

maybe
{ok, ClientContext} ?=
{ok, ClientContext0} ?=
oidcc_client_context:from_configuration_worker(
ProviderConfigurationWorkerName,
ClientId,
ClientSecret,
ClientContextOpts
),
{ok, ClientContext, OptsWithRefresh} = oidcc_profile:apply_profiles(
ClientContext0, OptsWithRefresh0
),
oidcc_token:retrieve(AuthCode, ClientContext, OptsWithRefresh)
end.

Expand Down Expand Up @@ -190,16 +194,17 @@ retrieve_userinfo(
ClientSecret,
Opts
) ->
{ClientContextOpts, OtherOpts} = extract_client_context_opts(Opts),
{ClientContextOpts, OtherOpts0} = extract_client_context_opts(Opts),

maybe
{ok, ClientContext} ?=
{ok, ClientContext0} ?=
oidcc_client_context:from_configuration_worker(
ProviderConfigurationWorkerName,
ClientId,
ClientSecret,
ClientContextOpts
),
{ok, ClientContext, OtherOpts} = oidcc_profile:apply_profiles(ClientContext0, OtherOpts0),
oidcc_userinfo:retrieve(Token, ClientContext, OtherOpts)
end.

Expand Down Expand Up @@ -260,16 +265,19 @@ refresh_token(
{ClientContextOpts, OtherOpts} = extract_client_context_opts(Opts),

RefreshJwksFun = oidcc_jwt_util:refresh_jwks_fun(ProviderConfigurationWorkerName),
OptsWithRefresh = maps_put_new(refresh_jwks, RefreshJwksFun, OtherOpts),
OptsWithRefresh0 = maps_put_new(refresh_jwks, RefreshJwksFun, OtherOpts),

maybe
{ok, ClientContext} ?=
{ok, ClientContext0} ?=
oidcc_client_context:from_configuration_worker(
ProviderConfigurationWorkerName,
ClientId,
ClientSecret,
ClientContextOpts
),
{ok, ClientContext, OptsWithRefresh} = oidcc_profile:apply_profiles(
ClientContext0, OptsWithRefresh0
),
oidcc_token:refresh(RefreshToken, ClientContext, OptsWithRefresh)
end.

Expand Down Expand Up @@ -314,16 +322,17 @@ introspect_token(
ClientSecret,
Opts
) ->
{ClientContextOpts, OtherOpts} = extract_client_context_opts(Opts),
{ClientContextOpts, OtherOpts0} = extract_client_context_opts(Opts),

maybe
{ok, ClientContext} ?=
{ok, ClientContext0} ?=
oidcc_client_context:from_configuration_worker(
ProviderConfigurationWorkerName,
ClientId,
ClientSecret,
ClientContextOpts
),
{ok, ClientContext, OtherOpts} = oidcc_profile:apply_profiles(ClientContext0, OtherOpts0),
oidcc_token_introspection:introspect(Token, ClientContext, OtherOpts)
end.

Expand Down Expand Up @@ -371,16 +380,19 @@ jwt_profile_token(Subject, ProviderConfigurationWorkerName, ClientId, ClientSecr
{ClientContextOpts, OtherOpts} = extract_client_context_opts(Opts),

RefreshJwksFun = oidcc_jwt_util:refresh_jwks_fun(ProviderConfigurationWorkerName),
OptsWithRefresh = maps_put_new(refresh_jwks, RefreshJwksFun, OtherOpts),
OptsWithRefresh0 = maps_put_new(refresh_jwks, RefreshJwksFun, OtherOpts),

maybe
{ok, ClientContext} ?=
{ok, ClientContext0} ?=
oidcc_client_context:from_configuration_worker(
ProviderConfigurationWorkerName,
ClientId,
ClientSecret,
ClientContextOpts
),
{ok, ClientContext, OptsWithRefresh} = oidcc_profile:apply_profiles(
ClientContext0, OptsWithRefresh0
),
oidcc_token:jwt_profile(Subject, ClientContext, Jwk, OptsWithRefresh)
end.

Expand Down Expand Up @@ -415,16 +427,19 @@ client_credentials_token(ProviderConfigurationWorkerName, ClientId, ClientSecret
{ClientContextOpts, OtherOpts} = extract_client_context_opts(Opts),

RefreshJwksFun = oidcc_jwt_util:refresh_jwks_fun(ProviderConfigurationWorkerName),
OptsWithRefresh = maps_put_new(refresh_jwks, RefreshJwksFun, OtherOpts),
OptsWithRefresh0 = maps_put_new(refresh_jwks, RefreshJwksFun, OtherOpts),

maybe
{ok, ClientContext} ?=
{ok, ClientContext0} ?=
oidcc_client_context:from_configuration_worker(
ProviderConfigurationWorkerName,
ClientId,
ClientSecret,
ClientContextOpts
),
{ok, ClientContext, OptsWithRefresh} = oidcc_profile:apply_profiles(
ClientContext0, OptsWithRefresh0
),
oidcc_token:client_credentials(ClientContext, OptsWithRefresh)
end.

Expand Down Expand Up @@ -464,16 +479,17 @@ when
ClientId :: binary(),
Opts :: oidcc_logout:initiate_url_opts() | oidcc_client_context:unauthenticated_opts().
initiate_logout_url(Token, ProviderConfigurationWorkerName, ClientId, Opts) ->
{ClientContextOpts, OtherOpts} = extract_client_context_opts(Opts),
{ClientContextOpts, OtherOpts0} = extract_client_context_opts(Opts),

maybe
{ok, ClientContext} ?=
{ok, ClientContext0} ?=
oidcc_client_context:from_configuration_worker(
ProviderConfigurationWorkerName,
ClientId,
unauthenticated,
ClientContextOpts
),
{ok, ClientContext, OtherOpts} = oidcc_profile:apply_profiles(ClientContext0, OtherOpts0),
oidcc_logout:initiate_url(Token, ClientContext, OtherOpts)
end.

Expand Down
Loading