Skip to content

Commit

Permalink
feat: FAPI2 profile support
Browse files Browse the repository at this point in the history
  • Loading branch information
paulswartz committed Dec 26, 2023
1 parent 4cbce04 commit 39aa09e
Show file tree
Hide file tree
Showing 11 changed files with 674 additions and 55 deletions.
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()}
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

0 comments on commit 39aa09e

Please sign in to comment.