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
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,32 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) according to OAuth / OpenID connect specifications, changes may break in order to comply with those.

## [unreleased]

### Added

- support OID4VCI tx codes
- return oauth token in credential issuance response
- expose previous code in oauth tokens
- support sd-jwt in presentations
- accept `JWT` proof `typ`
- signatures adapters with the implementation of Universal adapter
- better code errors on direct post requests
- verifiable credentials nested claims management
- agent credentials and agent code flows


### Changed

- verifiable credentials payloads (sd-jwt, jwt_vc)
- status tokens refactoring and improvement

### Fixed

- verifiable presentations various improvement and fixes
- oauth client long dids persistence
- do not use es256 to verify eddsa jwts

## [3.0.0-beta.3] - 2024-11-21

### Changed
Expand Down
4 changes: 3 additions & 1 deletion lib/boruta/adapters/ecto/codes.ex
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,9 @@ defmodule Boruta.Ecto.Codes do
code_challenge_method: code_challenge_method,
authorization_details: authorization_details,
presentation_definition: params[:presentation_definition],
public_client_id: params[:public_client_id]
public_client_id: params[:public_client_id],
relying_party_redirect_uri: params[:relying_party_redirect_uri],
previous_code: params[:previous_code]
}
])

Expand Down
11 changes: 9 additions & 2 deletions lib/boruta/adapters/ecto/schemas/token.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ defmodule Boruta.Ecto.Token do
c_nonce: String.t(),
scope: String.t(),
redirect_uri: String.t(),
relying_party_redirect_uri: String.t() | nil,
expires_at: integer(),
client: Client.t(),
public_client_id: String.t(),
Expand Down Expand Up @@ -70,6 +71,7 @@ defmodule Boruta.Ecto.Token do
field(:c_nonce, :string)
field(:scope, :string, default: "")
field(:redirect_uri, :string)
field(:relying_party_redirect_uri, :string)
field(:expires_at, :integer)
field(:revoked_at, :utc_datetime_usec)
field(:refresh_token_revoked_at, :utc_datetime_usec)
Expand Down Expand Up @@ -99,6 +101,7 @@ defmodule Boruta.Ecto.Token do
|> cast(attrs, [
:client_id,
:redirect_uri,
:relying_party_redirect_uri,
:sub,
:state,
:nonce,
Expand Down Expand Up @@ -260,7 +263,9 @@ defmodule Boruta.Ecto.Token do
:nonce,
:scope,
:authorization_details,
:presentation_definition
:presentation_definition,
:relying_party_redirect_uri,
:previous_code
])
|> validate_required([:authorization_code_ttl, :client_id, :sub, :redirect_uri])
|> foreign_key_constraint(:client_id)
Expand All @@ -284,7 +289,9 @@ defmodule Boruta.Ecto.Token do
:code_challenge,
:code_challenge_method,
:authorization_details,
:presentation_definition
:presentation_definition,
:relying_party_redirect_uri,
:previous_code
])
|> validate_required([
:authorization_code_ttl,
Expand Down
90 changes: 55 additions & 35 deletions lib/boruta/oauth/authorization.ex
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ defmodule Boruta.Oauth.AuthorizationSuccess do
public_client_id: nil,
client: nil,
redirect_uri: nil,
relying_party_redirect_uri: nil,
resource_owner: nil,
sub: nil,
scope: nil,
Expand All @@ -59,6 +60,7 @@ defmodule Boruta.Oauth.AuthorizationSuccess do
access_token: Boruta.Oauth.Token.t() | nil,
code: Boruta.Oauth.Token.t() | nil,
redirect_uri: String.t() | nil,
relying_party_redirect_uri: String.t() | nil,
sub: String.t() | nil,
resource_owner: Boruta.Oauth.ResourceOwner.t() | nil,
scope: String.t(),
Expand Down Expand Up @@ -270,15 +272,13 @@ defimpl Boruta.Oauth.Authorization, for: Boruta.Oauth.AuthorizationCodeRequest d
redirect_uri: redirect_uri,
client: client,
code_verifier: code_verifier
}),
{:ok, %ResourceOwner{sub: sub}} <-
Authorization.ResourceOwner.authorize(resource_owner: code.resource_owner) do
}) do
{:ok,
%AuthorizationSuccess{
client: client,
code: code,
redirect_uri: redirect_uri,
sub: sub,
sub: code.resource_owner.sub,
scope: code.scope,
nonce: code.nonce,
authorization_details: code.authorization_details
Expand Down Expand Up @@ -952,23 +952,26 @@ defimpl Boruta.Oauth.Authorization, for: Boruta.Oauth.PresentationRequest do
alias Boruta.Oauth.CodeRequest
alias Boruta.Oauth.Error
alias Boruta.Oauth.PresentationRequest
alias Boruta.Oauth.ResourceOwner
alias Boruta.Oauth.Token
alias Boruta.Openid.VerifiableCredentials
alias Boruta.Openid.VerifiablePresentations

def preauthorize(
%PresentationRequest{
authorization_details: authorization_details,
client_id: client_id,
resource_owner: resource_owner,
redirect_uri: redirect_uri,
state: state,
nonce: nonce,
scope: scope,
client_metadata: client_metadata,
code: code,
code_challenge: code_challenge,
code_challenge_method: code_challenge_method,
authorization_details: authorization_details,
client_metadata: client_metadata,
response_type: response_type
nonce: nonce,
redirect_uri: redirect_uri,
relying_party_redirect_uri: relying_party_redirect_uri,
resource_owner: resource_owner,
response_type: response_type,
scope: scope,
state: state
} = request
) do
with [response_type] = response_types <-
Expand All @@ -977,10 +980,11 @@ defimpl Boruta.Oauth.Authorization, for: Boruta.Oauth.PresentationRequest do
scope,
resource_owner.presentation_configuration
),
# TODO perform public client redirect_uri check
{:ok, client} <-
(case client_id do
"did:" <> _key ->
{:ok, ClientsAdapter.public!()}
{:ok, ClientsAdapter.public!()}

_ ->
Authorization.Client.authorize(
Expand All @@ -990,10 +994,18 @@ defimpl Boruta.Oauth.Authorization, for: Boruta.Oauth.PresentationRequest do
grant_type: response_type
)
end),
{:ok, resource_owner} <-
(case client_id do
"did:" <> _key -> {:ok, resource_owner}
_ -> Authorization.ResourceOwner.authorize(resource_owner: resource_owner)
{:ok, client} <-
(case relying_party_redirect_uri do
nil ->
{:ok, client}

relying_party_redirect_uri ->
Authorization.Client.authorize(
id: client_id,
source: nil,
redirect_uri: relying_party_redirect_uri,
grant_type: response_type
)
end),
:ok <- Authorization.Nonce.authorize(request),
:ok <- VerifiableCredentials.validate_authorization_details(authorization_details),
Expand All @@ -1011,19 +1023,21 @@ defimpl Boruta.Oauth.Authorization, for: Boruta.Oauth.PresentationRequest do

{:ok,
%AuthorizationSuccess{
response_types: response_types,
authorization_details: Jason.decode!(authorization_details),
client: client,
code: code,
code_challenge: code_challenge,
code_challenge_method: code_challenge_method,
nonce: nonce,
presentation_definition: presentation_definition,
redirect_uri: redirect_uri,
public_client_id: client_id,
client: client,
sub: resource_owner.sub,
redirect_uri: redirect_uri,
relying_party_redirect_uri: relying_party_redirect_uri,
response_mode: client.response_mode,
response_types: response_types,
scope: scope,
state: state,
nonce: nonce,
code_challenge: code_challenge,
code_challenge_method: code_challenge_method,
authorization_details: Jason.decode!(authorization_details),
response_mode: client.response_mode
sub: client_id
}}
else
{:error, :invalid_code_challenge} ->
Expand All @@ -1042,26 +1056,32 @@ defimpl Boruta.Oauth.Authorization, for: Boruta.Oauth.PresentationRequest do
def token(request) do
with {:ok,
%AuthorizationSuccess{
response_types: response_types,
authorization_details: authorization_details,
client: client,
code: code,
code_challenge: code_challenge,
code_challenge_method: code_challenge_method,
nonce: nonce,
presentation_definition: presentation_definition,
redirect_uri: redirect_uri,
public_client_id: public_client_id,
client: client,
sub: sub,
redirect_uri: redirect_uri,
relying_party_redirect_uri: relying_party_redirect_uri,
response_mode: response_mode,
response_types: response_types,
scope: scope,
state: state,
nonce: nonce,
code_challenge: code_challenge,
code_challenge_method: code_challenge_method,
authorization_details: authorization_details,
response_mode: response_mode
sub: sub
}} <-
preauthorize(request) do
# TODO create a presentation specific code
with {:ok, code} <-
CodesAdapter.create(%{
resource_owner: %ResourceOwner{sub: sub},
client: client,
public_client_id: public_client_id,
redirect_uri: redirect_uri,
relying_party_redirect_uri: relying_party_redirect_uri,
previous_code: code,
sub: sub,
scope: scope,
state: state,
Expand Down
3 changes: 2 additions & 1 deletion lib/boruta/oauth/authorization/client.ex
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,11 @@ defmodule Boruta.Oauth.Authorization.Client do
def authorize(
id: "did:" <> _key,
source: _source,
redirect_uri: _redirect_uri,
redirect_uri: redirect_uri,
grant_type: grant_type
) do
with %Client{} = client <- ClientsAdapter.public!(),
true <- Enum.member?(client.redirect_uris, redirect_uri) || {:error, "Invalid redirect_uri."},
true <- Client.wallet_grant_type_supported?(client, grant_type) do
{:ok, client}
else
Expand Down
10 changes: 10 additions & 0 deletions lib/boruta/oauth/authorization/resource_owner.ex
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,19 @@ defmodule Boruta.Oauth.Authorization.ResourceOwner do
}}
end
end

def authorize(resource_owner: %ResourceOwner{sub: "did:" <> _key}) do
{:error, %Error{
status: :unauthorized,
error: :invalid_resource_owner,
error_description: "Resource owner is invalid."
}}
end

def authorize(resource_owner: %ResourceOwner{sub: sub} = resource_owner) when not is_nil(sub) do
{:ok, resource_owner}
end

def authorize(_) do
{:error, %Error{
status: :unauthorized,
Expand Down
5 changes: 5 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,10 @@ defmodule Boruta.Oauth.ResourceOwners do
@callback claims(resource_owner :: ResourceOwner.t(), scope :: String.t()) ::
claims :: Boruta.Oauth.IdToken.claims()

# TODO documentation
@callback from_holder(holder :: map()) ::
{:ok, resource_owner :: ResourceOwner.t()}
| {:error, reason :: String.t()}

@optional_callbacks claims: 2
end
6 changes: 4 additions & 2 deletions lib/boruta/oauth/error.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ defmodule Boruta.Oauth.Error do
error_description: String.t(),
format: :query | :fragment | :json | nil,
redirect_uri: String.t() | nil,
state: String.t() | nil
state: String.t() | nil,
code: String.t() | nil
}

@enforce_keys [:status, :error, :error_description]
Expand All @@ -37,7 +38,8 @@ defmodule Boruta.Oauth.Error do
error_description: nil,
format: nil,
redirect_uri: nil,
state: nil
state: nil,
code: nil

@doc """
Returns the OAuth error augmented with the format according to request type.
Expand Down
2 changes: 2 additions & 0 deletions lib/boruta/oauth/request/base.ex
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,13 @@ defmodule Boruta.Oauth.Request.Base do
client_id: params["client_id"],
resource_owner: params["resource_owner"],
redirect_uri: params["redirect_uri"],
relying_party_redirect_uri: params["relying_party_redirect_uri"],
state: params["state"],
nonce: params["nonce"],
prompt: params["prompt"],
code_challenge: params["code_challenge"],
code_challenge_method: params["code_challenge_method"],
code: params["code"],
scope: params["scope"],
client_metadata: client_metadata,
response_type: params["response_type"]
Expand Down
4 changes: 4 additions & 0 deletions lib/boruta/oauth/requests/presentation_request.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ defmodule Boruta.Oauth.PresentationRequest do
"""
@type t :: %__MODULE__{
client_id: String.t(),
code: String.t() | nil,
resource_owner: Boruta.Oauth.ResourceOwner.t(),
redirect_uri: String.t(),
relying_party_redirect_uri: String.t(),
state: String.t(),
nonce: String.t(),
prompt: String.t(),
Expand All @@ -24,8 +26,10 @@ defmodule Boruta.Oauth.PresentationRequest do

@enforce_keys [:client_id, :redirect_uri]
defstruct client_id: nil,
code: nil,
resource_owner: nil,
redirect_uri: nil,
relying_party_redirect_uri: nil,
state: "",
nonce: "",
prompt: "",
Expand Down
2 changes: 2 additions & 0 deletions lib/boruta/oauth/schemas/token.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ defmodule Boruta.Oauth.Token do
c_nonce: nil,
scope: nil,
redirect_uri: nil,
relying_party_redirect_uri: nil,
expires_at: nil,
client: nil,
public_client_id: nil,
Expand Down Expand Up @@ -53,6 +54,7 @@ defmodule Boruta.Oauth.Token do
c_nonce: String.t() | nil,
scope: String.t(),
redirect_uri: String.t() | nil,
relying_party_redirect_uri: String.t() | nil,
expires_at: integer() | nil,
client: Boruta.Oauth.Client.t() | nil,
public_client_id: String.t() | nil,
Expand Down
Loading
Loading