Packages
boruta
3.0.0-beta.4
3.0.0-beta.4
3.0.0-beta.3
3.0.0-beta.2
3.0.0-beta.1
2.3.6
2.3.5
2.3.4
2.3.3
2.3.2
2.3.1
2.3.0
2.2.2
2.2.1
2.2.0
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.0.1
2.0.0
2.0.0-rc.1
2.0.0-rc.0
1.2.1
1.2.0
1.1.0
1.0.3
1.0.2
1.0.1
1.0.0
1.0.0-rc.3
1.0.0-rc.2
1.0.0-rc.1
1.0.0-rc.0
0.2.1
0.2.0
0.1.1
0.1.0
0.1.0-rc.5
0.1.0-rc.4
0.1.0-rc.3
0.1.0-rc.2
0.1.0-rc.1
Core of an OAuth/OpenID Connect provider enabling authorization in your applications.
Current section
Files
Jump to
Current section
Files
lib/boruta/adapters/internal/signatures.ex
defmodule Boruta.Internal.Signatures do
@behaviour Boruta.Oauth.Signatures
defmodule Token do
@moduledoc false
use Joken.Config
def token_config, do: %{}
end
@moduledoc false
alias Boruta.Internal.Signatures.SigningKey
alias Boruta.Oauth.Client
@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]
]
@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]
@spec hash_binary_size(Client.t()) :: binary_size :: integer()
def hash_binary_size(%Client{id_token_signature_alg: signature_alg}),
do: @signature_algorithms[String.to_atom(signature_alg)][:binary_size]
@spec hash(string :: String.t(), client :: Client.t()) :: hash :: String.t()
def hash(string, client) do
hash_alg(client)
|> Atom.to_string()
|> String.downcase()
|> String.to_atom()
|> :crypto.hash(string)
|> binary_part(0, hash_binary_size(client))
|> Base.url_encode64(padding: false)
end
@spec id_token_sign(payload :: map(), client :: Client.t()) ::
jwt :: String.t() | {:error, reason :: String.t()}
def id_token_sign(
payload,
%Client{
id_token_signature_alg: signature_alg
} = 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 userinfo_sign(payload :: map(), client :: Client.t()) ::
jwt :: String.t() | {:error, reason :: String.t()}
def userinfo_sign(
payload,
%Client{
userinfo_signed_response_alg: signature_alg
} = client
) do
with {:ok, signing_key} <- get_signing_key(client, :userinfo) do
signer =
case userinfo_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 verifiable_credential_sign(payload :: map(), client :: Client.t(), format :: String.t()) ::
jwt :: String.t() | {:error, reason :: String.t()}
def verifiable_credential_sign(
payload,
%Client{
id_token_signature_alg: signature_alg
} = client,
_format
) do
with {:ok, signing_key} <- get_signing_key(client, :verifiable_credential) 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},
%{
"typ" => "JWT",
"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 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]
@spec id_token_signature_type(Client.t()) :: id_token_signature_type :: atom()
def id_token_signature_type(%Client{id_token_signature_alg: signature_alg}),
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)
}}
end
defp get_signing_key(client, :userinfo) do
{:ok,
%SigningKey{
type: :internal,
private_key: client.private_key,
secret: client.secret,
kid: client.id
}}
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)
}}
end
end