diff --git a/.github/workflows/publish-nuget.yaml b/.github/workflows/publish-nuget.yaml
index 5971e13..4e53d04 100644
--- a/.github/workflows/publish-nuget.yaml
+++ b/.github/workflows/publish-nuget.yaml
@@ -48,7 +48,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
- dotnet-version: 8.0.*
+ global-json-file: global.json
# - name: Install libindy library
# run: |
@@ -58,7 +58,7 @@ jobs:
# apt-get install -y libindy
- name: Build
- run: dotnet build $SOLUTION --configuration $BUILD_CONFIG -p:Version=$APP_VERSION --no-restore
+ run: dotnet build $SOLUTION --configuration $BUILD_CONFIG -p:Version=$APP_VERSION
- name: Run tests
run: |
diff --git a/src/WalletFramework.Oid4Vc/Oid4Vp/Models/AuthorizationRequest.cs b/src/WalletFramework.Oid4Vc/Oid4Vp/Models/AuthorizationRequest.cs
index c6f5964..f7207f0 100644
--- a/src/WalletFramework.Oid4Vc/Oid4Vp/Models/AuthorizationRequest.cs
+++ b/src/WalletFramework.Oid4Vc/Oid4Vp/Models/AuthorizationRequest.cs
@@ -1,6 +1,8 @@
using System.Security.Cryptography.X509Certificates;
+using LanguageExt;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
+using WalletFramework.Core.Functional;
using WalletFramework.Oid4Vc.Oid4Vp.PresentationExchange.Models;
using static WalletFramework.Oid4Vc.Oid4Vp.Models.ClientIdScheme;
@@ -118,7 +120,7 @@ private AuthorizationRequest(
/// Thrown when the request does not match the HAIP.
public static AuthorizationRequest CreateAuthorizationRequest(string authorizationRequestJson)
=> CreateAuthorizationRequest(JObject.Parse(authorizationRequestJson));
-
+
private static AuthorizationRequest CreateAuthorizationRequest(JObject authorizationRequestJson) =>
IsHaipConform(authorizationRequestJson)
? authorizationRequestJson.ToObject()
@@ -175,6 +177,6 @@ internal static AuthorizationRequest WithX509(
internal static AuthorizationRequest WithClientMetadata(
this AuthorizationRequest authorizationRequest,
- ClientMetadata? clientMetadata)
- => authorizationRequest with { ClientMetadata = clientMetadata };
+ Option clientMetadata)
+ => authorizationRequest with { ClientMetadata = clientMetadata.ToNullable() };
}
diff --git a/src/WalletFramework.Oid4Vc/Oid4Vp/Models/AuthorizationRequestByReference.cs b/src/WalletFramework.Oid4Vc/Oid4Vp/Models/AuthorizationRequestByReference.cs
new file mode 100644
index 0000000..912a83d
--- /dev/null
+++ b/src/WalletFramework.Oid4Vc/Oid4Vp/Models/AuthorizationRequestByReference.cs
@@ -0,0 +1,26 @@
+using System.Web;
+using LanguageExt;
+
+namespace WalletFramework.Oid4Vc.Oid4Vp.Models;
+
+public record AuthorizationRequestByReference
+{
+ public Uri AuthorizationRequestUri { get; }
+
+ public Uri RequestUri { get; }
+
+ private AuthorizationRequestByReference(Uri authorizationRequestUri, Uri requestUri) => (AuthorizationRequestUri, RequestUri) = (authorizationRequestUri, requestUri);
+
+ public static Option CreateAuthorizationRequestByReference(Uri uri)
+ {
+ var queryString = HttpUtility.ParseQueryString(uri.Query);
+ var clientId = queryString["client_id"];
+ var requestUri = queryString["request_uri"];
+
+ if (string.IsNullOrEmpty(clientId)
+ || string.IsNullOrEmpty(requestUri))
+ return Option.None;
+
+ return new AuthorizationRequestByReference(uri, new Uri(requestUri));
+ }
+}
diff --git a/src/WalletFramework.Oid4Vc/Oid4Vp/Models/AuthorizationRequestByValue.cs b/src/WalletFramework.Oid4Vc/Oid4Vp/Models/AuthorizationRequestByValue.cs
new file mode 100644
index 0000000..53319bc
--- /dev/null
+++ b/src/WalletFramework.Oid4Vc/Oid4Vp/Models/AuthorizationRequestByValue.cs
@@ -0,0 +1,30 @@
+using System.Web;
+using LanguageExt;
+
+namespace WalletFramework.Oid4Vc.Oid4Vp.Models;
+
+public record AuthorizationRequestByValue
+{
+ public Uri RequestUri { get; }
+
+ public Option PresentationDefinitionUri { get; }
+
+ private AuthorizationRequestByValue(Uri requestUri, Option presentationDefinitionUri) => (RequestUri, PresentationDefinitionUri) = (requestUri, presentationDefinitionUri);
+
+ public static Option CreateAuthorizationRequestByValue(Uri uri)
+ {
+ var queryString = HttpUtility.ParseQueryString(uri.Query);
+ var clientId = queryString["client_id"];
+ var nonce = queryString["nonce"];
+ var presentationDefinition = queryString["presentation_definition"];
+ var presentationDefinitionUri = queryString["presentation_definition_uri"];
+ var scope = queryString["scope"];
+
+ if (string.IsNullOrEmpty(clientId)
+ || string.IsNullOrEmpty(nonce)
+ || string.IsNullOrEmpty(presentationDefinition) && string.IsNullOrEmpty(presentationDefinitionUri) && string.IsNullOrEmpty(scope))
+ return Option.None;
+
+ return new AuthorizationRequestByValue(uri, string.IsNullOrEmpty(presentationDefinitionUri) ? Option.None : new Uri(presentationDefinitionUri));
+ }
+}
diff --git a/src/WalletFramework.Oid4Vc/Oid4Vp/Models/AuthorizationRequestUri.cs b/src/WalletFramework.Oid4Vc/Oid4Vp/Models/AuthorizationRequestUri.cs
new file mode 100644
index 0000000..ab3eccb
--- /dev/null
+++ b/src/WalletFramework.Oid4Vc/Oid4Vp/Models/AuthorizationRequestUri.cs
@@ -0,0 +1,21 @@
+using OneOf;
+
+namespace WalletFramework.Oid4Vc.Oid4Vp.Models;
+
+public struct AuthorizationRequestUri
+{
+ public OneOf Value { get; }
+
+ private AuthorizationRequestUri(OneOf value) => Value = value;
+
+ public static AuthorizationRequestUri FromUri(Uri uri)
+ {
+ return AuthorizationRequestByReference.CreateAuthorizationRequestByReference(uri).Match(
+ byReference => new AuthorizationRequestUri(byReference),
+ () => AuthorizationRequestByValue.CreateAuthorizationRequestByValue(uri).Match(
+ byValue => new AuthorizationRequestUri(byValue),
+ () => throw new InvalidOperationException("Authorization Request Uri is not a valid")
+ )
+ );
+ }
+}
diff --git a/src/WalletFramework.Oid4Vc/Oid4Vp/Models/HaipAuthorizationRequestUri.cs b/src/WalletFramework.Oid4Vc/Oid4Vp/Models/HaipAuthorizationRequestUri.cs
deleted file mode 100644
index 2cc271c..0000000
--- a/src/WalletFramework.Oid4Vc/Oid4Vp/Models/HaipAuthorizationRequestUri.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using Hyperledger.Aries.Utils;
-
-namespace WalletFramework.Oid4Vc.Oid4Vp.Models;
-
-///
-/// This class represents a haip conform OpenID4VP Authorization Request Uri.
-///
-public class HaipAuthorizationRequestUri
-{
- ///
- /// Gets or sets the uri of the request.
- ///
- public Uri Uri { get; set; } = null!;
-
- ///
- /// Gets or sets the value of the request_uri parameter.
- ///
- public string RequestUri { get; set; } = null!;
-
- ///
- /// Validates the hap conformity of an uri and returns a HaipAuthorizationRequestUri.
- ///
- ///
- /// The HaipAuthorizationRequestUri
- ///
- public static HaipAuthorizationRequestUri FromUri(Uri uri)
- {
- var request = uri.GetQueryParam("request_uri");
- if (string.IsNullOrEmpty(request))
- throw new InvalidOperationException("HAIP requires request_uri parameter");
-
- return new HaipAuthorizationRequestUri()
- {
- RequestUri = request,
- Uri = uri
- };
- }
-}
diff --git a/src/WalletFramework.Oid4Vc/Oid4Vp/Models/RequestObject.cs b/src/WalletFramework.Oid4Vc/Oid4Vp/Models/RequestObject.cs
index 649c799..a1a344d 100644
--- a/src/WalletFramework.Oid4Vc/Oid4Vp/Models/RequestObject.cs
+++ b/src/WalletFramework.Oid4Vc/Oid4Vp/Models/RequestObject.cs
@@ -52,6 +52,9 @@ public static RequestObject CreateRequestObject(string requestObjectJson)
{
var tokenHandler = new JwtSecurityTokenHandler();
+ if (requestObjectJson.Split('.').Length == 2)
+ requestObjectJson += ".";
+
var jwt = tokenHandler.ReadJwtToken(requestObjectJson);
return new RequestObject(jwt);
@@ -73,7 +76,7 @@ public static class RequestObjectExtensions
///
/// The validated request object.
/// Throws when validation fails
- public static RequestObject ValidateJwt(this RequestObject requestObject)
+ public static RequestObject ValidateJwtSignature(this RequestObject requestObject)
{
var jwt = (JwtSecurityToken)requestObject;
var pubKey = requestObject.GetLeafCertificate().GetPublicKey();
diff --git a/src/WalletFramework.Oid4Vc/Oid4Vp/Services/AuthorizationRequestService.cs b/src/WalletFramework.Oid4Vc/Oid4Vp/Services/AuthorizationRequestService.cs
new file mode 100644
index 0000000..8ad3fad
--- /dev/null
+++ b/src/WalletFramework.Oid4Vc/Oid4Vp/Services/AuthorizationRequestService.cs
@@ -0,0 +1,107 @@
+using System.Net.Http.Headers;
+using System.Web;
+using LanguageExt;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using WalletFramework.Oid4Vc.Oid4Vp.Models;
+using static WalletFramework.Oid4Vc.Oid4Vp.Models.RequestObject;
+using static WalletFramework.Oid4Vc.Oid4Vp.Models.ClientIdScheme.ClientIdSchemeValue;
+using static Newtonsoft.Json.JsonConvert;
+
+namespace WalletFramework.Oid4Vc.Oid4Vp.Services;
+
+public class AuthorizationRequestService(
+ IHttpClientFactory httpClientFactory) : IAuthorizationRequestService
+{
+ public async Task CreateAuthorizationRequest(AuthorizationRequestByReference authRequestByReference)
+ {
+ var httpClient = httpClientFactory.CreateClient();
+ httpClient.DefaultRequestHeaders.Clear();
+
+ var requestObjectJson = await httpClient.GetStringAsync(authRequestByReference.RequestUri);
+ var requestObject = CreateRequestObject(requestObjectJson);
+
+ var clientMetadata = await FetchClientMetadata(requestObject.ToAuthorizationRequest());
+
+ return requestObject.ClientIdScheme.Value switch
+ {
+ X509SanDns => requestObject
+ .ValidateJwtSignature()
+ .ValidateTrustChain()
+ .ValidateSanName()
+ .ToAuthorizationRequest()
+ .WithX509(requestObject)
+ .WithClientMetadata(clientMetadata),
+ //TODO: Remove Redirect URi in the future (kept for now for compatibility)
+ RedirectUri => requestObject
+ .ToAuthorizationRequest()
+ .WithClientMetadata(clientMetadata),
+ VerifierAttestation =>
+ throw new NotImplementedException("Verifier Attestation not yet implemented"),
+ _ => throw new InvalidOperationException(
+ $"Client ID Scheme {requestObject.ClientIdScheme} not supported")
+ };
+ }
+
+ public async Task CreateAuthorizationRequest(AuthorizationRequestByValue authRequestByValue)
+ {
+ var queryParams = HttpUtility.ParseQueryString(authRequestByValue.RequestUri.Query);
+
+ var jObject = new JObject();
+ foreach (var key in queryParams.AllKeys)
+ {
+ var value = queryParams[key];
+
+ if (string.IsNullOrEmpty(value))
+ continue;
+
+ try
+ {
+ jObject[key] = JToken.Parse(value);
+ }
+ catch (JsonReaderException)
+ {
+ jObject[key] = value;
+ }
+ }
+
+ if (jObject.TryGetValue("presentation_definition_uri", out var presentationDefinitionUri))
+ {
+ var httpClient = httpClientFactory.CreateClient();
+ httpClient.DefaultRequestHeaders.Clear();
+
+ var requestObjectJson = await httpClient.GetStringAsync(presentationDefinitionUri.ToString());
+ jObject["presentation_definition"] = JToken.Parse(requestObjectJson);
+ }
+
+ var jsonString = SerializeObject(jObject);
+ var authRequest = AuthorizationRequest.CreateAuthorizationRequest(jsonString);
+
+ var clientMetadata = await FetchClientMetadata(authRequest);
+
+ return authRequest.ClientIdScheme.Value switch
+ {
+ RedirectUri => authRequest
+ .WithClientMetadata(clientMetadata),
+ _ => throw new InvalidOperationException(
+ $"Client ID Scheme {authRequest.ClientIdScheme.Value} not supported when passing the Authorization Request within the Uri")
+ };
+ }
+
+ private async Task