Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delete one time use credentials #225

Merged
merged 3 commits into from
Nov 21, 2024
Merged
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
25 changes: 18 additions & 7 deletions src/WalletFramework.MdocVc/MdocRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ public CredentialId CredentialId

public CredentialState CredentialState { get; }

//TODO: Must be set when batch issuance is implemented
// public bool OneTimeUse { get; }
/// <summary>
/// Tracks whether it's a one-time use Mdoc.
/// </summary>
public bool OneTimeUse { get; set; }

public Option<DateTime> ExpiresAt { get; }

Expand All @@ -56,7 +58,8 @@ public MdocRecord(
KeyId keyId,
CredentialSetId credentialSetId,
CredentialState credentialState,
Option<DateTime> expiresAt)
Option<DateTime> expiresAt,
bool isOneTimeUse = false)
{
CredentialId = CredentialId.CreateCredentialId();
Mdoc = mdoc;
Expand All @@ -65,6 +68,7 @@ public MdocRecord(
CredentialSetId = credentialSetId;
CredentialState = credentialState;
ExpiresAt = expiresAt;
OneTimeUse = isOneTimeUse;
}

#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
Expand Down Expand Up @@ -108,6 +112,7 @@ public static class MdocRecordFun
private const string CredentialSetIdJsonKey = "credentialSetId";
private const string CredentialStateJsonKey = "credentialState";
private const string ExpiresAtJsonKey = "expiresAt";
private const string OneTimeUseJsonKey = "oneTimeUse";

public static MdocRecord DecodeFromJson(JObject json)
{
Expand Down Expand Up @@ -136,7 +141,12 @@ from expires in json.GetByKey(ExpiresAtJsonKey).ToOption()

var credentialState = Enum.Parse<CredentialState>(json[CredentialStateJsonKey]!.ToString());

var result = new MdocRecord(mdoc, displays, keyId, credentialSetId, credentialState, expiresAt)
var oneTimeUse = json.GetByKey(OneTimeUseJsonKey).ToOption().Match(
Some: value => value.ToObject<bool>(),
None: () => false
);

var result = new MdocRecord(mdoc, displays, keyId, credentialSetId, credentialState, expiresAt, oneTimeUse)
{
Id = id
};
Expand All @@ -152,7 +162,8 @@ public static JObject EncodeToJson(this MdocRecord record)
{ MdocJsonKey, record.Mdoc.Encode() },
{ KeyIdJsonKey, record.KeyId.ToString() },
{ CredentialSetIdJsonKey, record.CredentialSetId.ToString() },
{ CredentialStateJsonKey, record.CredentialState.ToString() }
{ CredentialStateJsonKey, record.CredentialState.ToString() },
{ OneTimeUseJsonKey, record.OneTimeUse }
};

record.ExpiresAt.IfSome(expires => result.Add(ExpiresAtJsonKey, expires));
Expand All @@ -171,6 +182,6 @@ public static JObject EncodeToJson(this MdocRecord record)
return result;
}

public static MdocRecord ToRecord(this Mdoc mdoc, Option<List<MdocDisplay>> displays, KeyId keyId, CredentialSetId credentialSetId) =>
new(mdoc, displays, keyId, credentialSetId, CredentialState.Active, Option<DateTime>.None);
public static MdocRecord ToRecord(this Mdoc mdoc, Option<List<MdocDisplay>> displays, KeyId keyId, CredentialSetId credentialSetId, bool isOneTimeUse) =>
new(mdoc, displays, keyId, credentialSetId, CredentialState.Active, Option<DateTime>.None, isOneTimeUse);
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace WalletFramework.Oid4Vc.Oid4Vci.CredRequest.Implementations;

public class CredentialRequestService : ICredentialRequestService
{
private const int MaxBatchSize = 10;
private const int MaxBatchSize = 20;

public CredentialRequestService(
HttpClient httpClient,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ await credential.Value.Match(
async sdJwt =>
{
var record = sdJwt.Decoded.ToRecord(configuration.AsT0, response.KeyId,
credentialSet.GetCredentialSetId());
credentialSet.GetCredentialSetId(), creds.Count > 1);
var context = await _agentProvider.GetContextAsync();
await _sdJwtService.AddAsync(context, record);
Expand All @@ -300,7 +300,7 @@ await credential.Value.Match(
{
var displays = MdocFun.CreateMdocDisplays(configuration.AsT1);
var record = mdoc.Decoded.ToRecord(displays, response.KeyId,
credentialSet.GetCredentialSetId());
credentialSet.GetCredentialSetId(), creds.Count > 1);
await _mdocStorage.Add(record);
credentialSet.AddMDocData(record);
Expand Down Expand Up @@ -394,7 +394,7 @@ await credential.Value.Match(
dPop => dPop with { Token = dPop.Token with { CNonce = cNonce.ToNullable() } });
var record = sdJwt.Decoded.ToRecord(configuration.AsT0, response.KeyId,
credentialSet.GetCredentialSetId());
credentialSet.GetCredentialSetId(), creds.Count > 1);
await _sdJwtService.AddAsync(context, record);
credentialSet.AddSdJwtData(record);
Expand All @@ -407,7 +407,7 @@ await credential.Value.Match(
var displays = MdocFun.CreateMdocDisplays(configuration.AsT1);
var record = mdoc.Decoded.ToRecord(displays, response.KeyId,
credentialSet.GetCredentialSetId());
credentialSet.GetCredentialSetId(), creds.Count > 1);
await _mdocStorage.Add(record);
credentialSet.AddMDocData(record);
Expand Down Expand Up @@ -499,7 +499,7 @@ select credentialsOrTransactionId.Match<OneOf<List<ICredential>, TransactionId>>
sdJwt =>
{
var record = sdJwt.Decoded.ToRecord(configuration.AsT0, response.KeyId,
credentialSetRecord.GetCredentialSetId());
credentialSetRecord.GetCredentialSetId(), creds.Count > 1);
credentialSetRecord.AddSdJwtData(record);
Expand Down Expand Up @@ -527,7 +527,7 @@ select credentialsOrTransactionId.Match<OneOf<List<ICredential>, TransactionId>>
{
var displays = MdocFun.CreateMdocDisplays(configuration.AsT1);
var record = mdoc.Decoded.ToRecord(displays, response.KeyId,
credentialSetRecord.GetCredentialSetId());
credentialSetRecord.GetCredentialSetId(), creds.Count > 1);
credentialSetRecord.AddMDocData(record);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ public static SdJwtRecord ToRecord(
this SdJwtDoc sdJwtDoc,
SdJwtConfiguration configuration,
KeyId keyId,
CredentialSetId credentialSetId)
CredentialSetId credentialSetId,
bool isOneTimeUse)
{
var claims = configuration
.Claims?
Expand Down Expand Up @@ -62,7 +63,8 @@ select displays.Select(credentialDisplay =>
claims!,
display.Fallback(new List<SdJwtDisplay>()),
keyId,
credentialSetId);
credentialSetId,
isOneTimeUse);

return record;
}
Expand Down
22 changes: 20 additions & 2 deletions src/WalletFramework.Oid4Vc/Oid4Vp/Services/Oid4VpClientService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using WalletFramework.MdocLib.Security;
using WalletFramework.MdocLib.Security.Cose;
using WalletFramework.MdocVc;
using WalletFramework.Oid4Vc.Oid4Vci.Abstractions;
using WalletFramework.Oid4Vc.Oid4Vci.AuthFlow.Abstractions;
using WalletFramework.Oid4Vc.Oid4Vci.AuthFlow.Models;
using WalletFramework.Oid4Vc.Oid4Vci.CredConfiguration.Models;
Expand Down Expand Up @@ -52,13 +53,15 @@ public class Oid4VpClientService : IOid4VpClientService
/// <param name="logger">The ILogger.</param>
/// <param name="authFlowSessionStorage">The Auth Flow Session Storage.</param>
/// <param name="oid4VpRecordService">The service responsible for OidPresentationRecord related operations.</param>
/// <param name="mDocStorage">The service responsible for mDOc storage operations.</param>
public Oid4VpClientService(
IAgentProvider agentProvider,
IHttpClientFactory httpClientFactory,
ILogger<Oid4VpClientService> logger,
IMdocAuthenticationService mdocAuthenticationService,
IOid4VpHaipClient oid4VpHaipClient,
IOid4VpRecordService oid4VpRecordService,
IMdocStorage mDocStorage,
IPexService pexService,
IAuthFlowSessionStorage authFlowSessionStorage,
ISdJwtVcHolderService sdJwtVcHolderService)
Expand All @@ -69,6 +72,7 @@ public Oid4VpClientService(
_mdocAuthenticationService = mdocAuthenticationService;
_oid4VpHaipClient = oid4VpHaipClient;
_oid4VpRecordService = oid4VpRecordService;
_mDocStorage = mDocStorage;
_pexService = pexService;
_authFlowSessionStorage = authFlowSessionStorage;
_sdJwtVcHolderService = sdJwtVcHolderService;
Expand All @@ -80,6 +84,7 @@ public Oid4VpClientService(
private readonly IMdocAuthenticationService _mdocAuthenticationService;
private readonly IOid4VpHaipClient _oid4VpHaipClient;
private readonly IOid4VpRecordService _oid4VpRecordService;
private readonly IMdocStorage _mDocStorage;
private readonly IPexService _pexService;
private readonly IAuthFlowSessionStorage _authFlowSessionStorage;
private readonly ISdJwtVcHolderService _sdJwtVcHolderService;
Expand Down Expand Up @@ -218,6 +223,21 @@ from path in field.Path.Select(path => path.TrimStart('$', '.'))
if (clientAttestation != null)
httpClient.AddClientAttestationPopHeader(clientAttestation);

// ToDo: when to delete these records?
var context = await _agentProvider.GetContextAsync();
foreach (var credential in credentials)
{
switch (credential.Credential)
{
case SdJwtRecord sdJwtRecord when sdJwtRecord.OneTimeUse:
await _sdJwtVcHolderService.DeleteAsync(context, sdJwtRecord.GetId());
break;
case MdocRecord mDocRecord when mDocRecord.OneTimeUse:
await _mDocStorage.Delete(mDocRecord);
break;
}
}

var responseMessage = await httpClient.SendAsync(message);
if (!responseMessage.IsSuccessStatusCode)
{
Expand Down Expand Up @@ -277,8 +297,6 @@ from claim in sdJwtRecord.Claims
return result;
});

var context = await _agentProvider.GetContextAsync();

await _oid4VpRecordService.StoreAsync(
context,
authorizationRequest.ClientId,
Expand Down
27 changes: 18 additions & 9 deletions src/WalletFramework.SdJwtVc/Models/Records/SdJwtRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,10 @@ public sealed class SdJwtRecord : RecordBase, ICredential
/// </summary>
public CredentialState CredentialState { get; set; }

//TODO: Must be set when batch issuance is implemented
// public bool OneTimeUse { get; set; }
/// <summary>
/// Tracks whether it's a one-time use SD-JWT.
/// </summary>
public bool OneTimeUse { get; set; }

/// <summary>
/// Tracks the Expiration Date of the Sd-JWT
Expand Down Expand Up @@ -138,12 +140,13 @@ public SdJwtRecord()
/// <param name="claims">The claims made.</param>
/// <param name="disclosures">The disclosures.</param>
/// <param name="display">The display of the credential.</param>
/// <param name="issuerId"></param>
/// <param name="issuerId">The Id of the issuer</param>
/// <param name="encodedIssuerSignedJwt">The Issuer-signed JWT part of the SD-JWT.</param>
/// <param name="credentialSetId">The CredentialSetId.</param>
/// <param name="expiresAt">The CredentialSetId.</param>
/// <param name="issuedAt">The CredentialSetId.</param>
/// <param name="notBefore">The CredentialSetId.</param>
/// <param name="expiresAt">The Expiration Date.</param>
/// <param name="issuedAt">The Issued at date.</param>
/// <param name="notBefore">The valid after date.</param>
/// <param name="isOneTimeUse">Indicator whether the credential should be sued only once.</param>
[JsonConstructor]
public SdJwtRecord(
Dictionary<string, ClaimMetadata> displayedAttributes,
Expand All @@ -155,7 +158,8 @@ public SdJwtRecord(
string credentialSetId,
DateTime? expiresAt,
DateTime? issuedAt,
DateTime? notBefore)
DateTime? notBefore,
bool isOneTimeUse = false)
{
Claims = claims;
Disclosures = disclosures;
Expand All @@ -170,14 +174,16 @@ public SdJwtRecord(
NotBefore = notBefore;
IssuerId = issuerId;
CredentialSetId = credentialSetId;
OneTimeUse = isOneTimeUse;
}

public SdJwtRecord(
string serializedSdJwtWithDisclosures,
Dictionary<string, ClaimMetadata> displayedAttributes,
List<SdJwtDisplay> display,
KeyId keyId,
CredentialSetId credentialSetId)
CredentialSetId credentialSetId,
bool isOneTimeUse = false)
{
Id = Guid.NewGuid().ToString();

Expand All @@ -190,6 +196,7 @@ public SdJwtRecord(

CredentialSetId = credentialSetId;
CredentialState = CredentialState.Active;
OneTimeUse = isOneTimeUse;

KeyId = keyId;
ExpiresAt = sdJwtDoc.UnsecuredPayload.SelectToken("exp")?.Value<long>() is not null
Expand All @@ -212,7 +219,8 @@ public SdJwtRecord(
Dictionary<string, ClaimMetadata> displayedAttributes,
List<SdJwtDisplay> display,
KeyId keyId,
CredentialSetId credentialSetId)
CredentialSetId credentialSetId,
bool isOneTimeUse = false)
{
Id = Guid.NewGuid().ToString();

Expand All @@ -224,6 +232,7 @@ public SdJwtRecord(

CredentialSetId = credentialSetId;
CredentialState = CredentialState.Active;
OneTimeUse = isOneTimeUse;

KeyId = keyId;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public Oid4VpClientServiceTests()
_mdocAuthenticationService.Object,
oid4VpHaipClient,
_oid4VpRecordService,
_mdocStorageMock.Object,
pexService,
_authFlowSessionStorageMock.Object,
_sdJwtVcHolderService);
Expand Down
Loading