Extend VCVerifier to support EBSI Trusted Issuers Registry (TIR) v5 API alongside existing v3/v4 support. The v5 API returns attribute references (URLs) instead of inline attributes, requiring multi-step fetching (get issuer, list attributes with pagination, fetch each attribute). Configuration is extended with a type parameter on TrustedIssuersLists (mirroring the existing TrustedParticipantsList pattern): type "ebsi" selects v3/v4 auto-detection (default, backward-compatible), type "ebsi-v5" selects the v5 flow.
Goal: Change TrustedIssuersLists from a plain []string (URL-only) to a structured type with Type + Url fields, mirroring the existing TrustedParticipantsList/TrustedParticipantsLists pattern. All existing configs must continue to work unchanged (backward compatibility).
Files to modify:
-
config/configClient.go:- Create
TrustedIssuersListstruct withType stringandUrl stringfields (JSON/mapstructure tags), analogous toTrustedParticipantsList(line 197). - Create
TrustedIssuersListstype (slice ofTrustedIssuersList) with a customUnmarshalJSONmethod: try structured format first, fall back to plain string array (each string becomes{Type: DEFAULT_LIST_TYPE, Url: url}). This mirrorsTrustedParticipantsLists.UnmarshalJSON(lines 204-232). - Update the
Credentialstruct fieldTrustedIssuersListsfrom[]stringtoTrustedIssuersLists(line 113).
- Create
-
verifier/credentialsConfig.go:- Update
CredentialsConfiginterface: changeGetTrustedIssuersListsreturn type from[]stringto[]config.TrustedIssuersList(line 51). - Update
cacheBasedCredentialsConfig.GetTrustedIssuersListsimplementation to return[]config.TrustedIssuersList(lines 287-299).
- Update
-
verifier/verifier.go:- Update
TrustRegistriesValidationContext.trustedIssuersListsfield type frommap[string][]stringtomap[string][]configModel.TrustedIssuersList(line 194). - Update
GetTrustedIssuersLists()return type accordingly (line 198). - Update
getTrustRegistriesValidationContextandgetTrustRegistriesValidationContextFromScopeto use the new type (lines 1125, 1139, 1142, 1189, 1217, 1220).
- Update
-
verifier/trustedissuer.go:- Update
ValidateVCto work with[]TrustedIssuersListinstead of[]string. For backward compatibility in this step, extract URLs from entries whereType == "ebsi"(or empty/default) and pass them to the existingtirClient.GetTrustedIssuer. TheisWildcardTilhelper must be updated to checkUrlfield instead of raw strings. - Update
isWildcardTilsignature from[]stringto[]config.TrustedIssuersList.
- Update
-
database/models.go:- Update
CredentialDB.VO()to constructconfig.TrustedIssuersList{Type: listType, Url: endpoint}entries instead of plain strings forTrustedIssuersendpoints (lines 286-287). Pass throughListTypefromEndpointEntry. - Update
CredentialDB.FromVO()to readTypeandUrlfromTrustedIssuersListentries when buildingEndpointEntryvalues (lines 315-320).
- Update
Tests to add/update:
config/configClient_test.go: Add tests forTrustedIssuersListscustomUnmarshalJSON: structured format, plain string array fallback, mixed validation.verifier/trustedissuer_test.go: UpdategetVerificationContext(),getWildcardVerificationContext(),getInvalidMixedVerificationContext(), andgetWildcardAndNormalVerificationContext()helpers to constructTrustedIssuersListvalues instead of raw strings.verifier/trustedparticipant_test.go: Update any test helpers that constructTrustRegistriesValidationContextwithtrustedIssuersLists.database/models_test.go: Update round-trip tests forCredentialDB.VO()/FromVO()to verifyTrustedIssuersListtype and URL are preserved.
Acceptance criteria:
- All existing YAML/JSON configs with
trustedIssuersLists: ["https://..."]continue to work (parsed as[{type: "ebsi", url: "https://..."}]). - New structured format
trustedIssuersLists: [{type: "ebsi-v5", url: "https://..."}]is accepted. - All existing tests pass (with updated test helpers).
go build ./...succeeds with no compilation errors.
Goal: Add v5 API support to the TIR HTTP client. The v5 API differs from v3/v4: GET /v5/issuers/{did} returns {did, attributes: "<url>", hasAttributes: true/false} (attributes is a URL reference, not inline). Fetching attributes requires: GET /v5/issuers/{did}/attributes (paginated list of attribute IDs/hrefs) then GET /v5/issuers/{did}/attributes/{id} (individual attribute with the same hash/body/issuerType/tao/rootTao fields as v3/v4). The final result must be assembled into the existing TrustedIssuer struct for downstream compatibility.
Files to modify:
tir/tirClient.go:- Add path constant
ISSUERS_V5_PATH = "v5/issuers". - Add v5-specific response structs:
TrustedIssuerV5Response—{Did string, Attributes string, HasAttributes bool}(theAttributesfield is a URL).AttributesListV5Response— paginated list:{Items []AttributeListItem, Links PaginationLinks, Total int, PageSize int, Self string}.AttributeListItem—{ID string, Href string}.PaginationLinks—{First, Last, Next, Prev string}.AttributeV5Response— single attribute:{Attribute IssuerAttribute, Did string}.
- Add
getIssuerV5Url(did string) stringhelper returningISSUERS_V5_PATH + "/" + did. - Add
getAttributesV5Url(did string) stringhelper returningISSUERS_V5_PATH + "/" + did + "/attributes". - Extend the
TirClientinterface with two new methods:IsTrustedParticipantV5(tirEndpoint string, did string) bool— callsGET /v5/issuers/{did}, returns true if 200.GetTrustedIssuerV5(tirEndpoints []string, did string) (bool, TrustedIssuer, error)— for each endpoint: (1)GET /v5/issuers/{did}, (2) ifhasAttributes, follow pagination on the attributes URL to collect all attribute IDs, (3) fetch each attribute individually, (4) assemble intoTrustedIssuer{Did, Attributes}. Cache the assembled result intirCache/tilCache.
- Implement pagination loop for
AttributesListV5Response: followLinks.Nextuntil empty or equal to current page. - Reuse existing
HttpGetClientinterface for all HTTP calls.
- Add path constant
Tests to add:
tir/tirClient_test.go:- Add table-driven tests for
IsTrustedParticipantV5: mock HTTP responses for 200 (found), 404 (not found), network error. - Add table-driven tests for
GetTrustedIssuerV5: mock multi-step flow (issuer response → attributes list → individual attributes), verify assembledTrustedIssuermatches expected. Include cases for: single attribute, multiple attributes, pagination (multiple pages), issuer withhasAttributes: false, issuer not found (404), attribute fetch error (partial failure). - Test caching behavior: second call for same DID should hit cache.
- Add table-driven tests for
Acceptance criteria:
TirClientinterface hasIsTrustedParticipantV5andGetTrustedIssuerV5methods.- V5 multi-step attribute fetching correctly assembles a
TrustedIssuerwith all attributes. - Pagination is handled (follows
nextlinks). - Results are cached using existing
tirCache/tilCache. - All new and existing tests pass.
Goal: Route "ebsi-v5" typed entries to the new v5 TIR client methods. The existing "ebsi" type continues to use v3/v4 auto-detection. The dispatch pattern follows the existing trustedparticipant.go model (type-based if checks).
Files to modify:
-
verifier/trustedparticipant.go:- Add constant
typeEbsiV5 = "ebsi-v5"alongside existingtypeGaiaXandtypeEbsi(line 17). - In
ValidateVC, add a new dispatch branch:if participantList.Type == typeEbsiV5callstpvs.tirClient.IsTrustedParticipantV5(participantList.Url, ...)(after line 57).
- Add constant
-
verifier/trustedissuer.go:- Add constant
typeEbsiV5 = "ebsi-v5"andtypeEbsi = "ebsi". - Refactor
ValidateVCto dispatch based onTrustedIssuersList.Type:- For entries with type
"ebsi"(or empty/default): collect URLs into a[]stringslice and calltpvs.tirClient.GetTrustedIssuer(urls, did)(preserving existing v3/v4 behavior). - For entries with type
"ebsi-v5": collect URLs into a[]stringslice and calltpvs.tirClient.GetTrustedIssuerV5(urls, did). - The wildcard check (
isWildcardTil) should apply across all entries regardless of type.
- For entries with type
- Ensure that if a credential type has a mix of "ebsi" and "ebsi-v5" entries, both are tried (ebsi entries via v3/v4, ebsi-v5 entries via v5), and the first successful match wins.
- Add constant
Tests to add/update:
-
verifier/trustedparticipant_test.go:- Add test cases for "ebsi-v5" type: participant found via v5, participant not found via v5, mixed ebsi + ebsi-v5 lists.
- Update mock
TirClientto implementIsTrustedParticipantV5.
-
verifier/trustedissuer_test.go:- Add test cases for "ebsi-v5" type: issuer found via v5, issuer not found via v5.
- Add test case for mixed types: one "ebsi" entry and one "ebsi-v5" entry for the same credential type.
- Update mock
TirClientto implementGetTrustedIssuerV5. - Update existing test helper functions to use
TrustedIssuersListstruct with explicitType: "ebsi".
Acceptance criteria:
- "ebsi-v5" type in
TrustedParticipantsListroutes toIsTrustedParticipantV5. - "ebsi-v5" type in
TrustedIssuersListsroutes toGetTrustedIssuerV5. - "ebsi" (and empty/default) type continues to use v3/v4 auto-detection.
- Mixed type lists work correctly.
- All tests pass.
Goal: Add integration-level test scenarios covering the complete flow from config parsing through TIR v5 verification. Update test fixture files and config examples. Ensure all tests pass and the build is clean.
Files to modify/add:
-
config/data/: Add or update YAML/JSON test fixtures:- A fixture with
trustedIssuersListsin the new structured format (type "ebsi-v5"). - A fixture with mixed old-format (string array) and new-format entries to verify backward compatibility.
- A fixture with
trustedParticipantsListsincluding "ebsi-v5" type entry.
- A fixture with
-
verifier/verifier_test.go:- Add or update integration-level tests that exercise the full
AuthenticationResponseorGenerateTokenflow with "ebsi-v5" configured trust registries (using mocked TIR client). - Verify that
getTrustRegistriesValidationContextcorrectly propagates type information from config through to the validation context.
- Add or update integration-level tests that exercise the full
-
database/models_test.go:- Add round-trip tests for
CredentialDBwith "ebsi-v5" typed trusted issuers lists:FromVO()→VO()preserves type and URL.
- Add round-trip tests for
-
config/configClient_test.go:- Add test for
ReadConfig()/ config parsing with the new structuredtrustedIssuersListsformat in YAML. - Verify that YAML
trustedIssuersLists: ["https://url"]still parses correctly (backward compat via mapstructure).
- Add test for
Verification steps:
- Run
go build ./...— must succeed with no errors. - Run
go vet ./...— must succeed with no warnings. - Run
go test ./... -v— all tests must pass. - Run
go test ./... -v -coverprofile=profile.covto verify coverage of new code paths.
Acceptance criteria:
- All new and existing tests pass.
- Config backward compatibility is verified by tests (old
[]stringformat, new structured format, database round-trip). - The full verification chain works end-to-end with "ebsi-v5" configuration.
- No compilation warnings or vet issues.