Skip to content
Open
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
36 changes: 22 additions & 14 deletions lib/boruta/adapters/internal/signatures.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
defmodule Boruta.Internal.Signatures do
@behaviour Boruta.Oauth.Signatures

import Boruta.Config, only: [resource_owners: 0]

defmodule Token do
@moduledoc false

Expand Down Expand Up @@ -161,13 +163,16 @@ defmodule Boruta.Internal.Signatures do
do: @signature_algorithms[String.to_atom(signature_alg)][:type]

defp get_signing_key(client, :id_token) do
{:ok,
%SigningKey{
type: :internal,
private_key: client.private_key,
secret: client.secret,
kid: client.did || client.id_token_kid || Client.Crypto.kid_from_private_key(client.private_key)
}}
with {:ok, trust_chain} <- resource_owners().trust_chain(client) do
{:ok,
%SigningKey{
type: :internal,
private_key: client.private_key,
secret: client.secret,
kid: client.did || client.id_token_kid || Client.Crypto.kid_from_private_key(client.private_key),
trust_chain: trust_chain
}}
end
end

defp get_signing_key(client, :userinfo) do
Expand All @@ -181,12 +186,15 @@ defmodule Boruta.Internal.Signatures do
end

defp get_signing_key(client, :verifiable_credential) do
{:ok,
%SigningKey{
type: :internal,
private_key: client.private_key,
secret: client.secret,
kid: client.did || client.id_token_kid || Client.Crypto.kid_from_private_key(client.private_key)
}}
with {:ok, trust_chain} <- resource_owners().trust_chain(client) do
{:ok,
%SigningKey{
type: :internal,
private_key: client.private_key,
secret: client.secret,
kid: client.did || client.id_token_kid || Client.Crypto.kid_from_private_key(client.private_key),
trust_chain: trust_chain
}}
end
end
end
38 changes: 23 additions & 15 deletions lib/boruta/adapters/universal/signatures.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
defmodule Boruta.Universal.Signatures do
@behaviour Boruta.Oauth.Signatures

import Boruta.Config, only: [resource_owners: 0]

defmodule Token do
@moduledoc false

Expand Down Expand Up @@ -113,14 +115,17 @@ defmodule Boruta.Universal.Signatures do
do: @signature_algorithms[String.to_atom(signature_alg)][:type]

defp get_signing_key(client, :id_token) do
{:ok,
%SigningKey{
type: :internal,
private_key: client.private_key,
public_key: client.public_key,
secret: client.secret,
kid: client.did
}}
with {:ok, trust_chain} <- resource_owners().trust_chain(client) do
{:ok,
%SigningKey{
type: :internal,
private_key: client.private_key,
public_key: client.public_key,
secret: client.secret,
kid: client.did,
trust_chain: trust_chain
}}
end
end

defp get_signing_key(client, :userinfo) do
Expand All @@ -135,12 +140,15 @@ defmodule Boruta.Universal.Signatures do
end

defp get_signing_key(client, :verifiable_credential) do
{:ok,
%SigningKey{
type: :universal,
private_key: client.private_key,
public_key: client.public_key,
kid: client.did
}}
with {:ok, trust_chain} <- resource_owners().trust_chain(client) do
{:ok,
%SigningKey{
type: :universal,
private_key: client.private_key,
public_key: client.public_key,
kid: client.did,
trust_chain: trust_chain
}}
end
end
end
5 changes: 3 additions & 2 deletions lib/boruta/adapters/universal/signatures/signing_key.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ 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
def encode_and_sign_with_key(%__MODULE__{kid: kid, private_key: key_id, trust_chain: trust_chain}, payload) do
header =
%{
"typ" => "JWT",
"alg" => "EdDSA",
"kid" => kid
"kid" => kid,
"trust_chain" => trust_chain
}
|> Jason.encode!()
|> Base.url_encode64(padding: false)
Expand Down
59 changes: 46 additions & 13 deletions lib/boruta/oauth/authorization.ex
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ defimpl Boruta.Oauth.Authorization, for: Boruta.Oauth.AuthorizationCodeRequest d
alias Boruta.Oauth.AuthorizationCodeRequest
alias Boruta.Oauth.AuthorizationSuccess
alias Boruta.Oauth.Client
alias Boruta.Oauth.Error
alias Boruta.Oauth.IdToken
alias Boruta.Oauth.ResourceOwner
alias Boruta.Oauth.Scope
Expand Down Expand Up @@ -315,9 +316,16 @@ defimpl Boruta.Oauth.Authorization, for: Boruta.Oauth.AuthorizationCodeRequest d
{:ok, %{token: access_token}}

{_, true} ->
id_token = IdToken.generate(%{token: access_token}, nonce)

{:ok, %{token: access_token, id_token: id_token}}
case IdToken.generate(%{token: access_token}, nonce) do
{:ok, id_token} ->
{:ok, %{token: access_token, id_token: id_token}}
{:error, error} ->
{:error, %Error{
status: :internal_server_error,
error: :unknown_error,
error_description: error
}}
end

{_, false} ->
{:ok, %{token: access_token}}
Expand Down Expand Up @@ -434,6 +442,7 @@ end

defimpl Boruta.Oauth.Authorization, for: Boruta.Oauth.PreauthorizationCodeRequest do
alias Boruta.Oauth.Client
alias Boruta.Oauth.Error
alias Boruta.AccessTokensAdapter
alias Boruta.CodesAdapter
alias Boruta.Oauth.Authorization
Expand Down Expand Up @@ -501,9 +510,16 @@ defimpl Boruta.Oauth.Authorization, for: Boruta.Oauth.PreauthorizationCodeReques
{:ok, _code} <- CodesAdapter.revoke(code) do
case String.match?(scope, ~r/#{Scope.openid().name}/) do
true ->
id_token = IdToken.generate(%{token: access_token}, nonce)

{:ok, %{preauthorized_token: access_token, id_token: id_token}}
case IdToken.generate(%{token: access_token}, nonce) do
{:ok, id_token} ->
{:ok, %{preauthorized_token: access_token, id_token: id_token}}
{:error, error} ->
{:error, %Error{
status: :internal_server_error,
error: :unknown_error,
error_description: error
}}
end

false ->
{:ok, %{preauthorized_token: access_token}}
Expand Down Expand Up @@ -536,6 +552,7 @@ defimpl Boruta.Oauth.Authorization, for: Boruta.Oauth.TokenRequest do
alias Boruta.AccessTokensAdapter
alias Boruta.Oauth.Authorization
alias Boruta.Oauth.AuthorizationSuccess
alias Boruta.Oauth.Error
alias Boruta.Oauth.IdToken
alias Boruta.Oauth.ResourceOwner
alias Boruta.Oauth.Scope
Expand Down Expand Up @@ -612,8 +629,9 @@ defimpl Boruta.Oauth.Authorization, for: Boruta.Oauth.TokenRequest do
inserted_at: DateTime.utc_now()
}

id_token = IdToken.generate(%{base_token: base_token}, nonce)
{:ok, %{id_token: id_token}}
with {:ok, id_token} <- IdToken.generate(%{base_token: base_token}, nonce) do
{:ok, %{id_token: id_token}}
end

false ->
{:ok, %{}}
Expand All @@ -622,8 +640,16 @@ defimpl Boruta.Oauth.Authorization, for: Boruta.Oauth.TokenRequest do
"id_token", {:ok, tokens} ->
case String.match?(scope, ~r/#{Scope.openid().name}/) do
true ->
id_token = IdToken.generate(tokens, nonce)
{:ok, Map.put(tokens, :id_token, id_token)}
case IdToken.generate(tokens, nonce) do
{:ok, id_token} ->
{:ok, Map.put(tokens, :id_token, id_token)}
{:error, error} ->
{:error, %Error{
status: :internal_server_error,
error: :unknown_error,
error_description: error
}}
end

false ->
{:ok, tokens}
Expand All @@ -644,6 +670,13 @@ defimpl Boruta.Oauth.Authorization, for: Boruta.Oauth.TokenRequest do
) do
{:ok, Map.put(tokens, :token, access_token)}
end
_, {:error, error} ->
{:error,
%Error{
status: :internal_server_error,
error: :unknown_error,
error_description: "An error occurred during token creation: #{inspect(error)}."
}}
end)
end
end
Expand Down Expand Up @@ -1139,9 +1172,9 @@ defimpl Boruta.Oauth.Authorization, for: Boruta.Oauth.HybridRequest do
"id_token", {:ok, tokens} ->
case String.match?(scope, ~r/#{Scope.openid().name}/) do
true ->
id_token = IdToken.generate(tokens, nonce)

{:ok, Map.put(tokens, :id_token, id_token)}
with {:ok, id_token} <- IdToken.generate(tokens, nonce) do
{:ok, Map.put(tokens, :id_token, id_token)}
end

false ->
{:ok, tokens}
Expand Down
6 changes: 6 additions & 0 deletions lib/boruta/oauth/contexts/resource_owners.ex
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,11 @@ defmodule Boruta.Oauth.ResourceOwners do
@callback claims(resource_owner :: ResourceOwner.t(), scope :: String.t()) ::
claims :: Boruta.Oauth.IdToken.claims()

@doc """
Returns `id_token` trust_chain for the given client. This will be present in the `trust_chain` header of the resulting `id_token` of OpenID Connect flows.
"""
@callback trust_chain(client :: Boruta.Oauth.Client.t()) ::
{:ok, trust_chain :: list(String.t())} | {:error, reason :: String.t()}

@optional_callbacks claims: 2
end
3 changes: 2 additions & 1 deletion lib/boruta/oauth/error.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ defmodule Boruta.Oauth.Error do
alias Boruta.Oauth.TokenRequest

@type t :: %__MODULE__{
status: :internal_server_error | :bad_request | :unauthorized,
status: :internal_server_error | :bad_request | :unauthorized | :not_found,
error:
:invalid_request
| :invalid_client
Expand All @@ -24,6 +24,7 @@ defmodule Boruta.Oauth.Error do
| :invalid_code
| :invalid_resource_owner
| :login_required
| :not_found
| :unknown_error,
error_description: String.t(),
format: :query | :fragment | :json | nil,
Expand Down
8 changes: 5 additions & 3 deletions lib/boruta/oauth/schemas/id_token.ex
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,14 @@ defmodule Boruta.Oauth.IdToken do
}
}

@spec generate(tokens :: tokens(), nonce :: String.t()) :: id_token :: Oauth.Token.t()
@spec generate(tokens :: tokens(), nonce :: String.t()) ::
{:ok, id_token :: Oauth.Token.t()} | {:error, reason :: String.t()}
def generate(tokens, nonce) do
{base_token, payload} = payload(tokens, nonce, %{})

value = Client.Crypto.id_token_sign(payload, base_token.client)
%{base_token | type: "id_token", value: value}
with "" <> value <- Client.Crypto.id_token_sign(payload, base_token.client) do
{:ok, %{base_token | type: "id_token", value: value}}
end
end

defp payload(%{code: code} = tokens, nonce, acc) do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,9 @@ defmodule Boruta.OauthTest.AuthorizationCodeGrantTest do
end

test "returns a code with siopv2 (direct_post)" do
stub(Boruta.Support.ResourceOwners, :trust_chain, fn _client ->
{:ok, []}
end)
redirect_uri = "openid:"

assert {:authorize_success,
Expand Down Expand Up @@ -990,6 +993,9 @@ defmodule Boruta.OauthTest.AuthorizationCodeGrantTest do
end

test "returns a code with verifiable presentation (direct_post)" do
stub(Boruta.Support.ResourceOwners, :trust_chain, fn _client ->
{:ok, []}
end)
redirect_uri = "openid:"
insert(:scope, name: "vp_token", public: true)

Expand Down Expand Up @@ -1748,6 +1754,7 @@ defmodule Boruta.OauthTest.AuthorizationCodeGrantTest do
}}
end)
|> expect(:claims, fn _sub, _scope -> %{} end)
|> expect(:trust_chain, fn _client -> {:ok, []} end)

redirect_uri = List.first(client.redirect_uris)

Expand Down
15 changes: 10 additions & 5 deletions test/boruta/oauth/integration/hybrid_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,8 @@ 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)
|> expect(:trust_chain, fn _client -> {:ok, []} end)

redirect_uri = List.first(client.redirect_uris)
nonce = "nonce"
Expand Down Expand Up @@ -446,7 +447,8 @@ 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)
|> expect(:trust_chain, fn _client -> {:ok, []} end)

redirect_uri = List.first(client.redirect_uris)
nonce = "nonce"
Expand Down Expand Up @@ -580,7 +582,8 @@ 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)
|> expect(:trust_chain, fn _client -> {:ok, []} end)

redirect_uri = List.first(client.redirect_uris)
nonce = "nonce"
Expand Down Expand Up @@ -639,7 +642,8 @@ 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)
|> expect(:trust_chain, fn _client -> {:ok, []} end)

redirect_uri = List.first(client.redirect_uris)
nonce = "nonce"
Expand Down Expand Up @@ -699,7 +703,8 @@ 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)
|> expect(:trust_chain, fn _client -> {:ok, []} end)

redirect_uri = "https://wildcard-redirect-uri.uri"
nonce = "nonce"
Expand Down
2 changes: 2 additions & 0 deletions test/boruta/oauth/integration/implicit_grant_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ defmodule Boruta.OauthTest.ImplicitGrantTest do
ResourceOwners
|> expect(:authorized_scopes, fn _resource_owner -> [] end)
|> expect(:claims, fn _sub, _scope -> %{} end)
|> expect(:trust_chain, fn _client -> {:ok, []} end)

redirect_uri = List.first(client.redirect_uris)
nonce = "nonce"
Expand Down Expand Up @@ -471,6 +472,7 @@ defmodule Boruta.OauthTest.ImplicitGrantTest do
ResourceOwners
|> expect(:authorized_scopes, fn _resource_owner -> [] end)
|> expect(:claims, fn _sub, _scope -> %{} end)
|> expect(:trust_chain, fn _client -> {:ok, []} end)

redirect_uri = List.first(client.redirect_uris)
nonce = "nonce"
Expand Down
Loading
Loading