Skip to content

Commit 6131823

Browse files
committed
fix(consent): handle VCTMs without SVG templates in consent page
- SVGTemplateReply: guard against nil/empty SVG templates instead of panicking with index-out-of-range - UserLookup: when VCTM claims lack svg_id, fall back to extracting values from document data by claim path name (recursive search handles nested structures from credential mappings) - consent.js: make SVG template fetch optional — if it fails, display claims in table without the SVG card image - consent.html: conditionally hide <img> when credential.svg is null
1 parent e07629d commit 6131823

File tree

4 files changed

+90
-15
lines changed

4 files changed

+90
-15
lines changed

internal/apigw/apiv1/handlers_users.go

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -205,16 +205,24 @@ func (c *Client) UserLookup(ctx context.Context, req *vcclient.UserLookupRequest
205205
c.log.Debug("extracted claim values", "extracted_count", len(claimValues), "requested_count", len(jsonPaths.Displayable), "claims", claimValues)
206206

207207
for _, claim := range req.VCTM.Claims {
208-
value, ok := claimValues[claim.SVGID].(string)
209-
if !ok {
210-
continue
211-
}
212-
213208
if claim.SVGID != "" {
209+
value, ok := claimValues[claim.SVGID].(string)
210+
if !ok {
211+
continue
212+
}
214213
svgTemplateClaims[claim.SVGID] = vcclient.SVGClaim{
215214
Label: claim.Display[0].Label,
216215
Value: value,
217216
}
217+
} else if len(claim.Display) > 0 {
218+
// No svg_id — fall back to extracting claim value from document data by path
219+
key := claim.JSONPath()
220+
if value := findValueByName(doc.DocumentData, claim.Path); value != "" {
221+
svgTemplateClaims[key] = vcclient.SVGClaim{
222+
Label: claim.Display[0].Label,
223+
Value: value,
224+
}
225+
}
218226
}
219227
}
220228

@@ -265,16 +273,24 @@ func (c *Client) UserLookup(ctx context.Context, req *vcclient.UserLookupRequest
265273
"claims", claimValues)
266274

267275
for _, claim := range req.VCTM.Claims {
268-
value, ok := claimValues[claim.SVGID].(string)
269-
if !ok {
270-
continue
271-
}
272-
273276
if claim.SVGID != "" {
277+
value, ok := claimValues[claim.SVGID].(string)
278+
if !ok {
279+
continue
280+
}
274281
svgTemplateClaims[claim.SVGID] = vcclient.SVGClaim{
275282
Label: claim.Display[0].Label,
276283
Value: value,
277284
}
285+
} else if len(claim.Display) > 0 {
286+
// No svg_id — fall back to extracting claim value from document data by path
287+
key := claim.JSONPath()
288+
if value := findValueByName(doc.DocumentData, claim.Path); value != "" {
289+
svgTemplateClaims[key] = vcclient.SVGClaim{
290+
Label: claim.Display[0].Label,
291+
Value: value,
292+
}
293+
}
278294
}
279295
}
280296

@@ -298,3 +314,53 @@ func (c *Client) UserLookup(ctx context.Context, req *vcclient.UserLookupRequest
298314

299315
return reply, nil
300316
}
317+
318+
// findValueByName searches the document data for a claim value matching
319+
// the VCTM claim path. It first tries an exact JSONPath-style lookup,
320+
// then falls back to searching recursively by the leaf key name.
321+
func findValueByName(data map[string]any, path []*string) string {
322+
if len(path) == 0 {
323+
return ""
324+
}
325+
326+
// Walk the path through nested maps
327+
var current any = data
328+
for _, p := range path {
329+
if p == nil {
330+
break
331+
}
332+
m, ok := current.(map[string]any)
333+
if !ok {
334+
break
335+
}
336+
current, ok = m[*p]
337+
if !ok {
338+
// Exact path failed — try recursive search by leaf key name
339+
leafKey := *path[len(path)-1]
340+
return findValueRecursive(data, leafKey)
341+
}
342+
}
343+
344+
if s, ok := current.(string); ok {
345+
return s
346+
}
347+
return fmt.Sprintf("%v", current)
348+
}
349+
350+
// findValueRecursive searches a nested map for the first string value matching key.
351+
func findValueRecursive(data map[string]any, key string) string {
352+
if v, ok := data[key]; ok {
353+
if s, ok := v.(string); ok {
354+
return s
355+
}
356+
return fmt.Sprintf("%v", v)
357+
}
358+
for _, v := range data {
359+
if nested, ok := v.(map[string]any); ok {
360+
if result := findValueRecursive(nested, key); result != "" {
361+
return result
362+
}
363+
}
364+
}
365+
return ""
366+
}

internal/apigw/apiv1/handlers_vctm.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ type SVGTemplateRequest struct {
110110
}
111111

112112
func (c *Client) SVGTemplateReply(ctx context.Context, req *SVGTemplateRequest) (*vcclient.SVGTemplateReply, error) {
113+
if len(req.VCTM.Display) == 0 || req.VCTM.Display[0].Rendering == nil ||
114+
len(req.VCTM.Display[0].Rendering.SVGTemplates) == 0 {
115+
return nil, fmt.Errorf("VCTM has no SVG templates")
116+
}
117+
113118
svgTemplateURI := req.VCTM.Display[0].Rendering.SVGTemplates[0].URI
114119

115120
if cached, ok := c.cacheService.SVGTemplate.Get(ctx, svgTemplateURI); ok {

internal/apigw/staticembed/consent.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ <h1 class="text-3xl font-bold leading-tight tracking-tight text-gray-900 text-ce
134134
</div>
135135
<label :for="`credential:${index}`" class="vc-radio-label radio block cursor-pointer" :class="{ 'outline-3 outline-double outline-primary-light bg-primary-light bg-opacity-10': checked }">
136136
<div class="block">
137-
<img class="vc-credential-card mb-2" width="829" height="504" :src="credential.svg" alt="">
137+
<img x-show="credential.svg" class="vc-credential-card mb-2" width="829" height="504" :src="credential.svg" alt="">
138138
<table class="table-auto w-full">
139139
<tbody>
140140
<template x-for="[svg_id, claim] of Object.entries(credential.claims)">

internal/apigw/staticembed/consent.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -326,10 +326,14 @@ Alpine.data("app", () => ({
326326

327327
this.redirectUrl = data.redirect_url;
328328

329-
const svg = await this.createCredentialSvgImageUri(
330-
data.svg_template_claims,
331-
);
332-
329+
let svg = null;
330+
try {
331+
svg = await this.createCredentialSvgImageUri(
332+
data.svg_template_claims,
333+
);
334+
} catch (_) {
335+
// VCTM has no SVG template — display claims without card image
336+
}
333337

334338
this.credentials.push({
335339
vct: "N/A",

0 commit comments

Comments
 (0)