Skip to content
53 changes: 48 additions & 5 deletions src/WalletFramework.Core/ClaimPaths/ClaimPath.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using LanguageExt;
using WalletFramework.Core.ClaimPaths.Errors;
using WalletFramework.Core.Functional;
using WalletFramework.Core.Path;
Expand Down Expand Up @@ -31,18 +32,60 @@ from components in array.TraverseAll(ClaimPathComponent.Create)
from path in FromComponents(components)
select path;
}

public static JArray ToArray(ClaimPath claimPath)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ToJArray

{
var array = new JArray();
foreach (var component in claimPath.GetPathComponents())
{
component.Match(
key =>
{
array.Add(new JValue(key));
return Unit.Default;
},
index =>
{
array.Add(new JValue(index));
return Unit.Default;
},
selectAll =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value selectAll is never used

{
array.Add(JValue.CreateNull());
return Unit.Default;
}
);
}
return array;
}
}

public static class ClaimPathFun
{
public static JsonPath ToJsonPath(this ClaimPath claimPath)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use a StringBuilder

{
var jsonPath = "$." + string.Join('.', claimPath.GetPathComponents().Select(x =>
var jsonPath = "$";

foreach (var component in claimPath.GetPathComponents())
{
if (x.IsKey) return x.AsKey();
if (x.IsIndex) return x.AsIndex()?.ToString();
return null;
}).Where(x => x is not null));
component.Match(
key =>
{
jsonPath += $".{key}";
return Unit.Default;
},
integer =>
{
jsonPath += $"[{integer}]";
return Unit.Default;
},
selectAll =>
{
jsonPath += "[*]";
return Unit.Default;
});
}

return JsonPath.ValidJsonPath(jsonPath).UnwrapOrThrow();
}
}
2 changes: 2 additions & 0 deletions src/WalletFramework.Core/Credentials/CredentialSetId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ private CredentialSetId(string value)
}

public string AsString() => Value;

public Guid AsGuid() => Guid.Parse(Value);

public override string ToString() => Value;

Expand Down
2 changes: 0 additions & 2 deletions src/WalletFramework.MdocVc/MdocCredential.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@
using WalletFramework.Core.Credentials.Abstractions;
using WalletFramework.Core.Cryptography.Models;
using WalletFramework.MdocLib;
using WalletFramework.MdocVc.Display;

namespace WalletFramework.MdocVc;

public record MdocCredential(
Mdoc Mdoc,
CredentialId CredentialId,
CredentialSetId CredentialSetId,
Option<List<MdocDisplay>> Displays,
KeyId KeyId,
CredentialState CredentialState,
bool OneTimeUse,
Expand Down
4 changes: 1 addition & 3 deletions src/WalletFramework.MdocVc/MdocCredentialExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,21 @@
using WalletFramework.Core.Credentials;
using WalletFramework.Core.Cryptography.Models;
using WalletFramework.MdocLib;
using WalletFramework.MdocVc.Display;

namespace WalletFramework.MdocVc;

public static class MdocCredentialExtensions
{
public static MdocCredential ToMdocCredential(
this Mdoc mdoc,
Option<List<MdocDisplay>> displays,
KeyId keyId,
CredentialSetId credentialSetId,
CredentialState credentialState,
bool oneTimeUse,
Option<DateTime> expiresAt,
CredentialId credentialId)
{
return new MdocCredential(mdoc, credentialId, credentialSetId, displays, keyId, credentialState, oneTimeUse, expiresAt);
return new MdocCredential(mdoc, credentialId, credentialSetId, keyId, credentialState, oneTimeUse, expiresAt);
}

public static string ToJsonString(this MdocCredential mdocCredential)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ namespace WalletFramework.MdocVc.Persistence;
public sealed record MdocCredentialRecord : RecordBase
{
public string DocType { get; init; }

public string CredentialSetId { get; init; }

public string Serialized { get; init; }

Expand All @@ -25,6 +27,7 @@ public MdocCredentialRecord(MdocCredential mdoc) : base(Guid.Parse(mdoc.Credenti
{
DocType = mdoc.Mdoc.DocType.AsString();
Serialized = MdocCredentialSerializer.Serialize(mdoc);
CredentialSetId = mdoc.CredentialSetId.AsString();
}

public MdocCredential ToDomainModel()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public Unit Configure(ModelBuilder modelBuilder)
{
var entity = modelBuilder.Entity<MdocCredentialRecord>();
entity.HasIndex(r => r.DocType);
entity.HasIndex(r => r.CredentialSetId);

return Unit.Default;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,6 @@ private static JObject EncodeToJObject(this MdocCredential credential)

credential.ExpiresAt.IfSome(expires => result.Add(ExpiresAtJsonKey, expires));

credential.Displays.IfSome(displays =>
{
var array = new JArray(displays.Select(MdocDisplaySerializer.Serialize));
result.Add(DisplaysJsonKey, array);
});

return result;
}

Expand Down Expand Up @@ -73,17 +67,10 @@ private static MdocCredential DecodeFromJObject(JObject json)
from ex in json.GetByKey(ExpiresAtJsonKey).ToOption()
select ex.ToObject<DateTime>();

var displays =
from token in json.GetByKey(DisplaysJsonKey).ToOption()
from array in token.ToJArray().ToOption()
from list in array.TraverseAll(t => MdocDisplaySerializer.Deserialize(t.ToString()).ToOption())
select list.ToList();

return new MdocCredential(
mdoc,
credentialId,
credentialSetId,
displays,
keyId,
credentialState,
oneTimeUse,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using LanguageExt;
using Newtonsoft.Json.Linq;
using WalletFramework.Core.Functional;
using WalletFramework.Core.Json;
using WalletFramework.Core.Localization;
using static WalletFramework.Core.Localization.Locale;
using static WalletFramework.Oid4Vc.Oid4Vci.CredConfiguration.Models.ClaimDisplayJsonExtensions;

namespace WalletFramework.Oid4Vc.Oid4Vci.CredConfiguration.Models;

public record ClaimDisplay
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete the other legacy ClaimDisplays

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to have a name without a locale ? Otherwise, a name must be mapped by a locale and this is not reflected here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, according to the specs a name can exist without a locale. https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#appendix-B.2-2.3.1

{
public Option<string> Name { get; set; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove public setter


public Option<Locale> Locale { get; set; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove public setter


private ClaimDisplay(Option<string> name, Option<Locale> locale)
{
Name = name;
Locale = locale;
}

private static ClaimDisplay Create(Option<string> name, Option<Locale> locale) =>
new(name, locale);

public static Validation<ClaimDisplay> ValidClaimDisplay(JToken config)
{
var name =
from jToken in config.GetByKey(NameJsonKey)
select jToken.ToObject<string>();

var locale =
from jToken in config.GetByKey(LocaleJsonKey)
from validLocale in ValidLocale(jToken)
select validLocale;

return ValidationFun.Valid(Create)
.Apply(name.ToOption())
.Apply(locale.ToOption());
}
}

public static class ClaimDisplayJsonExtensions
{
public const string NameJsonKey = "name";
public const string LocaleJsonKey = "locale";

public static JObject EncodeToJson(this ClaimDisplay display)
{
var result = new JObject();

display.Locale.IfSome(locale => result.Add(LocaleJsonKey, locale.ToString()));
display.Name.IfSome(name => result.Add(NameJsonKey, name));

return result;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using LanguageExt;
using Newtonsoft.Json.Linq;
using WalletFramework.Core.ClaimPaths;
using WalletFramework.Core.Functional;
using WalletFramework.Core.Json;
using static WalletFramework.Oid4Vc.Oid4Vci.CredConfiguration.Models.ClaimMetadataJsonExtensions;

namespace WalletFramework.Oid4Vc.Oid4Vci.CredConfiguration.Models;

public record ClaimMetadata
{
public ClaimPath Path { get; }

public Option<List<ClaimDisplay>> Display { get; }

public Option<bool> Mandatory { get; }

private ClaimMetadata(ClaimPath claimPath, Option<List<ClaimDisplay>> claimDisplays, Option<bool> mandatory)
{
Path = claimPath;
Display = claimDisplays;
Mandatory = mandatory;
}

private static ClaimMetadata Create(ClaimPath claimPath, Option<List<ClaimDisplay>> claimDisplays, Option<bool> mandatory) =>
new(claimPath, claimDisplays, mandatory);

public static Validation<ClaimMetadata> ValidClaimMetadata(JToken config, Func<JToken, Validation<ClaimPath>> claimPathValidation)
{
var claimPath =
from jToken in config.GetByKey(PathJsonKey)
from jArray in claimPathValidation(jToken)
select jArray;

var claimDisplays =
from jToken in config.GetByKey(DisplayJsonKey)
from jArray in jToken.ToJArray()
from claimDisplay in jArray.TraverseAny(ClaimDisplay.ValidClaimDisplay)
select claimDisplay.ToList();

var mandatory =
from jToken in config.GetByKey(MandatoryJsonKey)
select jToken.ToObject<bool>();

var result = ValidationFun.Valid(Create)
.Apply(claimPath)
.Apply(claimDisplays.ToOption())
.Apply(mandatory.ToOption());

return result;
}
}

public static class ClaimMetadataJsonExtensions
{
public const string PathJsonKey = "path";
public const string DisplayJsonKey = "display";
public const string MandatoryJsonKey = "mandatory";

public static JObject EncodeToJson(this ClaimMetadata display)
{
var result = new JObject();

result.Add(PathJsonKey, ClaimPath.ToArray(display.Path));

display.Display.IfSome(claimDisplays =>
{
var claimsArray = new JArray();
foreach (var claimDisplay in claimDisplays)
{
claimsArray.Add(claimDisplay.EncodeToJson());
}
result.Add(DisplayJsonKey, claimsArray);
});

display.Mandatory.IfSome(mandatory => { result.Add(MandatoryJsonKey, mandatory); });

return result;
}
}
Loading