Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,22 @@ The certification is funded as an
- Response Mode: `default`
- Client Registration Type: `dynamic_client`

#### FAPI2-Security-Profile-ID2: Relying Party (client) test

- **Relevant for Certification: Yes**
- Id: `fapi2-security-profile-id2-client-test-plan`
- Client Authentication Type: `private_key_jwt`
- Sender COnstraining: `dpop`
- FAPI Client Type: `oidc`
- FAPI Profile: `plain_fapi`
- Server
- jwks: `./artifacts/server.priv.jwkset`
- Client
- client_id: `client_id`
- scope: `openid profile`
- redirect_uri: `http://localhost:4000/callback`
- jwks: `./artifacts/client.pub.jwkset`

## How to Execute the tests

### Setup
Expand Down
23 changes: 23 additions & 0 deletions artifacts/GENERATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generation of keys

```bash
# Generate Private Keys
openssl ecparam -genkey -name prime256v1 -noout -out artifacts/client.priv.pem
openssl ecparam -genkey -name prime256v1 -noout -out artifacts/server.priv.pem

# Generate Public Keys
openssl ec -in ./artifacts/client.priv.pem -pubout -out ./artifacts/client.pub.pem
openssl ec -in ./artifacts/server.priv.pem -pubout -out ./artifacts/server.pub.pem

# Generate JWKs for public & private keys
npx eckles ./artifacts/client.priv.pem > ./artifacts/client.priv.jwk
npx eckles ./artifacts/client.pub.pem > ./artifacts/client.pub.jwk
npx eckles ./artifacts/server.priv.pem > ./artifacts/server.priv.jwk
npx eckles ./artifacts/server.pub.pem > ./artifacts/server.pub.jwk

# Generate JWK sets
cat ./artifacts/client.priv.jwk| jq '{"keys": [. * {"use": "sig", "kid": "client", "alg": "ES256"}]}' > ./artifacts/client.priv.jwkset
cat ./artifacts/client.pub.jwk| jq '{"keys": [. * {"use": "sig", "kid": "client", "alg": "ES256"}]}' > ./artifacts/client.pub.jwkset
cat ./artifacts/server.priv.jwk| jq '{"keys": [. * {"use": "sig", "kid": "server", "alg": "ES256"}]}' > ./artifacts/server.priv.jwkset
cat ./artifacts/server.pub.jwk| jq '{"keys": [. * {"use": "sig", "kid": "server", "alg": "ES256"}]}' > ./artifacts/server.pub.jwkset
```
7 changes: 7 additions & 0 deletions artifacts/client.priv.jwk
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"kty": "EC",
"crv": "P-256",
"d": "I5XC9VN5oJUQpXO5LqN5jNm9NTPAo_8zUkiUhJlgoRk",
"x": "QaXzjB1aMQRoXcgFTQUneS6EBVaQHQObzaHJjKV6_fM",
"y": "fljC46dVTTQFsyB4Iap7pmTzolhLX2KOtajOI2kEh7g"
}
14 changes: 14 additions & 0 deletions artifacts/client.priv.jwkset
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"keys": [
{
"kty": "EC",
"crv": "P-256",
"d": "I5XC9VN5oJUQpXO5LqN5jNm9NTPAo_8zUkiUhJlgoRk",
"x": "QaXzjB1aMQRoXcgFTQUneS6EBVaQHQObzaHJjKV6_fM",
"y": "fljC46dVTTQFsyB4Iap7pmTzolhLX2KOtajOI2kEh7g",
"use": "sig",
"kid": "client",
"alg": "ES256"
}
]
}
5 changes: 5 additions & 0 deletions artifacts/client.priv.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEICOVwvVTeaCVEKVzuS6jeYzZvTUzwKP/M1JIlISZYKEZoAoGCCqGSM49
AwEHoUQDQgAEQaXzjB1aMQRoXcgFTQUneS6EBVaQHQObzaHJjKV6/fN+WMLjp1VN
NAWzIHghqnumZPOiWEtfYo61qM4jaQSHuA==
-----END EC PRIVATE KEY-----
6 changes: 6 additions & 0 deletions artifacts/client.pub.jwk
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"kty": "EC",
"crv": "P-256",
"x": "QaXzjB1aMQRoXcgFTQUneS6EBVaQHQObzaHJjKV6_fM",
"y": "fljC46dVTTQFsyB4Iap7pmTzolhLX2KOtajOI2kEh7g"
}
13 changes: 13 additions & 0 deletions artifacts/client.pub.jwkset
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"keys": [
{
"kty": "EC",
"crv": "P-256",
"x": "QaXzjB1aMQRoXcgFTQUneS6EBVaQHQObzaHJjKV6_fM",
"y": "fljC46dVTTQFsyB4Iap7pmTzolhLX2KOtajOI2kEh7g",
"use": "sig",
"kid": "client",
"alg": "ES256"
}
]
}
4 changes: 4 additions & 0 deletions artifacts/client.pub.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEQaXzjB1aMQRoXcgFTQUneS6EBVaQ
HQObzaHJjKV6/fN+WMLjp1VNNAWzIHghqnumZPOiWEtfYo61qM4jaQSHuA==
-----END PUBLIC KEY-----
7 changes: 7 additions & 0 deletions artifacts/server.priv.jwk
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"kty": "EC",
"crv": "P-256",
"d": "6-YakWKMTt1LEZDlEuuUpKjrMuhpYEaFeors8RKdxr8",
"x": "-e56Pjr4gW6fk73WGO1hyldhEG7X4pnkD_U6Tclqe58",
"y": "RgLVVgsLbPeGZEZs2_9buxp6f8RqsJza37JQJZOySCM"
}
14 changes: 14 additions & 0 deletions artifacts/server.priv.jwkset
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"keys": [
{
"kty": "EC",
"crv": "P-256",
"d": "6-YakWKMTt1LEZDlEuuUpKjrMuhpYEaFeors8RKdxr8",
"x": "-e56Pjr4gW6fk73WGO1hyldhEG7X4pnkD_U6Tclqe58",
"y": "RgLVVgsLbPeGZEZs2_9buxp6f8RqsJza37JQJZOySCM",
"use": "sig",
"kid": "server",
"alg": "ES256"
}
]
}
5 changes: 5 additions & 0 deletions artifacts/server.priv.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIOvmGpFijE7dSxGQ5RLrlKSo6zLoaWBGhXqK7PESnca/oAoGCCqGSM49
AwEHoUQDQgAE+e56Pjr4gW6fk73WGO1hyldhEG7X4pnkD/U6Tclqe59GAtVWCwts
94ZkRmzb/1u7Gnp/xGqwnNrfslAlk7JIIw==
-----END EC PRIVATE KEY-----
6 changes: 6 additions & 0 deletions artifacts/server.pub.jwk
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"kty": "EC",
"crv": "P-256",
"x": "-e56Pjr4gW6fk73WGO1hyldhEG7X4pnkD_U6Tclqe58",
"y": "RgLVVgsLbPeGZEZs2_9buxp6f8RqsJza37JQJZOySCM"
}
13 changes: 13 additions & 0 deletions artifacts/server.pub.jwkset
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"keys": [
{
"kty": "EC",
"crv": "P-256",
"x": "-e56Pjr4gW6fk73WGO1hyldhEG7X4pnkD_U6Tclqe58",
"y": "RgLVVgsLbPeGZEZs2_9buxp6f8RqsJza37JQJZOySCM",
"use": "sig",
"kid": "server",
"alg": "ES256"
}
]
}
4 changes: 4 additions & 0 deletions artifacts/server.pub.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+e56Pjr4gW6fk73WGO1hyldhEG7X
4pnkD/U6Tclqe59GAtVWCwts94ZkRmzb/1u7Gnp/xGqwnNrfslAlk7JIIw==
-----END PUBLIC KEY-----
160 changes: 100 additions & 60 deletions lib/conformance/auth_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ defmodule Conformance.AuthController do

require Logger

alias Oidcc.ClientContext
alias Oidcc.ProviderConfiguration
alias Oidcc.Token

plug Oidcc.Plug.AuthorizationCallback,
[
provider: Conformance.ConfigWorker,
client_id: &Conformance.RegisterClient.client_id/0,
client_secret: &Conformance.RegisterClient.client_secret/0,
client_context_opts: &Conformance.RegisterClient.client_context_opts/0,
client_profile_opts: Conformance.RegisterClient.client_profile_opts(),
redirect_uri: &__MODULE__.redirect_url/0
]
when action in [:callback]
Expand All @@ -20,6 +24,8 @@ defmodule Conformance.AuthController do
provider: Conformance.ConfigWorker,
client_id: &Conformance.RegisterClient.client_id/0,
client_secret: &Conformance.RegisterClient.client_secret/0,
client_context_opts: &Conformance.RegisterClient.client_context_opts/0,
client_profile_opts: Conformance.RegisterClient.client_profile_opts(),
redirect_uri: &__MODULE__.redirect_url/0,
scopes: ["openid", "profile"]
]
Expand All @@ -44,50 +50,52 @@ defmodule Conformance.AuthController do
Logger.info("Retrieved Token: #{inspect(token, pretty: true)}")
Logger.info("Retrieved Userinfo: #{inspect(userinfo, pretty: true)}")

case Oidcc.ProviderConfiguration.Worker.get_provider_configuration(Conformance.ConfigWorker) do
%Oidcc.ProviderConfiguration{end_session_endpoint: :undefined} ->
conn =
with {:ok, {refreshed_token, refreshed_userinfo}} <- maybe_refresh(token) do
send_resp(
conn,
200,
inspect(
%{
token: token,
userinfo: userinfo,
refreshed_token: refreshed_token,
refreshed_userinfo: refreshed_userinfo
},
pretty: true
)
provider_configuration =
Oidcc.ProviderConfiguration.Worker.get_provider_configuration(Conformance.ConfigWorker)

{:ok, client_context} =
ClientContext.from_configuration_worker(
Conformance.ConfigWorker,
Conformance.RegisterClient.client_id(),
Conformance.RegisterClient.client_secret(),
Conformance.RegisterClient.client_context_opts()
)

conn =
with {:ok, {refreshed_token, refreshed_userinfo}} <-
maybe_refresh(token, provider_configuration),
{:ok, accounts_response} <- maybe_call_accounts(token, client_context) do
Logger.info("Refreshed Token: #{inspect(refreshed_token, pretty: true)}")
Logger.info("Refreshed Userinfo: #{inspect(refreshed_userinfo, pretty: true)}")
Logger.info("Accounts Response: #{inspect(accounts_response, pretty: true)}")

if is_binary(provider_configuration.end_session_endpoint) do
target_uri = url(~p"/logged-out")

{:ok, redirect_uri} =
Oidcc.initiate_logout_url(
token,
Conformance.ConfigWorker,
Conformance.RegisterClient.client_id(),
%{post_logout_redirect_uri: target_uri, state: "example_state"}
)
else
{:error, reason} -> error_response(conn, reason)
end

spawn(fn ->
Process.sleep(2_000)
redirect(conn, external: IO.iodata_to_binary(redirect_uri))
else
send_resp(conn, 200, "OK")
end
else
{:error, reason} -> error_response(conn, reason)
end

Conformance.Screenshot.take()
Process.send(Conformance.Runner, :stop, [])
end)

conn

%Oidcc.ProviderConfiguration{} ->
target_uri = url(~p"/logged-out")
spawn(fn ->
Process.sleep(2_000)

{:ok, redirect_uri} =
Oidcc.initiate_logout_url(
token,
Conformance.ConfigWorker,
Conformance.RegisterClient.client_id(),
Conformance.RegisterClient.client_secret(),
%{post_logout_redirect_uri: target_uri, state: "example_state"}
)
Conformance.Screenshot.take()
Process.send(Conformance.Runner, :stop, [])
end)

redirect(conn, external: IO.iodata_to_binary(redirect_uri))
end
conn
end

def callback(
Expand Down Expand Up @@ -131,30 +139,62 @@ defmodule Conformance.AuthController do
send_resp(conn, 200, inspect(%{params: params}, pretty: true))
end

defp maybe_refresh(%Token{refresh: %Token.Refresh{token: _refresh_token}} = token) do
with {:ok, token} <-
Oidcc.refresh_token(
token,
Conformance.ConfigWorker,
Conformance.RegisterClient.client_id(),
Conformance.RegisterClient.client_secret()
),
{:ok, userinfo} <-
Oidcc.retrieve_userinfo(
token,
Conformance.ConfigWorker,
Conformance.RegisterClient.client_id(),
Conformance.RegisterClient.client_secret(),
%{}
) do
Logger.info("Retrieved Token: #{inspect(token, pretty: true)}")
Logger.info("Retrieved Userinfo: #{inspect(userinfo, pretty: true)}")

{:ok, {token, userinfo}}
defp maybe_refresh(
%Token{refresh: %Token.Refresh{token: _refresh_token}} = token,
%ProviderConfiguration{grant_types_supported: grant_types_supported}
) do
if "refresh_token" in grant_types_supported do
with {:ok, token} <-
Oidcc.refresh_token(
token,
Conformance.ConfigWorker,
Conformance.RegisterClient.client_id(),
Conformance.RegisterClient.client_secret()
),
{:ok, userinfo} <-
Oidcc.retrieve_userinfo(
token,
Conformance.ConfigWorker,
Conformance.RegisterClient.client_id(),
Conformance.RegisterClient.client_secret(),
%{}
) do
Logger.info("Retrieved Token: #{inspect(token, pretty: true)}")
Logger.info("Retrieved Userinfo: #{inspect(userinfo, pretty: true)}")

{:ok, {token, userinfo}}
end
else
{:ok, {nil, nil}}
end
end

defp maybe_refresh(%Token{}), do: {:ok, {nil, nil}}
defp maybe_refresh(%Token{}, _config), do: {:ok, {nil, nil}}

defp maybe_call_accounts(token, client_context) do
accounts_uri = Application.get_env(:conformance, :call_accounts, false)

if accounts_uri do
headers =
:oidcc_auth_util.add_authorization_header(
token.access.token,
token.access.type,
:get,
accounts_uri,
%{},
ClientContext.struct_to_record(client_context)
)

:oidcc_http_util.request(
:get,
{accounts_uri, headers},
%{topic: [:conformance, :accounts]},
%{}
)
else
{:ok, nil}
end
end

defp error_response(conn, reason) do
Logger.error("OIDC Error: #{inspect(reason, pretty: true)}")
Expand Down
8 changes: 4 additions & 4 deletions lib/conformance/endpoint.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ defmodule Conformance.Endpoint do

plug(Conformance.Router)

@impl Phoenix.Endpoint
def init(_context, config) do
%URI{host: host, port: port, scheme: scheme} =
URI.new!(Ngrok.public_url(Conformance.Ngrok))
# %URI{host: host, port: port, scheme: scheme} =
# URI.new!(Ngrok.public_url(Conformance.Ngrok))

{:ok, Keyword.put(config, :url, path: "/", host: host, port: port, scheme: scheme)}
# {:ok, Keyword.put(config, :url, path: "/", host: host, port: port, scheme: scheme)}
{:ok, config}
end
end
10 changes: 9 additions & 1 deletion lib/conformance/log_configuration.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ defmodule Conformance.LogConfiguration do
ref =
:telemetry_test.attach_event_handlers(self(), [
[:oidcc, :load_configuration, :stop],
[:oidcc, :load_jwks, :stop]
[:oidcc, :load_jwks, :stop],
[:oidcc, :par_request, :start],
[:oidcc, :par_request, :stop],
[:oidcc, :par_request, :exception]
])

{:ok, ref}
Expand Down Expand Up @@ -40,4 +43,9 @@ defmodule Conformance.LogConfiguration do

{:noreply, ref}
end

def handle_info({[:oidcc | _topic], ref, _measurement, _meta} = event, ref) do
Logger.info("Event Received: #{inspect(event, pretty: true)}")
{:noreply, ref}
end
end
Loading