Version: observed on 0.99.0; re-checked against main @ 145e9e1a — unchanged.
What happens
getClaimsImpl iterates the entire verified SD-JWT payload and emits a JsonClaim for every top-level entry:
|
// By design, we only include the top-level claims. |
|
val dt = documentTypeRepository?.getDocumentTypeForJson(vct) |
|
for ((claimName, claimValue) in processedJwt) { |
|
val attribute = dt?.jsonDocumentType?.claims?.get(claimName) |
|
ret.add( |
|
JsonClaim( |
|
displayName = dt?.jsonDocumentType?.claims?.get(claimName)?.displayName ?: claimName, |
|
attribute = attribute, |
|
vct = vct, |
|
claimPath = buildJsonArray { add(claimName) }, |
|
value = claimValue |
|
) |
|
) |
|
} |
The comment above the loop reads "By design, we only include the top-level claims" — which is precisely the problem: the JWT's own registered claims are top-level too. So a PID from the EU reference issuer comes back as
iss, iat, exp, vct, cnf, _sd_alg, family_name, given_name, birth_date, …
On first run, our document detail screen showed vct, iss and cnf as if they were attributes of the person — cnf being the holder key confirmation, i.e. a JSON object containing a public key rendered as a user-facing value.
Why it matters
Every consumer of this API has to know which JWT registered names to filter, and get that list right, or leak protocol internals into the UI. It is also inconsistent with the mdoc path, where the namespace structure keeps envelope data out of the claim set for free.
Suggested fix
Filter the SD-JWT VC envelope claims out of the returned list — at minimum iss, sub, aud, exp, nbf, iat, jti, cnf, vct, vct#integrity, status, _sd, _sd_alg (SD-JWT VC, draft/RFC registered claims). They remain available through the typed accessors on the credential (vct, validity, status) where a caller genuinely needs them.
Related
Workaround we ship
Our document reader filters the envelope names before displaying claims. The filter is load-bearing, not tidiness.
Version: observed on 0.99.0; re-checked against
main@145e9e1a— unchanged.What happens
getClaimsImpliterates the entire verified SD-JWT payload and emits aJsonClaimfor every top-level entry:multipaz/multipaz/src/commonMain/kotlin/org/multipaz/sdjwt/credential/SdJwtVcCredential.kt
Lines 50 to 63 in 145e9e1
The comment above the loop reads "By design, we only include the top-level claims" — which is precisely the problem: the JWT's own registered claims are top-level too. So a PID from the EU reference issuer comes back as
On first run, our document detail screen showed
vct,issandcnfas if they were attributes of the person —cnfbeing the holder key confirmation, i.e. a JSON object containing a public key rendered as a user-facing value.Why it matters
Every consumer of this API has to know which JWT registered names to filter, and get that list right, or leak protocol internals into the UI. It is also inconsistent with the mdoc path, where the namespace structure keeps envelope data out of the claim set for free.
Suggested fix
Filter the SD-JWT VC envelope claims out of the returned list — at minimum
iss,sub,aud,exp,nbf,iat,jti,cnf,vct,vct#integrity,status,_sd,_sd_alg(SD-JWT VC, draft/RFC registered claims). They remain available through the typed accessors on the credential (vct, validity, status) where a caller genuinely needs them.Related
x5c— and is fixed onmain(it now throws a typedIllegalStateException). This is a separate issue in the same loop.Workaround we ship
Our document reader filters the envelope names before displaying claims. The filter is load-bearing, not tidiness.