Skip to content

Commit 3aef45f

Browse files
committed
Add request_uri_method post support
Signed-off-by: Johannes Tuerk <johannes.tuerk@lissi.id>
1 parent 0c4566a commit 3aef45f

File tree

4 files changed

+98
-17
lines changed

4 files changed

+98
-17
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace WalletFramework.Oid4Vc.Oid4Vp.Errors;
2+
3+
/// <summary>
4+
/// Error indicating that the request_uri_method is not supported or invalid.
5+
/// This error is defined in OpenID4VP specification section 5.10.2.
6+
/// </summary>
7+
public record InvalidRequestUriMethodError : VpError
8+
{
9+
private const string Code = "invalid_request_uri_method";
10+
11+
public InvalidRequestUriMethodError(string message) : base(Code, message) { }
12+
}

src/WalletFramework.Oid4Vc/Oid4Vp/Models/AuthorizationRequestByReference.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,23 @@ public record AuthorizationRequestByReference
88
public Uri AuthorizationRequestUri { get; }
99

1010
public Uri RequestUri { get; }
11+
12+
public Option<string> RequestUriMethod { get; }
1113

12-
private AuthorizationRequestByReference(Uri authorizationRequestUri, Uri requestUri) => (AuthorizationRequestUri, RequestUri) = (authorizationRequestUri, requestUri);
14+
private AuthorizationRequestByReference(Uri authorizationRequestUri, Uri requestUri, Option<string> requestUriMethod) =>
15+
(AuthorizationRequestUri, RequestUri, RequestUriMethod) = (authorizationRequestUri, requestUri, requestUriMethod);
1316

1417
public static Option<AuthorizationRequestByReference> CreateAuthorizationRequestByReference(Uri uri)
1518
{
1619
var queryString = HttpUtility.ParseQueryString(uri.Query);
1720
var clientId = queryString["client_id"];
1821
var requestUri = queryString["request_uri"];
22+
var requestUriMethod = queryString["request_uri_method"];
1923

2024
if (string.IsNullOrEmpty(clientId)
2125
|| string.IsNullOrEmpty(requestUri))
2226
return Option<AuthorizationRequestByReference>.None;
2327

24-
return new AuthorizationRequestByReference(uri, new Uri(requestUri));
28+
return new AuthorizationRequestByReference(uri, new Uri(requestUri), requestUriMethod);
2529
}
2630
}

src/WalletFramework.Oid4Vc/Oid4Vp/Models/RequestObject.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ private RequestObject(JwtSecurityToken token, AuthorizationRequest authorization
4848
/// Creates a new instance of the <see cref="RequestObject" /> class.
4949
/// </summary>
5050
public static Validation<AuthorizationRequestCancellation, RequestObject> FromStr(
51-
string requestObjectJson)
51+
string requestObjectJson, Option<string> walletNonce)
5252
{
5353
var tokenHandler = new JwtSecurityTokenHandler();
5454

@@ -66,6 +66,19 @@ public static Validation<AuthorizationRequestCancellation, RequestObject> FromSt
6666
return new AuthorizationRequestCancellation(Option<Uri>.None, [error]);
6767
}
6868

69+
walletNonce.IfSome(nonce =>
70+
{
71+
if (jwt.Payload.TryGetValue("wallet_nonce", out var nonceValue))
72+
{
73+
if (nonceValue.ToString() != nonce)
74+
throw new InvalidOperationException("wallet_nonce in request object does not match the provided wallet_nonce");
75+
}
76+
else
77+
{
78+
throw new InvalidOperationException("wallet_nonce is required but not present in the Request Object");
79+
}
80+
});
81+
6982
var json = jwt.Payload.SerializeToJson();
7083

7184
return

src/WalletFramework.Oid4Vc/Oid4Vp/Services/AuthorizationRequestService.cs

Lines changed: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Net.Http.Headers;
22
using System.Web;
3+
using Hyperledger.Aries.Utils;
34
using LanguageExt;
45
using Newtonsoft.Json;
56
using Newtonsoft.Json.Linq;
@@ -18,6 +19,9 @@ public class AuthorizationRequestService(
1819
IHttpClientFactory httpClientFactory,
1920
IRpAuthService rpAuthService) : IAuthorizationRequestService
2021
{
22+
private const string RequestUriMethodGet = "get";
23+
private const string RequestUriMethodPost = "post";
24+
2125
public async Task<Validation<AuthorizationRequestCancellation, AuthorizationRequest>> GetAuthorizationRequest(
2226
AuthorizationRequestUri authorizationRequestUri) =>
2327
await authorizationRequestUri.Value.Match(
@@ -40,29 +44,22 @@ await authorizationRequestUri.Value.Match(
4044
seq => seq
4145
);
4246
},
43-
async value =>
44-
{
45-
return await GetAuthRequestByValue(value);
46-
}
47-
);
47+
async value => await GetAuthRequestByValue(value));
4848

4949
private async Task<Validation<AuthorizationRequestCancellation, RequestObject>> GetRequestObject(
5050
AuthorizationRequestByReference authRequestByReference)
5151
{
52-
var httpClient = httpClientFactory.CreateClient();
53-
httpClient.DefaultRequestHeaders.Clear();
54-
55-
var jsonString = await httpClient.GetStringAsync(authRequestByReference.RequestUri);
56-
var requestObjectValidation = FromStr(jsonString);
52+
var requestObjectValidation = await FetchRequestObject(authRequestByReference);
5753

58-
return await requestObjectValidation.MatchAsync(async requestObject =>
54+
return await requestObjectValidation.MatchAsync(
55+
async requestObject =>
5956
{
6057
var authRequest = requestObject.ToAuthorizationRequest();
6158
var clientMetadataOption =
6259
await FetchClientMetadata(authRequest).OnException(_ => Option<ClientMetadata>.None);
63-
60+
6461
var error = new InvalidRequestError($"Client ID Scheme {requestObject.ClientIdScheme} is not supported");
65-
62+
6663
Validation<AuthorizationRequestCancellation, RequestObject> result =
6764
requestObject.ClientIdScheme.Value switch
6865
{
@@ -79,7 +76,7 @@ private async Task<Validation<AuthorizationRequestCancellation, RequestObject>>
7976
.WithClientMetadata(clientMetadataOption),
8077
_ => new AuthorizationRequestCancellation(authRequest.GetResponseUriMaybe(), [error])
8178
};
82-
79+
8380
return result;
8481
},
8582
seq => seq);
@@ -137,6 +134,61 @@ private async Task<Validation<AuthorizationRequestCancellation, AuthorizationReq
137134
},
138135
seq => seq);
139136
}
137+
138+
private async Task<Validation<AuthorizationRequestCancellation, RequestObject>> FetchRequestObject(AuthorizationRequestByReference authRequestByReference)
139+
{
140+
return await authRequestByReference.RequestUriMethod.Match<Task<Validation<AuthorizationRequestCancellation, RequestObject>>>(
141+
async method =>
142+
{
143+
return method.ToLowerInvariant() switch
144+
{
145+
RequestUriMethodGet => await FetchRequestObjectViaGet(authRequestByReference),
146+
RequestUriMethodPost => await FetchRequestObjectViaPost(authRequestByReference),
147+
_ => new AuthorizationRequestCancellation(Option<Uri>.None, [new InvalidRequestUriMethodError($"Unsupported request_uri_method: '{method}'.")])
148+
};
149+
},
150+
async () => await FetchRequestObjectViaGet(authRequestByReference));
151+
}
152+
153+
private async Task<Validation<AuthorizationRequestCancellation, RequestObject>> FetchRequestObjectViaPost(AuthorizationRequestByReference authRequestByReference)
154+
{
155+
var httpClient = httpClientFactory.CreateClient();
156+
httpClient.DefaultRequestHeaders.Clear();
157+
158+
var walletNonce = Base64UrlEncoder.Encode(Guid.NewGuid().ToString());
159+
var keyValuePairs = new List<KeyValuePair<string, string>>();
160+
keyValuePairs.Add(new KeyValuePair<string, string>("wallet_nonce", walletNonce));
161+
keyValuePairs.Add(new KeyValuePair<string, string>("wallet_metadata", new JObject()
162+
{
163+
["vp_formats_supported"] = new JObject()
164+
{
165+
["dc+sd-jwt"] = new JObject()
166+
{
167+
["sd-jwt_alg_values"] = new JArray(){ "ES256", "ES384", "ES512", "RS256" },
168+
["kb-jwt_alg_values"] = new JArray(){ "ES256" }
169+
},
170+
["mso_mdoc"] = new JObject()
171+
{
172+
["issuerauth_alg_values"] = new JArray(){ "ES256" },
173+
["deviceauth_alg_values"] = new JArray(){ "ES256" }
174+
}
175+
}
176+
}.ToString()));
177+
178+
var response = await httpClient.PostAsync(authRequestByReference.RequestUri, new FormUrlEncodedContent(keyValuePairs));
179+
response.EnsureSuccessStatusCode();
180+
var stringContent = await response.Content.ReadAsStringAsync();
181+
182+
return FromStr(stringContent, walletNonce);
183+
}
184+
185+
private async Task<Validation<AuthorizationRequestCancellation, RequestObject>> FetchRequestObjectViaGet(AuthorizationRequestByReference authRequestByReference)
186+
{
187+
var httpClient = httpClientFactory.CreateClient();
188+
httpClient.DefaultRequestHeaders.Clear();
189+
190+
return FromStr(await httpClient.GetStringAsync(authRequestByReference.RequestUri), Option<string>.None);
191+
}
140192

141193
private async Task<Option<ClientMetadata>> FetchClientMetadata(AuthorizationRequest authorizationRequest)
142194
{

0 commit comments

Comments
 (0)