Skip to content

Commit d9aa634

Browse files
committed
support sdJwt and mDoc Cred Request at once
Signed-off-by: Johannes Tuerk <johannes.tuerk@lissi.id>
1 parent 986cef8 commit d9aa634

File tree

7 files changed

+69
-49
lines changed

7 files changed

+69
-49
lines changed

src/WalletFramework.Oid4Vc/Oid4Vci/Abstractions/IOid4VciClientService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public interface IOid4VciClientService
3838
/// <returns>
3939
/// A list of credentials.
4040
/// </returns>
41-
Task<Validation<OneOf<SdJwtRecord, MdocRecord>>> RequestCredential(IssuanceSession issuanceSession);
41+
Task<Validation<List<OneOf<SdJwtRecord, MdocRecord>>>> RequestCredential(IssuanceSession issuanceSession);
4242

4343
/// <summary>
4444
/// Processes a credential offer

src/WalletFramework.Oid4Vc/Oid4Vci/Authorization/DPop/Models/DPopToken.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace WalletFramework.Oid4Vc.Oid4Vci.Authorization.DPop.Models;
44

55
public record DPopToken
66
{
7-
internal OAuthToken Token { get; }
7+
internal OAuthToken Token { get; init; }
88

99
internal DPop DPop { get; }
1010

src/WalletFramework.Oid4Vc/Oid4Vci/Authorization/Models/OAuthToken.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace WalletFramework.Oid4Vc.Oid4Vci.Authorization.Models;
77
/// Represents a successful response from the OAuth 2.0 Authorization Server containing
88
/// the issued access token and related information.
99
/// </summary>
10-
public class OAuthToken
10+
public record OAuthToken
1111
{
1212
/// <summary>
1313
/// Indicates if the Token Request is still pending as the Credential Issuer

src/WalletFramework.Oid4Vc/Oid4Vci/Implementations/Oid4VciClientService.cs

Lines changed: 49 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using WalletFramework.Core.Functional;
1818
using WalletFramework.Core.Localization;
1919
using WalletFramework.MdocVc;
20+
using WalletFramework.Oid4Vc.Oid4Vci.Authorization.DPop.Models;
2021
using WalletFramework.SdJwtVc.Models.Records;
2122
using WalletFramework.SdJwtVc.Services.SdJwtVcHolderService;
2223
using static Newtonsoft.Json.JsonConvert;
@@ -217,7 +218,7 @@ public async Task<Uri> InitiateAuthFlow(Uri uri, ClientOptions clientOptions, Op
217218
clientOptions,
218219
validIssuerMetadata,
219220
authServerMetadata,
220-
new List<CredentialConfigurationId>(){validIssuerMetadata.CredentialConfigurationsSupported.Keys.First()});
221+
validIssuerMetadata.CredentialConfigurationsSupported.Keys.ToList());
221222

222223
var context = await _agentProvider.GetContextAsync();
223224
await _authFlowSessionStorage.StoreAsync(
@@ -271,7 +272,7 @@ select credentialOrTransactionId.Match(
271272
{
272273
var record = sdJwt.Decoded.ToRecord(configuration.AsT0, issuerMetadata, response.KeyId);
273274
var context = await _agentProvider.GetContextAsync();
274-
await _sdJwtService.SaveAsync(context, record);
275+
await _sdJwtService.AddAsync(context, record);
275276
return record;
276277
},
277278
async mdoc =>
@@ -302,7 +303,7 @@ from metadata in _issuerMetadataService.ProcessMetadata(offer.CredentialIssuer,
302303
}
303304

304305
/// <inheritdoc />
305-
public async Task<Validation<OneOf<SdJwtRecord, MdocRecord>>> RequestCredential(IssuanceSession issuanceSession)
306+
public async Task<Validation<List<OneOf<SdJwtRecord, MdocRecord>>>> RequestCredential(IssuanceSession issuanceSession)
306307
{
307308
var context = await _agentProvider.GetContextAsync();
308309

@@ -313,8 +314,7 @@ public async Task<Validation<OneOf<SdJwtRecord, MdocRecord>>> RequestCredential(
313314
.IssuerMetadata
314315
.CredentialConfigurationsSupported
315316
.Where(config => session.AuthorizationData.CredentialConfigurationIds.Contains(config.Key))
316-
.Select(pair => pair.Value)
317-
.First();
317+
.Select(pair => pair.Value);
318318

319319
var scope = session
320320
.AuthorizationData
@@ -336,37 +336,53 @@ public async Task<Validation<OneOf<SdJwtRecord, MdocRecord>>> RequestCredential(
336336
var token = await _tokenService.RequestToken(
337337
tokenRequest,
338338
session.AuthorizationData.AuthorizationServerMetadata);
339-
340-
var validResponse = await _credentialRequestService.RequestCredentials(
341-
credConfiguration,
342-
session.AuthorizationData.IssuerMetadata,
343-
token,
344-
session.AuthorizationData.ClientOptions);
339+
340+
List<OneOf<SdJwtRecord, MdocRecord>> credentials = new();
341+
//TODO: Make sure that it does not always request all available credConfigurations
342+
foreach (var configuration in credConfiguration)
343+
{
344+
var validResponse = await _credentialRequestService.RequestCredentials(
345+
configuration,
346+
session.AuthorizationData.IssuerMetadata,
347+
token,
348+
session.AuthorizationData.ClientOptions);
349+
350+
var result =
351+
from response in validResponse
352+
let cNonce = response.CNonce
353+
let credentialOrTransactionId = response.CredentialOrTransactionId
354+
select credentialOrTransactionId.Match(
355+
async credential => await credential.Value.Match<Task<OneOf<SdJwtRecord, MdocRecord>>>(
356+
async sdJwt =>
357+
{
358+
token = token.Match<OneOf<OAuthToken, DPopToken>>(
359+
oAuth => oAuth with { CNonce = cNonce.ToNullable()},
360+
dPop => dPop with { Token = dPop.Token with {CNonce = cNonce.ToNullable()}});
361+
362+
var record = sdJwt.Decoded.ToRecord(configuration.AsT0, session.AuthorizationData.IssuerMetadata, response.KeyId);
363+
await _sdJwtService.AddAsync(context, record);
364+
return record;
365+
},
366+
async mdoc =>
367+
{
368+
token = token.Match<OneOf<OAuthToken, DPopToken>>(
369+
oAuth => oAuth with { CNonce = cNonce.ToNullable()},
370+
dPop => dPop with { Token = dPop.Token with {CNonce = cNonce.ToNullable()}});
371+
372+
var displays = MdocFun.CreateMdocDisplays(configuration.AsT1);
373+
var record = mdoc.Decoded.ToRecord(displays);
374+
await _mdocStorage.Add(record);
375+
return record;
376+
}),
377+
// ReSharper disable once UnusedParameter.Local
378+
transactionId => throw new NotImplementedException());
379+
380+
await result.OnSuccess(async task => credentials.Add(await task));
381+
}
345382

346383
await _authFlowSessionStorage.DeleteAsync(context, session.AuthFlowSessionState);
347384

348-
var result =
349-
from response in validResponse
350-
let credentialOrTransactionId = response.CredentialOrTransactionId
351-
select credentialOrTransactionId.Match(
352-
async credential => await credential.Value.Match<Task<OneOf<SdJwtRecord, MdocRecord>>>(
353-
async sdJwt =>
354-
{
355-
var record = sdJwt.Decoded.ToRecord(credConfiguration.AsT0, session.AuthorizationData.IssuerMetadata, response.KeyId);
356-
await _sdJwtService.SaveAsync(context, record);
357-
return record;
358-
},
359-
async mdoc =>
360-
{
361-
var displays = MdocFun.CreateMdocDisplays(credConfiguration.AsT1);
362-
var record = mdoc.Decoded.ToRecord(displays);
363-
await _mdocStorage.Add(record);
364-
return record;
365-
}),
366-
// ReSharper disable once UnusedParameter.Local
367-
transactionId => throw new NotImplementedException());
368-
369-
return await result.OnSuccess(task => task);
385+
return credentials;
370386
}
371387

372388
private static AuthorizationCodeParameters CreateAndStoreCodeChallenge()

src/WalletFramework.SdJwtVc/Services/SdJwtVcHolderService/ISdJwtVcHolderService.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,18 @@ Task<List<SdJwtRecord>> ListAsync(
6666
int skip = 0);
6767

6868
/// <summary>
69-
/// Stores or updates a SD-JWT record.
69+
/// Updates a SD-JWT record.
7070
/// </summary>
7171
/// <param name="context">The agent context.</param>
7272
/// <param name="record">The SD-JWT record to be saved</param>
7373
/// <returns>A task representing the asynchronous operation. The task result contains the ID of the stored JWT record.</returns>
74-
Task SaveAsync(IAgentContext context, SdJwtRecord record);
74+
Task UpdateAsync(IAgentContext context, SdJwtRecord record);
75+
76+
/// <summary>
77+
/// Adds a SD-JWT record.
78+
/// </summary>
79+
/// <param name="context">The agent context.</param>
80+
/// <param name="record">The SD-JWT record to be saved</param>
81+
/// <returns>A task representing the asynchronous operation. The task result contains the ID of the stored JWT record.</returns>
82+
Task AddAsync(IAgentContext context, SdJwtRecord record);
7583
}

src/WalletFramework.SdJwtVc/Services/SdJwtVcHolderService/SdJwtVcHolderService.cs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -91,17 +91,13 @@ public Task<List<SdJwtRecord>> ListAsync(
9191
int skip = 0) => _recordService.SearchAsync<SdJwtRecord>(context.Wallet, query, null, count, skip);
9292

9393
/// <inheritdoc />
94-
public virtual async Task SaveAsync(IAgentContext context, SdJwtRecord record)
95-
{
96-
try
97-
{
94+
public virtual async Task AddAsync(IAgentContext context, SdJwtRecord record) =>
9895
await _recordService.AddAsync(context.Wallet, record);
99-
}
100-
catch (WalletItemAlreadyExistsException)
101-
{
102-
await _recordService.UpdateAsync(context.Wallet, record);
103-
}
104-
}
96+
97+
/// <inheritdoc />
98+
public virtual async Task UpdateAsync(IAgentContext context, SdJwtRecord record) =>
99+
await _recordService.UpdateAsync(context.Wallet, record);
100+
105101
}
106102

107103
internal static class SdJwtRecordExtensions

test/WalletFramework.Integration.Tests/WalletFramework.Integration.Tests/Oid4Vp/Oid4VpClientServiceTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public async Task CanExecuteOpenId4VpFlow()
8383

8484
var sdJwt = new SdJwtRecord();
8585

86-
await _sdJwtVcHolderService.SaveAsync(_agent1!.Context, sdJwt);
86+
await _sdJwtVcHolderService.AddAsync(_agent1!.Context, sdJwt);
8787

8888
//Act
8989
var (authorizationRequest, credentials) =

0 commit comments

Comments
 (0)