diff --git a/lib/boruta/adapters/ecto/schemas/client.ex b/lib/boruta/adapters/ecto/schemas/client.ex index f0b9038c..4d835018 100644 --- a/lib/boruta/adapters/ecto/schemas/client.ex +++ b/lib/boruta/adapters/ecto/schemas/client.ex @@ -46,7 +46,8 @@ defmodule Boruta.Ecto.Client do jwt_public_key: String.t(), public_key: String.t(), private_key: String.t(), - response_mode: String.t() + response_mode: String.t(), + signatures_adapter: String.t() } @token_endpoint_auth_methods [ @@ -94,6 +95,9 @@ defmodule Boruta.Ecto.Client do "HS256", "HS384", "HS512" + ], + "universal" => [ + "EdDSA" ] } @@ -124,6 +128,8 @@ defmodule Boruta.Ecto.Client do field(:id_token_signature_alg, :string, default: "RS512") field(:id_token_kid, :string) + field(:signatures_adapter, :string, default: "Elixir.Boruta.Internal.Signatures") + field(:key_pair_type, :map, default: %{ "type" => "rsa", @@ -190,7 +196,8 @@ defmodule Boruta.Ecto.Client do :logo_uri, :metadata, :response_mode, - :key_pair_type + :signatures_adapter, + :key_pair_type, ]) |> validate_required([:redirect_uris, :key_pair_type]) |> unique_constraint(:id, name: :clients_pkey) @@ -214,6 +221,7 @@ defmodule Boruta.Ecto.Client do ) |> put_assoc(:authorized_scopes, parse_authorized_scopes(attrs)) |> translate_jwk() + |> validate_signatures_adapter() |> validate_key_pair_type() |> generate_key_pair() |> put_secret() @@ -250,6 +258,7 @@ defmodule Boruta.Ecto.Client do :logo_uri, :metadata, :response_mode, + :signatures_adapter, :key_pair_type ]) |> validate_required([ @@ -278,6 +287,7 @@ defmodule Boruta.Ecto.Client do |> validate_supported_grant_types() |> validate_id_token_signature_alg() |> put_assoc(:authorized_scopes, parse_authorized_scopes(attrs)) + |> validate_signatures_adapter() |> validate_key_pair_type() |> translate_jwk() end @@ -354,6 +364,19 @@ defmodule Boruta.Ecto.Client do end end + defp validate_signatures_adapter(changeset) do + key_pair_type = get_field(changeset, :key_pair_type) + + case key_pair_type do + %{"type" => "universal"} -> + validate_inclusion(changeset, :signatures_adapter, [Atom.to_string(Boruta.Universal.Signatures)]) + %{"type" => type} when type in ["ec", "rsa"] -> + validate_inclusion(changeset, :signatures_adapter, [Atom.to_string(Boruta.Internal.Signatures)]) + _ -> + add_error(changeset, :signatures_adapter, "unknown key pair type") + end + end + defp validate_key_pair_type(changeset) do key_pair_type = get_field(changeset, :key_pair_type) @@ -471,14 +494,18 @@ defmodule Boruta.Ecto.Client do "universal" -> with {:ok, did, jwk} <- Did.create("key"), - {:ok, key} <- Universal.Signatures.SigningKey.get_key_by_did(did) do + {:ok, key_id} <- Universal.Signatures.SigningKey.get_key_by_did(did) do + "did:key:" <> key = did public_key = JOSE.JWK.from_map(jwk) {_type, public_pem} = JOSE.JWK.to_pem(public_key) changeset - |> put_change(:private_key, key["id"]) + |> put_change(:private_key, key_id["id"]) |> put_change(:public_key, public_pem) - |> put_change(:did, did) + |> put_change(:did, "#{did}##{key}") + |> put_change(:signatures_adapter, Boruta.Universal.Signatures |> Atom.to_string()) + |> put_change(:id_token_signature_alg, "EdDSA") + |> put_change(:userinfo_signed_response_alg, "EdDSA") else {:error, error} -> add_error(changeset, :private_key, error) @@ -493,6 +520,7 @@ defmodule Boruta.Ecto.Client do changeset |> put_change(:public_key, public_pem) |> put_change(:private_key, private_pem) + |> put_change(:signatures_adapter, Boruta.Internal.Signatures |> Atom.to_string()) end end @@ -518,8 +546,9 @@ defmodule Boruta.Ecto.Client do {_, jwk} = JOSE.JWK.from_pem(pem) |> JOSE.JWK.to_map() case Did.create("key", jwk) do - {:ok, did} -> - put_change(changeset, :did, did) + {:ok, did, _jwk} -> + "did:key:" <> key = did + put_change(changeset, :did, "#{did}##{key}") {:error, error} -> add_error(changeset, :did, error) diff --git a/lib/boruta/adapters/internal/signatures.ex b/lib/boruta/adapters/internal/signatures.ex index 77f9b14b..635724d0 100644 --- a/lib/boruta/adapters/internal/signatures.ex +++ b/lib/boruta/adapters/internal/signatures.ex @@ -26,9 +26,6 @@ defmodule Boruta.Internal.Signatures do HS512: [type: :symmetric, hash_algorithm: :SHA512, binary_size: 32] ] - @spec signature_algorithms() :: list(atom()) - def signature_algorithms, do: Keyword.keys(@signature_algorithms) - @spec hash_alg(Client.t()) :: hash_alg :: atom() def hash_alg(%Client{id_token_signature_alg: signature_alg}), do: @signature_algorithms[String.to_atom(signature_alg)][:hash_algorithm] @@ -83,23 +80,6 @@ defmodule Boruta.Internal.Signatures do end end - @spec verify_id_token_signature(id_token :: String.t(), jwk :: JOSE.JWK.t()) :: - :ok | {:error, reason :: String.t()} - def verify_id_token_signature(id_token, jwk) do - case Joken.peek_header(id_token) do - {:ok, %{"alg" => alg}} -> - signer = Joken.Signer.create(alg, %{"pem" => JOSE.JWK.from_map(jwk) |> JOSE.JWK.to_pem()}) - - case Token.verify(id_token, signer) do - {:ok, claims} -> {:ok, claims} - {:error, reason} -> {:error, inspect(reason)} - end - - {:error, reason} -> - {:error, inspect(reason)} - end - end - @spec userinfo_sign(payload :: map(), client :: Client.t()) :: jwt :: String.t() | {:error, reason :: String.t()} def userinfo_sign( @@ -172,11 +152,6 @@ defmodule Boruta.Internal.Signatures do end end - @spec kid_from_private_key(private_pem :: String.t()) :: kid :: String.t() - def kid_from_private_key(private_pem) do - :crypto.hash(:md5, private_pem) |> Base.url_encode64() |> String.slice(0..16) - end - @spec userinfo_signature_type(Client.t()) :: userinfo_token_signature_type :: atom() def userinfo_signature_type(%Client{userinfo_signed_response_alg: signature_alg}), do: @signature_algorithms[String.to_atom(signature_alg)][:type] @@ -191,7 +166,7 @@ defmodule Boruta.Internal.Signatures do type: :internal, private_key: client.private_key, secret: client.secret, - kid: client.id_token_kid || kid_from_private_key(client.private_key) + kid: client.did || client.id_token_kid || Client.Crypto.kid_from_private_key(client.private_key) }} end @@ -211,7 +186,7 @@ defmodule Boruta.Internal.Signatures do type: :internal, private_key: client.private_key, secret: client.secret, - kid: client.did || client.id_token_kid || kid_from_private_key(client.private_key) + kid: client.did || client.id_token_kid || Client.Crypto.kid_from_private_key(client.private_key) }} end end diff --git a/lib/boruta/adapters/signatures.ex b/lib/boruta/adapters/signatures.ex deleted file mode 100644 index 89f2d1ee..00000000 --- a/lib/boruta/adapters/signatures.ex +++ /dev/null @@ -1,42 +0,0 @@ -defmodule Boruta.SignaturesAdapter do - @moduledoc """ - Encapsulate injected `Boruta.Oauth.Signatures` and `Boruta.Openid.Signatures` adapter in context configuration - """ - - @behaviour Boruta.Oauth.Signatures - @behaviour Boruta.Openid.Signatures - - import Boruta.Config, only: [signatures: 0] - - @impl Boruta.Oauth.Signatures - def signature_algorithms, do: signatures().signature_algorithms() - - @impl Boruta.Oauth.Signatures - def hash_alg(client), do: signatures().hash_alg(client) - - @impl Boruta.Oauth.Signatures - def hash_binary_size(client), do: signatures().hash_binary_size(client) - - @impl Boruta.Oauth.Signatures - def hash(string, client), do: signatures().hash(string, client) - - @impl Boruta.Oauth.Signatures - def id_token_sign(payload, client), do: signatures().id_token_sign(payload, client) - - @impl Boruta.Oauth.Signatures - def verify_id_token_signature(id_token, jwk), - do: signatures().verify_id_token_signature(id_token, jwk) - - @impl Boruta.Oauth.Signatures - def userinfo_sign(payload, client), do: signatures().userinfo_sign(payload, client) - - @impl Boruta.Openid.Signatures - def verifiable_credential_sign(payload, client, format), - do: signatures().verifiable_credential_sign(payload, client, format) - - @impl Boruta.Oauth.Signatures - def kid_from_private_key(private_pem), do: signatures().kid_from_private_key(private_pem) - - @impl Boruta.Oauth.Signatures - def userinfo_signature_type(client), do: signatures().userinfo_signature_type(client) -end diff --git a/lib/boruta/adapters/universal/signatures.ex b/lib/boruta/adapters/universal/signatures.ex index f2a6ef26..579c89c9 100644 --- a/lib/boruta/adapters/universal/signatures.ex +++ b/lib/boruta/adapters/universal/signatures.ex @@ -11,24 +11,11 @@ defmodule Boruta.Universal.Signatures do @moduledoc false - import Boruta.Config, only: [ - universal_did_auth: 0, - signature_credentials_base_url: 0 - ] - - alias Boruta.Internal.Signatures.SigningKey alias Boruta.Oauth.Client + alias Boruta.Universal.Signatures.SigningKey @signature_algorithms [ - ES256: [type: :asymmetric, hash_algorithm: :SHA256, binary_size: 16], - ES384: [type: :asymmetric, hash_algorithm: :SHA384, binary_size: 24], - ES512: [type: :asymmetric, hash_algorithm: :SHA512, binary_size: 32], - RS256: [type: :asymmetric, hash_algorithm: :SHA256, binary_size: 16], - RS384: [type: :asymmetric, hash_algorithm: :SHA384, binary_size: 24], - RS512: [type: :asymmetric, hash_algorithm: :SHA512, binary_size: 32], - HS256: [type: :symmetric, hash_algorithm: :SHA256, binary_size: 16], - HS384: [type: :symmetric, hash_algorithm: :SHA384, binary_size: 24], - HS512: [type: :symmetric, hash_algorithm: :SHA512, binary_size: 32] + EdDSA: [type: :asymmetric, hash_algorithm: :SHA256, binary_size: 16], ] @spec signature_algorithms() :: list(atom()) @@ -57,51 +44,15 @@ defmodule Boruta.Universal.Signatures do jwt :: String.t() | {:error, reason :: String.t()} def id_token_sign( payload, - %Client{ - id_token_signature_alg: signature_alg - } = client + %Client{} = client ) do - with {:ok, signing_key} <- get_signing_key(client, :id_token) do - signer = - case id_token_signature_type(client) do - :symmetric -> - Joken.Signer.create(signature_alg, signing_key.secret) - - :asymmetric -> - Joken.Signer.create( - signature_alg, - %{"pem" => signing_key.private_key}, - %{ - "kid" => signing_key.kid, - "trust_chain" => signing_key.trust_chain - } - ) - end - - case Token.encode_and_sign(payload, signer) do - {:ok, token, _payload} -> - token - - {:error, error} -> - {:error, "Could not sign the given payload with client credentials: #{inspect(error)}"} - end - end - end - - @spec verify_id_token_signature(id_token :: String.t(), jwk :: JOSE.JWK.t()) :: - :ok | {:error, reason :: String.t()} - def verify_id_token_signature(id_token, jwk) do - case Joken.peek_header(id_token) do - {:ok, %{"alg" => alg}} -> - signer = Joken.Signer.create(alg, %{"pem" => JOSE.JWK.from_map(jwk) |> JOSE.JWK.to_pem()}) + with {:ok, key} <- get_signing_key(client, :id_token), + {:ok, token} <- SigningKey.encode_and_sign_with_key(key, payload) do + token - case Token.verify(id_token, signer) do - {:ok, claims} -> {:ok, claims} - {:error, reason} -> {:error, inspect(reason)} - end - - {:error, reason} -> - {:error, inspect(reason)} + else + {:error, error} -> + {:error, "Could not sign the given payload with client credentials: #{inspect(error)}"} end end @@ -140,43 +91,19 @@ defmodule Boruta.Universal.Signatures do end end - @universal_format %{ - "jwt_vc" => "jwt" - } @spec verifiable_credential_sign(payload :: map(), client :: Client.t(), format :: String.t()) :: jwt :: String.t() | {:error, reason :: String.t()} def verifiable_credential_sign( credential, - %Client{ - id_token_signature_alg: signature_alg - } = client, - format + %Client{} = client, + _format ) do - payload = %{ - "credential" => credential, - "options" => %{ - "format" => @universal_format[format] - } - } - case Finch.build(:post, signature_credentials_base_url(), [ - {"Authorization", "Bearer #{universal_did_auth()[:token]}"}, - {"Content-Type", "application/json"} - ], Jason.encode!(payload)) - |> Finch.request(OpenIDHttpClient) |> dbg do - {:ok, %Finch.Response{body: body, status: 201}} -> - body - {:ok, %Finch.Response{body: body}} -> - {:error, "Could not sign verifiable credential - #{body}"} - {:error, error} -> - {:error, "Could not sign verifiable credential - #{inspect(error)}"} + with {:ok, key} <- get_signing_key(client, :verifiable_credential), + {:ok, credential} <- SigningKey.encode_and_sign_with_key(key, credential) do + credential end end - @spec kid_from_private_key(private_pem :: String.t()) :: kid :: String.t() - def kid_from_private_key(private_pem) do - :crypto.hash(:md5, private_pem) |> Base.url_encode64() |> String.slice(0..16) - end - @spec userinfo_signature_type(Client.t()) :: userinfo_token_signature_type :: atom() def userinfo_signature_type(%Client{userinfo_signed_response_alg: signature_alg}), do: @signature_algorithms[String.to_atom(signature_alg)][:type] @@ -190,8 +117,9 @@ defmodule Boruta.Universal.Signatures do %SigningKey{ type: :internal, private_key: client.private_key, + public_key: client.public_key, secret: client.secret, - kid: client.id_token_kid || kid_from_private_key(client.private_key) + kid: client.did }} end @@ -200,18 +128,19 @@ defmodule Boruta.Universal.Signatures do %SigningKey{ type: :internal, private_key: client.private_key, + public_key: client.public_key, secret: client.secret, - kid: client.id + kid: client.did }} end defp get_signing_key(client, :verifiable_credential) do {:ok, %SigningKey{ - type: :internal, + type: :universal, private_key: client.private_key, - secret: client.secret, - kid: client.did || client.id_token_kid || kid_from_private_key(client.private_key) + public_key: client.public_key, + kid: client.did }} end end diff --git a/lib/boruta/adapters/universal/signatures/signging_key.ex b/lib/boruta/adapters/universal/signatures/signing_key.ex similarity index 55% rename from lib/boruta/adapters/universal/signatures/signging_key.ex rename to lib/boruta/adapters/universal/signatures/signing_key.ex index 0162de11..faa538ce 100644 --- a/lib/boruta/adapters/universal/signatures/signging_key.ex +++ b/lib/boruta/adapters/universal/signatures/signing_key.ex @@ -7,7 +7,8 @@ defmodule Boruta.Universal.Signatures.SigningKey do import Boruta.Config, only: [ universal_did_auth: 0, - universal_keys_base_url: 0 + universal_keys_base_url: 0, + universal_sign_base_url: 0 ] @type t :: %__MODULE__{ @@ -19,6 +20,38 @@ defmodule Boruta.Universal.Signatures.SigningKey do trust_chain: list(String.t()) | nil } + def encode_and_sign_with_key(%__MODULE__{kid: kid, private_key: key_id}, payload) do + header = + %{ + "typ" => "JWT", + "alg" => "EdDSA", + "kid" => kid + } + |> Jason.encode!() + |> Base.url_encode64(padding: false) + + payload = + Jason.encode!(payload) + |> Base.url_encode64(padding: false) + + case Finch.build( + :post, + universal_sign_base_url() <> "?id=#{key_id}&algorithm=EdDSA", + [ + {"Authorization", "Bearer #{universal_did_auth()[:token]}"}, + {"Content-Type", "application/octet-stream"} + ], + "#{header}.#{payload}" + ) + |> Finch.request(OpenIDHttpClient) do + {:ok, %Finch.Response{status: 200, body: signature}} -> + {:ok, "#{header}.#{payload}.#{Base.url_encode64(signature, padding: false)}"} + + _ -> + {:error, "Could not sign with universal key."} + end + end + def get_key_by_did(did) do with {:ok, %Finch.Response{status: 200, body: body}} <- Finch.build( diff --git a/lib/boruta/config.ex b/lib/boruta/config.ex index e1a0ad04..6529a957 100644 --- a/lib/boruta/config.ex +++ b/lib/boruta/config.ex @@ -48,7 +48,6 @@ defmodule Boruta.Config do scopes: Boruta.Ecto.Scopes, requests: Boruta.Ecto.Requests, credentials: Boruta.Ecto.Credentials, - signatures: Boruta.Internal.Signatures ], max_ttl: [ authorization_request: 300, @@ -62,6 +61,7 @@ defmodule Boruta.Config do did_registrar_base_url: "https://api.godiddy.com/1.0.0/universal-registrar", signature_credentials_base_url: "https://api.godiddy.com/1.0.0/universal-issuer/credentials/issue", universal_keys_base_url: "https://api.godiddy.com/0.1.0/wallet-service/keys", + universal_sign_base_url: "https://api.godiddy.com/0.1.0/wallet-service/keys/sign", universal_did_auth: %{ type: "bearer", token: nil @@ -159,12 +159,6 @@ defmodule Boruta.Config do Keyword.fetch!(oauth_config(), :contexts)[:credentials] end - @spec signatures() :: module() - @doc false - def signatures do - Keyword.fetch!(oauth_config(), :contexts)[:signatures] - end - @spec resource_owners() :: module() @doc false def resource_owners do @@ -216,6 +210,12 @@ defmodule Boruta.Config do Keyword.fetch!(oauth_config(), :universal_keys_base_url) end + @spec universal_sign_base_url() :: String.t() + @doc false + def universal_sign_base_url do + Keyword.fetch!(oauth_config(), :universal_sign_base_url) + end + @spec universal_did_auth() :: map() @doc false def universal_did_auth do diff --git a/lib/boruta/did.ex b/lib/boruta/did.ex index 58ad3937..f2a31e9d 100644 --- a/lib/boruta/did.ex +++ b/lib/boruta/did.ex @@ -61,8 +61,12 @@ defmodule Boruta.Did do end end - @spec create(method :: String.t(), jwk :: map()) :: - {:ok, did :: String.t()} | {:error, reason :: String.t()} + @dialyzer {:no_return, create: 1} + @dialyzer {:no_return, create: 2} + @spec create(method :: String.t()) :: + {:ok, did :: String.t(), jwk :: map()} | {:error, reason :: String.t()} + @spec create(method :: String.t(), jwk :: map() | nil) :: + {:ok, did :: String.t(), jwk :: map()} | {:error, reason :: String.t()} def create("key" = method, jwk \\ nil) do payload = %{ "didDocument" => %{ @@ -123,4 +127,8 @@ defmodule Boruta.Did do {:error, "Could not create did."} end end + + @spec controller(did :: String.t() | nil) :: controller :: String.t() | nil + def controller(nil), do: nil + def controller(did), do: String.split(did, "#") |> List.first() end diff --git a/lib/boruta/oauth/contexts/signatures.ex b/lib/boruta/oauth/contexts/signatures.ex index f39e2ea7..e3a65d12 100644 --- a/lib/boruta/oauth/contexts/signatures.ex +++ b/lib/boruta/oauth/contexts/signatures.ex @@ -3,17 +3,13 @@ defmodule Boruta.Oauth.Signatures do TODO Utilities to provide signature abilities to OAuth clients """ - @callback signature_algorithms() :: list(atom()) @callback hash_alg(Boruta.Oauth.Client.t()) :: hash_alg :: atom() @callback hash_binary_size(Boruta.Oauth.Client.t()) :: binary_size :: integer() @callback hash(string :: String.t(), client :: Boruta.Oauth.Client.t()) :: hash :: String.t() @callback id_token_sign(payload :: map(), client :: Boruta.Oauth.Client.t()) :: jwt :: String.t() | {:error, reason :: String.t()} - @callback verify_id_token_signature(id_token :: String.t(), jwk :: JOSE.JWK.t()) :: - :ok | {:error, reason :: String.t()} @callback userinfo_sign(payload :: map(), client :: Boruta.Oauth.Client.t()) :: jwt :: String.t() | {:error, reason :: String.t()} - @callback kid_from_private_key(private_pem :: String.t()) :: kid :: String.t() @callback userinfo_signature_type(Boruta.Oauth.Client.t()) :: userinfo_token_signature_type :: atom() end diff --git a/lib/boruta/oauth/schemas/client.ex b/lib/boruta/oauth/schemas/client.ex index cac13476..c6c52d2e 100644 --- a/lib/boruta/oauth/schemas/client.ex +++ b/lib/boruta/oauth/schemas/client.ex @@ -42,7 +42,8 @@ defmodule Boruta.Oauth.Client do did: nil, logo_uri: nil, response_mode: nil, - metadata: %{} + metadata: %{}, + signatures_adapter: nil @type t :: %__MODULE__{ id: any(), @@ -75,7 +76,8 @@ defmodule Boruta.Oauth.Client do did: String.t() | nil, logo_uri: String.t() | nil, response_mode: String.t(), - metadata: map() + metadata: map(), + signatures_adapter: String.t() } @wallet_grant_types [ @@ -196,40 +198,88 @@ defmodule Boruta.Oauth.Client do def public?(%__MODULE__{public_client_id: _public_client_id}), do: false + @spec signatures_adapter(client :: t()) :: signatures_adapter :: atom() + def signatures_adapter(%__MODULE__{signatures_adapter: signatures_adapter}) do + String.to_atom(signatures_adapter) + end + defmodule Crypto do @moduledoc false alias Boruta.Oauth.Client - alias Boruta.SignaturesAdapter + + @signature_algorithms [ + :ES256, + :ES384, + :ES512, + :RS256, + :RS384, + :RS512, + :HS256, + :HS384, + :HS512, + :EdDSA + ] @spec signature_algorithms() :: list(atom()) - def signature_algorithms, do: SignaturesAdapter.signature_algorithms() + def signature_algorithms, do: @signature_algorithms @spec hash_alg(Client.t()) :: hash_alg :: atom() - def hash_alg(client), do: SignaturesAdapter.hash_alg(client) + def hash_alg(client), do: Client.signatures_adapter(client).hash_alg(client) @spec hash_binary_size(Client.t()) :: binary_size :: integer() - def hash_binary_size(client), do: SignaturesAdapter.hash_binary_size(client) + def hash_binary_size(client), do: Client.signatures_adapter(client).hash_binary_size(client) @spec hash(string :: String.t(), client :: Client.t()) :: hash :: String.t() - def hash(string, client), do: SignaturesAdapter.hash(string, client) + def hash(string, client), do: Client.signatures_adapter(client).hash(string, client) @spec id_token_sign(payload :: map(), client :: Client.t()) :: jwt :: String.t() | {:error, reason :: String.t()} - def id_token_sign(payload, client), do: SignaturesAdapter.id_token_sign(payload, client) + def id_token_sign(payload, client), + do: Client.signatures_adapter(client).id_token_sign(payload, client) @spec verify_id_token_signature(id_token :: String.t(), jwk :: JOSE.JWK.t()) :: :ok | {:error, reason :: String.t()} - def verify_id_token_signature(id_token, jwk), do: SignaturesAdapter.verify_id_token_signature(id_token, jwk) + def verify_id_token_signature(id_token, jwk) do + case Joken.peek_header(id_token) do + {:ok, %{"alg" => "EdDSA"}} -> + signer = + Joken.Signer.create( + "EdDSA", + %{"pem" => JOSE.JWK.from_map(jwk) |> JOSE.JWK.to_pem()}, + %{"alg" => "EdDSA"} + ) + + case Token.verify(id_token, signer) do + {:ok, claims} -> {:ok, claims} + {:error, reason} -> {:error, inspect(reason)} + end + + {:ok, %{"alg" => alg}} -> + signer = + Joken.Signer.create(alg, %{"pem" => JOSE.JWK.from_map(jwk) |> JOSE.JWK.to_pem()}) + + case Token.verify(id_token, signer) do + {:ok, claims} -> {:ok, claims} + {:error, reason} -> {:error, inspect(reason)} + end + + {:error, reason} -> + {:error, inspect(reason)} + end + end @spec userinfo_sign(payload :: map(), client :: Client.t()) :: jwt :: String.t() | {:error, reason :: String.t()} - def userinfo_sign(payload, client), do: SignaturesAdapter.userinfo_sign(payload, client) + def userinfo_sign(payload, client), + do: Client.signatures_adapter(client).userinfo_sign(payload, client) @spec kid_from_private_key(private_pem :: String.t()) :: kid :: String.t() - def kid_from_private_key(private_pem), do: SignaturesAdapter.kid_from_private_key(private_pem) + def kid_from_private_key(private_pem) do + :crypto.hash(:md5, private_pem) |> Base.url_encode64() |> String.slice(0..16) + end @spec userinfo_signature_type(Client.t()) :: userinfo_token_signature_type :: atom() - def userinfo_signature_type(client), do: SignaturesAdapter.userinfo_signature_type(client) + def userinfo_signature_type(client), do: Client.signatures_adapter(client).userinfo_signature_type(client) end end diff --git a/lib/boruta/oauth/schemas/id_token.ex b/lib/boruta/oauth/schemas/id_token.ex index 2fbcebf0..2760ca01 100644 --- a/lib/boruta/oauth/schemas/id_token.ex +++ b/lib/boruta/oauth/schemas/id_token.ex @@ -5,6 +5,7 @@ defmodule Boruta.Oauth.IdToken do import Boruta.Config, only: [resource_owners: 0, issuer: 0] + alias Boruta.Did alias Boruta.Oauth alias Boruta.Oauth.Client @@ -86,7 +87,7 @@ defmodule Boruta.Oauth.IdToken do resource_owners().claims(resource_owner, scope) |> Map.merge(format_claims(resource_owner.extra_claims)) |> Map.put("sub", sub) - |> Map.put("iss", issuer()) + |> Map.put("iss", Did.controller(client.did) || issuer()) |> Map.put("aud", client.id) |> Map.put("iat", iat) |> Map.put("auth_time", auth_time) diff --git a/lib/boruta/openid/verifiable_credentials.ex b/lib/boruta/openid/verifiable_credentials.ex index 49f81823..7e18f717 100644 --- a/lib/boruta/openid/verifiable_credentials.ex +++ b/lib/boruta/openid/verifiable_credentials.ex @@ -47,7 +47,6 @@ defmodule Boruta.Openid.VerifiableCredentials do alias Boruta.Oauth.ResourceOwner alias Boruta.Oauth.Scope alias Boruta.Openid.Credential - alias Boruta.SignaturesAdapter alias ExJsonSchema.Schema alias ExJsonSchema.Validator.Error.BorutaFormatter @@ -338,7 +337,7 @@ defmodule Boruta.Openid.VerifiableCredentials do "sub" => sub, # TODO store credential "jti" => Config.issuer() <> "/credentials/#{credential_id}", - "iss" => client.did, + "iss" => Did.controller(client.did) || Config.issuer(), "nbf" => now, "iat" => now, "exp" => now + credential_configuration[:time_to_live], @@ -370,7 +369,7 @@ defmodule Boruta.Openid.VerifiableCredentials do } } - case SignaturesAdapter.verifiable_credential_sign(payload, client, format) do + case Client.signatures_adapter(client).verifiable_credential_sign(payload, client, format) do {:error, error} -> {:error, error} @@ -410,7 +409,7 @@ defmodule Boruta.Openid.VerifiableCredentials do "issued" => DateTime.from_unix!(now) |> DateTime.to_iso8601(), "issuanceDate" => DateTime.from_unix!(now) |> DateTime.to_iso8601(), "type" => credential_configuration[:types], - "issuer" => client.did || Config.issuer(), + "issuer" => Did.controller(client.did), "validFrom" => DateTime.utc_now() |> DateTime.to_iso8601(), "credentialSubject" => %{ "id" => sub, @@ -425,7 +424,7 @@ defmodule Boruta.Openid.VerifiableCredentials do } } - case SignaturesAdapter.verifiable_credential_sign(payload, client, format) do + case Client.signatures_adapter(client).verifiable_credential_sign(payload, client, format) do {:error, error} -> {:error, error} @@ -474,7 +473,7 @@ defmodule Boruta.Openid.VerifiableCredentials do payload = %{ "sub" => sub, - "iss" => client.did || Config.issuer(), + "iss" => Did.controller(client.did) || Config.issuer(), "iat" => :os.system_time(:seconds), "exp" => :os.system_time(:seconds) + credential_configuration[:time_to_live], "nonce" => token.c_nonce, @@ -484,7 +483,7 @@ defmodule Boruta.Openid.VerifiableCredentials do } } - case SignaturesAdapter.verifiable_credential_sign(payload, client, format) do + case Client.signatures_adapter(client).verifiable_credential_sign(payload, client, format) do {:error, error} -> {:error, error} diff --git a/priv/boruta/migrations/20241220105748_clients_signatures_adapters.ex b/priv/boruta/migrations/20241220105748_clients_signatures_adapters.ex new file mode 100644 index 00000000..dfe996c2 --- /dev/null +++ b/priv/boruta/migrations/20241220105748_clients_signatures_adapters.ex @@ -0,0 +1,15 @@ +defmodule Boruta.Migrations.ClientsSignaturesAdapters do + @moduledoc false + + defmacro __using__(_args) do + quote do + def change do + # 20241220082127_add_signatures_adapter_to_oauth_clients.exs + alter table(:oauth_clients) do + add :signatures_adapter, :string, null: false, default: "Elixir.Boruta.Internal.Signatures" + end + end + end + end +end + diff --git a/priv/repo/migrations/20241220082127_add_signatures_adapter_to_oauth_clients.exs b/priv/repo/migrations/20241220082127_add_signatures_adapter_to_oauth_clients.exs new file mode 100644 index 00000000..e9512925 --- /dev/null +++ b/priv/repo/migrations/20241220082127_add_signatures_adapter_to_oauth_clients.exs @@ -0,0 +1,9 @@ +defmodule Boruta.Repo.Migrations.AddSignaturesAdapterToOauthClients do + use Ecto.Migration + + def change do + alter table(:oauth_clients) do + add :signatures_adapter, :string, null: false, default: "Elixir.Boruta.Internal.Signatures" + end + end +end diff --git a/test/boruta/admin_test.exs b/test/boruta/admin_test.exs index b79ba737..d2f7eeec 100644 --- a/test/boruta/admin_test.exs +++ b/test/boruta/admin_test.exs @@ -94,7 +94,10 @@ defmodule Boruta.Ecto.AdminTest do # TODO create an universal mock adapter @tag :skip test "creates a client with universal key" do - assert {:ok, %Client{}} = Admin.create_client(Map.put(@client_valid_attrs, :key_pair_type, %{"type" => "universal"})) + assert {:ok, %Client{}} = + Admin.create_client( + Map.put(@client_valid_attrs, :key_pair_type, %{"type" => "universal"}) + ) end test "creates a client with supported_grant_types" do @@ -234,44 +237,68 @@ defmodule Boruta.Ecto.AdminTest do test "creates a client with key pair type" do assert {:ok, %Client{public_key: pem_public_key, private_key: pem_private_key}} = - Admin.create_client(Map.put(@client_valid_attrs, :key_pair_type, %{ - "type" => "ec", - "curve" => "P-256" - })) + Admin.create_client( + Map.put(@client_valid_attrs, :key_pair_type, %{ + "type" => "ec", + "curve" => "P-256" + }) + ) + assert %{"kty" => "EC"} = JOSE.JWK.from_pem(pem_private_key) |> JOSE.JWK.to_map() |> elem(1) assert %{"kty" => "EC"} = JOSE.JWK.from_pem(pem_public_key) |> JOSE.JWK.to_map() |> elem(1) end test "returns an error with key pair not matching JWT alg" do assert {:error, %Ecto.Changeset{errors: errors}} = - Admin.create_client(Map.put(@client_valid_attrs, :key_pair_type, %{ - "type" => "oct" - })) + Admin.create_client( + Map.merge(@client_valid_attrs, %{ + key_pair_type: %{"type" => "rsa"}, + signatures_adapter: "Elixir.Universal.Signatures" + }) + ) + assert errors == [ - {:private_key, {"private_key_type is invalid", []}}, - {:key_pair_type, {"validation failed: #/type do match required pattern /^ec|rsa/.", []}} - ] + {:private_key, {"private_key_type is invalid", []}}, + {:signatures_adapter, + { + "is invalid", + [{:validation, :inclusion}, {:enum, ["Elixir.Boruta.Internal.Signatures"]}] + }} + ] end - test "returns an error with an invalid key pair" do + test "returns an error with an invalid key pair type" do assert {:error, %Ecto.Changeset{errors: errors}} = - Admin.create_client(Map.put(@client_valid_attrs, :key_pair_type, %{ - "type" => "oct" - })) + Admin.create_client( + Map.put(@client_valid_attrs, :key_pair_type, %{ + "type" => "oct" + }) + ) + assert errors == [ - {:private_key, {"private_key_type is invalid", []}}, - {:key_pair_type, {"validation failed: #/type do match required pattern /^ec|rsa/.", []}} - ] + {:private_key, {"private_key_type is invalid", []}}, + { + :key_pair_type, + { + "validation failed: #/type do match required pattern /^ec|rsa|universal$/.", + [] + } + }, + {:signatures_adapter, {"unknown key pair type", []}} + ] end test "returns an error with an invalid key pair configuration" do assert {:error, %Ecto.Changeset{errors: errors}} = - Admin.create_client(Map.put(@client_valid_attrs, :key_pair_type, %{ - "type" => "ec" - })) + Admin.create_client( + Map.put(@client_valid_attrs, :key_pair_type, %{ + "type" => "ec" + }) + ) + assert errors == [ - {:private_key, {"private_key_type is invalid", []}} - ] + {:private_key, {"private_key_type is invalid", []}} + ] end test "creates a client with default id token signature alg" do @@ -287,7 +314,7 @@ defmodule Boruta.Ecto.AdminTest do end test "creates a client with id token signature alg" do - Enum.map([:RS256, :RS384, :RS512, :HS256, :HS384, :HS512] , fn alg -> + Enum.map([:RS256, :RS384, :RS512, :HS256, :HS384, :HS512], fn alg -> assert {:ok, client} = Admin.create_client( Map.put(@client_valid_attrs, :id_token_signature_alg, Atom.to_string(alg)) @@ -295,7 +322,8 @@ defmodule Boruta.Ecto.AdminTest do assert client.id_token_signature_alg == Atom.to_string(alg) end) - Enum.map([:ES256, :ES384, :ES512, :HS256, :HS384, :HS512] , fn alg -> + + Enum.map([:ES256, :ES384, :ES512, :HS256, :HS384, :HS512], fn alg -> assert {:ok, client} = Admin.create_client( Map.put(@client_valid_attrs, :id_token_signature_alg, Atom.to_string(alg)) @@ -348,12 +376,15 @@ defmodule Boruta.Ecto.AdminTest do test "updates the client with an id token signature alg" do client = client_fixture() - ec_client = client_fixture(%{key_pair_type: %{ - "type" => "ec", - "curve" => "P-256" - }}) + ec_client = + client_fixture(%{ + key_pair_type: %{ + "type" => "ec", + "curve" => "P-256" + } + }) - Enum.map([:RS256, :RS384, :RS512, :HS256, :HS384, :HS512] , fn alg -> + Enum.map([:RS256, :RS384, :RS512, :HS256, :HS384, :HS512], fn alg -> assert {:ok, client} = Admin.update_client( client, @@ -362,7 +393,8 @@ defmodule Boruta.Ecto.AdminTest do assert client.id_token_signature_alg == Atom.to_string(alg) end) - Enum.map([:ES256, :ES384, :ES512, :HS256, :HS384, :HS512] , fn alg -> + + Enum.map([:ES256, :ES384, :ES512, :HS256, :HS384, :HS512], fn alg -> assert {:ok, client} = Admin.update_client( ec_client, @@ -392,10 +424,14 @@ defmodule Boruta.Ecto.AdminTest do client = client_fixture() assert {:ok, %Client{key_pair_type: key_pair_type}} = - Admin.update_client(client, Map.put(@client_update_attrs, :key_pair_type, %{ - "type" => "ec", - "curve" => "P-256" - })) + Admin.update_client( + client, + Map.put(@client_update_attrs, :key_pair_type, %{ + "type" => "ec", + "curve" => "P-256" + }) + ) + assert key_pair_type == %{"curve" => "P-256", "type" => "ec"} end end diff --git a/test/boruta/oauth/integration/hybrid_test.exs b/test/boruta/oauth/integration/hybrid_test.exs index 0d3d31b8..bdfec8b2 100644 --- a/test/boruta/oauth/integration/hybrid_test.exs +++ b/test/boruta/oauth/integration/hybrid_test.exs @@ -10,6 +10,7 @@ defmodule Boruta.OauthTest.HybridGrantTest do alias Boruta.Oauth alias Boruta.Oauth.ApplicationMock alias Boruta.Oauth.AuthorizeResponse + alias Boruta.Oauth.Client alias Boruta.Oauth.Error alias Boruta.Oauth.ResourceOwner alias Boruta.Oauth.Scope @@ -78,7 +79,8 @@ defmodule Boruta.OauthTest.HybridGrantTest do {:authorize_error, %Error{ error: :invalid_request, - error_description: "Query params validation failed. #/response_mode do match required pattern /^(query|fragment)$/.", + error_description: + "Query params validation failed. #/response_mode do match required pattern /^(query|fragment)$/.", status: :bad_request }} end @@ -172,7 +174,10 @@ defmodule Boruta.OauthTest.HybridGrantTest do }} end - test "returns a code and a token without a nonce", %{client: client, resource_owner: resource_owner} do + test "returns a code and a token without a nonce", %{ + client: client, + resource_owner: resource_owner + } do ResourceOwners |> expect(:authorized_scopes, fn _resource_owner -> [] end) @@ -205,7 +210,10 @@ defmodule Boruta.OauthTest.HybridGrantTest do assert expires_in end - test "creates a code and an id_token with a nonce", %{client: client, resource_owner: resource_owner} do + test "creates a code and an id_token with a nonce", %{ + client: client, + resource_owner: resource_owner + } do redirect_uri = List.first(client.redirect_uris) nonce = "nonce" @@ -253,7 +261,10 @@ defmodule Boruta.OauthTest.HybridGrantTest do }} end - test "returns an error as fragment without a nonce and `code id_token` response types", %{client: client, resource_owner: resource_owner} do + test "returns an error as fragment without a nonce and `code id_token` response types", %{ + client: client, + resource_owner: resource_owner + } do ResourceOwners |> expect(:authorized_scopes, fn _resource_owner -> [] end) @@ -270,7 +281,7 @@ defmodule Boruta.OauthTest.HybridGrantTest do }, resource_owner, ApplicationMock - ) == + ) == {:authorize_error, %Error{ format: :fragment, @@ -281,7 +292,8 @@ defmodule Boruta.OauthTest.HybridGrantTest do }} end - test "returns an error as query params with `response_mode=query`, without a nonce, and `code id_token` response types", %{client: client, resource_owner: resource_owner} do + test "returns an error as query params with `response_mode=query`, without a nonce, and `code id_token` response types", + %{client: client, resource_owner: resource_owner} do ResourceOwners |> expect(:authorized_scopes, fn _resource_owner -> [] end) @@ -299,7 +311,7 @@ defmodule Boruta.OauthTest.HybridGrantTest do }, resource_owner, ApplicationMock - ) == + ) == {:authorize_error, %Error{ format: :query, @@ -319,26 +331,27 @@ defmodule Boruta.OauthTest.HybridGrantTest do redirect_uri = List.first(client.redirect_uris) assert {:authorize_error, - %Boruta.Oauth.Error{ - error: :unknown_error, - error_description: "An error occurred during token creation: \"Could not create code : sub is invalid\".", - format: :fragment, - redirect_uri: "https://redirect.uri", - state: nil, - status: :internal_server_error - }} = - Oauth.authorize( - %Plug.Conn{ - query_params: %{ - "response_type" => "code token", - "client_id" => client.id, - "redirect_uri" => redirect_uri, - "scope" => "openid" - } - }, - resource_owner, - ApplicationMock - ) + %Boruta.Oauth.Error{ + error: :unknown_error, + error_description: + "An error occurred during token creation: \"Could not create code : sub is invalid\".", + format: :fragment, + redirect_uri: "https://redirect.uri", + state: nil, + status: :internal_server_error + }} = + Oauth.authorize( + %Plug.Conn{ + query_params: %{ + "response_type" => "code token", + "client_id" => client.id, + "redirect_uri" => redirect_uri, + "scope" => "openid" + } + }, + resource_owner, + ApplicationMock + ) end test "does not return an id_token without `openid` scope", %{ @@ -375,7 +388,7 @@ defmodule Boruta.OauthTest.HybridGrantTest do test "returns a code and an id_token", %{client: client, resource_owner: resource_owner} do ResourceOwners |> expect(:authorized_scopes, fn _resource_owner -> [] end) - |> expect(:claims, fn (_sub, _scope) -> %{"email" => resource_owner.username} end) + |> expect(:claims, fn _sub, _scope -> %{"email" => resource_owner.username} end) redirect_uri = List.first(client.redirect_uris) nonce = "nonce" @@ -398,7 +411,7 @@ defmodule Boruta.OauthTest.HybridGrantTest do "nonce" => nonce } }, - %{resource_owner|extra_claims: %{"resource_owner_extra_claim" => "claim"}}, + %{resource_owner | extra_claims: %{"resource_owner_extra_claim" => "claim"}}, ApplicationMock ) @@ -407,9 +420,12 @@ defmodule Boruta.OauthTest.HybridGrantTest do assert id_token assert expires_in - signer = Joken.Signer.create("RS512", %{"pem" => client.private_key, "aud" => client.id}) + assert {:ok, claims} = + Client.Crypto.verify_id_token_signature( + id_token, + JOSE.JWK.from_pem(client.public_key) |> JOSE.JWK.to_map() + ) - {:ok, claims} = Oauth.Client.Token.verify_and_validate(id_token, signer) client_id = client.id resource_owner_id = resource_owner.sub @@ -424,10 +440,13 @@ defmodule Boruta.OauthTest.HybridGrantTest do } = claims end - test "returns a code and an id_token with `response_mode=query`", %{client: client, resource_owner: resource_owner} do + test "returns a code and an id_token with `response_mode=query`", %{ + client: client, + resource_owner: resource_owner + } do ResourceOwners |> expect(:authorized_scopes, fn _resource_owner -> [] end) - |> expect(:claims, fn (_sub, _scope) -> %{"email" => resource_owner.username} end) + |> expect(:claims, fn _sub, _scope -> %{"email" => resource_owner.username} end) redirect_uri = List.first(client.redirect_uris) nonce = "nonce" @@ -461,9 +480,12 @@ defmodule Boruta.OauthTest.HybridGrantTest do assert id_token assert expires_in - signer = Joken.Signer.create("RS512", %{"pem" => client.private_key, "aud" => client.id}) + assert {:ok, claims} = + Client.Crypto.verify_id_token_signature( + id_token, + JOSE.JWK.from_pem(client.public_key) |> JOSE.JWK.to_map() + ) - {:ok, claims} = Oauth.Client.Token.verify_and_validate(id_token, signer) client_id = client.id resource_owner_id = resource_owner.sub @@ -512,7 +534,10 @@ defmodule Boruta.OauthTest.HybridGrantTest do assert expires_in end - test "returns a code and a token with `response_mode=query`", %{client: client, resource_owner: resource_owner} do + test "returns a code and a token with `response_mode=query`", %{ + client: client, + resource_owner: resource_owner + } do ResourceOwners |> expect(:authorized_scopes, fn _resource_owner -> [] end) @@ -550,12 +575,12 @@ defmodule Boruta.OauthTest.HybridGrantTest do end test "returns a code, a token and an id_token", %{ - client: client, - resource_owner: resource_owner + resource_owner: resource_owner, + client: client } do ResourceOwners |> expect(:authorized_scopes, fn _resource_owner -> [] end) - |> expect(:claims, fn (_sub, _scope) -> %{"email" => resource_owner.username} end) + |> expect(:claims, fn _sub, _scope -> %{"email" => resource_owner.username} end) redirect_uri = List.first(client.redirect_uris) nonce = "nonce" @@ -578,7 +603,7 @@ defmodule Boruta.OauthTest.HybridGrantTest do "nonce" => nonce } }, - %{resource_owner|extra_claims: %{"resource_owner_extra_claim" => "claim"}}, + %{resource_owner | extra_claims: %{"resource_owner_extra_claim" => "claim"}}, ApplicationMock ) @@ -588,9 +613,12 @@ defmodule Boruta.OauthTest.HybridGrantTest do assert access_token assert expires_in - signer = Joken.Signer.create("RS512", %{"pem" => client.private_key, "aud" => client.id}) + assert {:ok, claims} = + Client.Crypto.verify_id_token_signature( + id_token, + JOSE.JWK.from_pem(client.public_key) |> JOSE.JWK.to_map() + ) - {:ok, claims} = Oauth.Client.Token.verify_and_validate(id_token, signer) client_id = client.id resource_owner_id = resource_owner.sub @@ -611,7 +639,7 @@ defmodule Boruta.OauthTest.HybridGrantTest do } do ResourceOwners |> expect(:authorized_scopes, fn _resource_owner -> [] end) - |> expect(:claims, fn (_sub, _scope) -> %{"email" => resource_owner.username} end) + |> expect(:claims, fn _sub, _scope -> %{"email" => resource_owner.username} end) redirect_uri = List.first(client.redirect_uris) nonce = "nonce" @@ -646,9 +674,12 @@ defmodule Boruta.OauthTest.HybridGrantTest do assert access_token assert expires_in - signer = Joken.Signer.create("RS512", %{"pem" => client.private_key, "aud" => client.id}) + assert {:ok, claims} = + Client.Crypto.verify_id_token_signature( + id_token, + JOSE.JWK.from_pem(client.public_key) |> JOSE.JWK.to_map() + ) - {:ok, claims} = Oauth.Client.Token.verify_and_validate(id_token, signer) client_id = client.id resource_owner_id = resource_owner.sub @@ -668,7 +699,7 @@ defmodule Boruta.OauthTest.HybridGrantTest do } do ResourceOwners |> expect(:authorized_scopes, fn _resource_owner -> [] end) - |> expect(:claims, fn (_sub, _scope) -> %{"email" => resource_owner.username} end) + |> expect(:claims, fn _sub, _scope -> %{"email" => resource_owner.username} end) redirect_uri = "https://wildcard-redirect-uri.uri" nonce = "nonce" @@ -701,9 +732,12 @@ defmodule Boruta.OauthTest.HybridGrantTest do assert access_token assert expires_in - signer = Joken.Signer.create("RS512", %{"pem" => client.private_key, "aud" => client.id}) + assert {:ok, claims} = + Client.Crypto.verify_id_token_signature( + id_token, + JOSE.JWK.from_pem(client.public_key) |> JOSE.JWK.to_map() + ) - {:ok, claims} = Oauth.Client.Token.verify_and_validate(id_token, signer) client_id = client.id resource_owner_id = resource_owner.sub @@ -748,7 +782,10 @@ defmodule Boruta.OauthTest.HybridGrantTest do assert expires_in end - test "returns a code with public scope (from cache)", %{client: client, resource_owner: resource_owner} do + test "returns a code with public scope (from cache)", %{ + client: client, + resource_owner: resource_owner + } do ResourceOwners |> expect(:authorized_scopes, fn _resource_owner -> [] end) diff --git a/test/boruta/oauth/schemas/id_token_test.exs b/test/boruta/oauth/schemas/id_token_test.exs index 7ba441fc..aa9b806c 100644 --- a/test/boruta/oauth/schemas/id_token_test.exs +++ b/test/boruta/oauth/schemas/id_token_test.exs @@ -649,6 +649,7 @@ defmodule Boruta.Oauth.IdTokenTest do id_token_ttl: 10, id_token_signature_alg: "RS512", secret: "secret", + signatures_adapter: "Elixir.Boruta.Internal.Signatures", private_key: "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA1PaP/gbXix5itjRCaegvI/B3aFOeoxlwPPLvfLHGA4QfDmVO\nf8cU8OuZFAYzLArW3PnnwWWy39nVJOx42QRVGCGdUCmV7shDHRsr86+2DlL7pwUa\n9QyHsTj84fAJn2Fv9h9mqrIvUzAtEYRlGFvjVTGCwzEullpsB0GJafopUTFby8Wd\nSq3dGLJBB1r+Q8QtZnAxxvolhwOmYkBkkidefmm48X7hFXL2cSJm2G7wQyinOey/\nU8xDZ68mgTakiqS2RtjnFD0dnpBl5CYTe4s6oZKEyFiFNiW4KkR1GVjsKwY9oC2t\npyQ0AEUMvk9T9VdIltSIiAvOKlwFzL49cgwZDwIDAQABAoIBAG0dg/upL8k1IWiv\n8BNphrXIYLYQmiiBQTPJWZGvWIC2sl7i40yvCXjDjiRnZNK9HwgL94XtALCXYRFR\nJD41bRA3MO5A0HSPIWwJXwS10/cU56HVCNHjwKa6Rz/QiG2kNASMZEMzlvHtrjna\ndx36/sjI3HH8gh1BaTZyiuDE72SMkPbL838jfL1YY9uJ0u6hWFDbdn3sqPfJ6Cnz\n1cu0piT35nkilnIGCNYA0i3lyMeo4XrdXaAJdN9nnqbCi5ewQWqaHbrIIY5LTgzJ\nYlOr3IiecyokFxHCbULXle60u0KqXYgBHmlQJJr1Dj4c9AkQmefjC2jRMlhOrIzo\nIkIUeMECgYEA+MNLB+w6vv1ogqzM3M1OLt6bziWJCn+XkziuMrCiY9KeDD+S70+E\nhfbhM5RjCE3wxC/k59039laT973BmdMHxrDd2zSjOFmCIORv5yrD5oBHMaMZcwuQ\n45Xisi4aoQoOhyznSnjo/RjeQB7qEDzXFznLLNT79HzqyAtCWD3UIu8CgYEA2yik\n9FKl7HJEY94D2K6vNh1AHGnkwIQC72pXzlUrVuwQYngj6/Gkhw8ayFBApHfwVCXj\no9rDYPdNrrAs0Zz0JsiJp6bOCEKCrMYE16UiejUUAg/OZ5eg6+3m3/iWatkzLUuK\n1LIkVBJlEyY0uPuAaBF0V0VleNvfCGhVYOn46+ECgYAUD4OsduNh5YOZDiBTKgdF\nBlSgMiyz+QgbKjX6Bn6B+EkgibvqqonwV7FffHbkA40H9SjLfe52YhL6poXHRtpY\nroillcAX2jgBOQrBJJS5sNyM5y81NNiRUdP/NHKXS/1R71ATlF6NkoTRvOx5NL7P\ns6xryB0tYSl5ylamUQ4bZwKBgHF6FB9mA//wErVbKcayfIqajq2nrwh30kVBXQG7\nW9uAE+PIrWDoF/bOvWFnHHGMoOYRUFNxXKUCqDiBhFNs34aNY6lpV1kzhxIK3ksC\neF2qyhdfM9Kz0mEXJ+pkfw4INNWJPfNv4hueArPtnnMB1rUMBJ+DkU0JG+zwiPTL\ncVZBAoGBAM6kOsh5KGn3aI83g9ZO0TrKLXXFotxJt31Wu11ydj9K33/Qj3UXcxd4\nJPXr600F0DkLeUKBob6BALeHFWcrSz5FGLGRqdRxdv+L6g18WH5m2xEs7o6M6e5I\nIhyUC60ZewJ2M8rV4KgCJJdZE2kENlSgjU92IDVPT9Oetrc7hQJd\n-----END RSA PRIVATE KEY-----\n\n" } diff --git a/test/boruta/openid/responses/userinfo_response_test.exs b/test/boruta/openid/responses/userinfo_response_test.exs index 3754d967..7c61119a 100644 --- a/test/boruta/openid/responses/userinfo_response_test.exs +++ b/test/boruta/openid/responses/userinfo_response_test.exs @@ -17,7 +17,7 @@ defmodule Boruta.Openid.UserinfoResponseTest do test "returns userinfo with client userinfo_signed_response_alg" do userinfo = %{"sub" => "sub"} - client = %Client{id: SecureRandom.uuid(), secret: "secret", userinfo_signed_response_alg: "HS256"} + client = %Client{id: SecureRandom.uuid(), secret: "secret", userinfo_signed_response_alg: "HS256", signatures_adapter: "Elixir.Boruta.Internal.Signatures"} assert %UserinfoResponse{ userinfo: ^userinfo, diff --git a/test/boruta/openid/verifiable_credentials_test.exs b/test/boruta/openid/verifiable_credentials_test.exs index 13fa6cd0..100a6221 100644 --- a/test/boruta/openid/verifiable_credentials_test.exs +++ b/test/boruta/openid/verifiable_credentials_test.exs @@ -207,12 +207,12 @@ defmodule Boruta.Openid.VerifiableCredentialsTest do VerifiableCredentials.issue_verifiable_credential( resource_owner, credential_params, - insert(:token, client: build(:client, did: "did:indy:danube:BjVFw137Xf75GbWw3ApYof")) |> to_oauth_schema(), + insert(:token) |> to_oauth_schema(), %{} ) # TODO validate credential body - assert credential |> dbg + assert credential end test "issues jwt_vc_json credential", %{ @@ -253,7 +253,6 @@ defmodule Boruta.Openid.VerifiableCredentialsTest do test "issues vc+sd-jwt credential", %{ credential_params: credential_params } do - resource_owner = %ResourceOwner{ sub: SecureRandom.uuid(), extra_claims: %{