From c788e0aa35a6d1d520bc9d907aaa49b127cdf168 Mon Sep 17 00:00:00 2001 From: Stefan Wiedemann Date: Fri, 6 Mar 2026 13:58:34 +0100 Subject: [PATCH 1/3] provide id token --- CLAUDE.md | 144 +++++++++++++++++++ IMPLEMENTATION_PLAN.md | 304 +++++++++++++++++++++++++++++++++++++++++ SUMMARY.md | 50 +++++++ openapi/api_api.go | 6 +- 4 files changed, 501 insertions(+), 3 deletions(-) create mode 100644 CLAUDE.md create mode 100644 IMPLEMENTATION_PLAN.md create mode 100644 SUMMARY.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..c39a4f5 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,144 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Commands + +```bash +# Run all tests +go test ./... -v + +# Run tests with coverage +go test ./... -v -coverprofile=profile.cov ./... + +# Run tests for a specific package +go test ./verifier/... -v +go test ./openapi/... -v + +# Run a single test +go test ./verifier/... -v -run TestFunctionName + +# Build the binary +go build -o VCVerifier . + +# Build Docker image +docker build -t vcverifier . + +# Run locally (requires server.yaml config) +./VCVerifier +# or with alternate config: +CONFIG_FILE=path/to/config.yaml ./VCVerifier +``` + +## Architecture + +VCVerifier implements SIOP-2/OIDC4VP authentication flows. It acts as a Relying Party that receives Verifiable Presentations from wallets, verifies the credentials inside them, and issues signed JWTs for downstream use. + +### Package structure + +- **`main.go`**: Entry point. Reads config, initializes the verifier singleton and presentation parser, sets up the Gin HTTP server with routes from `openapi/routers.go`. +- **`openapi/`**: HTTP layer (generated from OpenAPI spec). + - `routers.go`: Route definitions. + - `api_api.go`: REST API handlers (token exchange, authentication response, JWKS, SIOP flows). + - `api_frontend.go`: UI handlers (QR login page). + - `websocket.go`: WebSocket support for cross-device flows. +- **`verifier/`**: Core business logic. The `Verifier` interface (implemented by `CredentialVerifier`) is the central abstraction. + - `verifier.go`: Orchestrates the full flow — session management, QR/connection string generation, JWT issuance. + - `presentation_parser.go`: Parses VP tokens (both JSON-LD and SD-JWT formats) using trustbloc libraries. + - `trustedparticipant.go`: Validates credential issuer against Trusted Participants Lists (EBSI TIR or Gaia-X Registry). + - `trustedissuer.go`: Validates issuer claims against Trusted Issuers Lists (EBSI TIR). + - `compliance.go`: Validates Gaia-X compliance credentials via SHA-256 signature checking. + - `holder.go`: Optional holder binding verification. + - `jwt_verifier.go`: JWT signature verification. + - `key_resolver.go`: DID key resolution (did:key, did:web, did:jwk). + - `elsi_proof_checker.go`: JAdES proof checking for did:elsi. + - `gaiax.go`: Gaia-X-specific credential validation logic. + - `credentialsConfig.go`: Retrieves per-service credential/scope configuration. + - `caching_client.go`: HTTP client with caching for JSON-LD document loading. + - `request_object_client.go`: Stores/retrieves signed request objects for `byReference` mode. +- **`config/`**: Configuration model (`config.go`) and loading (`provider.go`). The `configClient.go` fetches service config from an external Credentials-Config-Service or falls back to static YAML. +- **`tir/`**: EBSI Trusted Issuers Registry client. `tirClient.go` checks participation/issuance rights. `tokenProvider.go` handles M2M OAuth tokens for TIR access. +- **`gaiax/`**: Gaia-X Digital Clearing House registry client (`gaiaXClient.go`, `registry.go`). +- **`jades/`**: JAdES signature validation for did:elsi support. +- **`common/`**: Shared utilities — `cache.go` (in-memory cache wrapper), `clock.go`, `httpUtils.go`, `tokenSigner.go`, `metadata.go` (OpenID provider metadata). +- **`logging/`**: Structured logging setup (zap-based) and Gin middleware. +- **`views/`**: HTML templates and static assets for the QR login page (`verifier_present_qr.html`). +- **`api/`**: OpenAPI spec (`api.yaml`) and supporting YAML specs. + +### Key flows + +**Cross-device flow** (wallet on separate device): +1. Frontend calls `/api/v1/loginQR` → verifier creates session, generates QR code containing `openid4vp://` URI +2. User scans QR with wallet → wallet POSTs VP to `/api/v1/authentication_response` +3. Verifier validates credentials (participant lists → issuer lists → optional compliance/holder checks) +4. Frontend polls via WebSocket or receives callback → exchanges authorization code for JWT at `/token` + +**Same-device flow**: +1. Call `/api/v1/samedevice` → redirected to wallet with `openid4vp://` URI +2. Wallet POSTs to `/api/v1/authentication_response` +3. Response redirects back with `code` → exchange at `/token` + +### Request modes + +Three modes for the `openid4vp://` URI (configurable per request via `requestMode` param): +- `urlEncoded`: Parameters directly in URL +- `byValue`: Signed JWT request object embedded in URL +- `byReference`: Signed JWT served from `/api/v1/request/:id` + +`byValue` and `byReference` require `verifier.clientIdentification` to be configured with a signing key. + +### Configuration + +Config file at `./server.yaml` (or `CONFIG_FILE` env var). Key sections: +- `verifier.did`: The verifier's DID +- `verifier.clientIdentification`: Key/cert for signing request objects +- `verifier.keyAlgorithm` / `verifier.generateKey`: JWT signing key setup +- `configRepo`: Either a `configEndpoint` (external Credentials-Config-Service) or static `services` list defining per-service credential trust and presentation definitions + +### Singletons + +`verifier.InitVerifier()` and `verifier.InitPresentationParser()` set package-level singletons accessed by the HTTP handlers. Tests must call these or set the singletons directly. + +### Testing patterns + +Tests use `github.com/stretchr/testify/assert` and table-driven test cases. Mock implementations of interfaces (e.g. `Verifier`, `TirClient`, `GaiaXClient`) are defined inline in test files. The `common.Cache` and `common.Clock` interfaces exist specifically to enable time-controlled testing. + +## Gitea workflow + +The repository has a local Gitea instance for code review. Use this for all branch pushes and pull requests. + +- **Gitea URL**: `http://localhost:3000` +- **Repo**: `wistefan/verifier` +- **Credentials**: user `claude`, password `password` +- **Remote name**: `gitea` (configured with `http://claude:password@localhost:3000/wistefan/verifier.git`) +- **Base branch for PRs**: `trustbloc` + +### Creating a step branch and PR + +```bash +# Create branch from trustbloc +git checkout trustbloc +git checkout -b step-N-description + +# ... make changes, commit ... + +# Push to gitea +git push -u gitea step-N-description + +# Create PR via Gitea API +curl -s -u claude:password -X POST \ + http://localhost:3000/api/v1/repos/wistefan/verifier/pulls \ + -H 'Content-Type: application/json' \ + -d '{ + "title": "Step N: Description", + "body": "...", + "head": "step-N-description", + "base": "trustbloc" + }' +``` + +After a PR is merged, update the local `trustbloc` branch before creating the next step branch: +```bash +git checkout trustbloc +git pull gitea trustbloc +``` diff --git a/IMPLEMENTATION_PLAN.md b/IMPLEMENTATION_PLAN.md new file mode 100644 index 0000000..9d8a328 --- /dev/null +++ b/IMPLEMENTATION_PLAN.md @@ -0,0 +1,304 @@ +# Plan: Replace trustbloc libraries with custom implementations + +## Context + +The VCVerifier project depends on three trustbloc libraries (`did-go`, `vc-go`, `kms-go`) for DID resolution, Verifiable Credential/Presentation handling, proof verification, and SD-JWT parsing. These libraries are no longer well maintained and need to be replaced with custom, in-project implementations. The replacement must be transparent to the rest of the codebase — the existing interfaces and data flow should remain unchanged wherever possible. + +**Approach**: All replacements will be custom implementations (no new third-party DID or SD-JWT libraries). The M2M token provider (`tir/tokenProvider.go`) LD-proof creation will be the last step. + +## Pre-work: Improve test coverage on trustbloc-dependent code + +Before any replacement work begins, add tests for currently untested trustbloc-dependent code paths. This ensures we have a safety net for verifying functional equivalence after replacement. + +### Step 0: Add missing tests + +**0a.** `verifier/key_resolver.go` — `VdrKeyResolver.ResolvePublicKeyFromDID()` +- Zero test coverage today. Add tests with mocked VDR implementations: + - Successful resolution with `kid` containing `#` fragment + - Successful resolution with bare `did:key:...` (no fragment) + - VDR resolution failure (all VDRs error) + - Key ID not found in verification methods + - JWK serialization error +- Create a mock VDR type implementing the `api.VDR` interface from `did-go/vdr/api` for test isolation. + +**0b.** `verifier/jwt_verifier.go` — `TrustBlocValidator.ValidateVC()` +- Add tests for all validation modes: `"none"`, `"combined"`, `"jsonLd"`, `"baseContext"` +- Test error return paths when `ValidateCredential()` fails + +**0c.** `verifier/presentation_parser.go` — `ConfigurableSdJwtParser.ParseWithSdJwt()` +- Test malformed base64 payload +- Test missing `vp` claim in JWT payload +- Test missing `verifiableCredential` in vp +- Test missing `holder` field +- Test proof check failure path + +**0d.** `verifier/presentation_parser.go` — `ConfigurableSdJwtParser.ClaimsToCredential()` +- Test missing `iss` claim +- Test missing `vct` claim +- Test successful conversion with extra custom fields + +**0e.** `verifier/trustedissuer.go` — `parseAttribute()` / `parseAttributes()` +- Test base64 decode errors +- Test JSON unmarshal errors +- Test empty attributes array + +Files to modify: +- `verifier/key_resolver_test.go` (new or extend) +- `verifier/jwt_verifier_test.go` (extend) +- `verifier/presentation_parser_test.go` (extend) +- `verifier/trustedissuer_test.go` (extend) + +Verification: `go test ./verifier/... -v` — all existing + new tests pass. + +--- + +## Step 1: Introduce local credential/presentation types (`common/credential.go`) + +Create project-local types that mirror the subset of trustbloc `verifiable` types actually used. These become the new domain types that the rest of the codebase will operate on. + +**New file**: `common/credential.go` + +Types to define: +```go +type Issuer struct { ID string } +type Subject struct { ID string; CustomFields map[string]interface{} } +type CustomFields map[string]interface{} +type JSONObject = map[string]interface{} + +type CredentialContents struct { + ID string + Types []string + Issuer *Issuer + Subject []Subject +} + +type Credential struct { /* internal fields for contents, rawJSON, customFields */ } +// Methods: Contents(), ToRawJSON(), MarshalJSON(), ValidateCredential(opts...) + +type Presentation struct { + ID string + Holder string + /* internal credentials slice */ +} +// Methods: Credentials(), AddCredentials(), MarshalJSON() + +func CreateCredential(contents CredentialContents, customFields CustomFields) (*Credential, error) +func NewPresentation(opts ...PresentationOpt) (*Presentation, error) +``` + +At this point, **no production code changes**. Just define the types and write unit tests for them. + +Files to create: `common/credential.go`, `common/credential_test.go` + +Verification: `go test ./common/... -v` + +--- + +## Step 2: Custom DID resolution (`did/` package) + +Create a new `did/` package with resolvers for `did:key`, `did:web`, and `did:jwk`. + +**New files**: +- `did/resolver.go` — `Registry` interface + multi-method registry +- `did/document.go` — `Document`, `VerificationMethod`, `DocResolution` types +- `did/did_key.go` — `did:key` resolver (multicodec decode → JWK) +- `did/did_web.go` — `did:web` resolver (HTTP fetch → JSON parse) +- `did/did_jwk.go` — `did:jwk` resolver (base64url decode → JWK) +- `did/resolver_test.go`, `did/did_key_test.go`, `did/did_web_test.go`, `did/did_jwk_test.go` + +Key interfaces: +```go +type VDR interface { + Accept(method string) bool + Read(did string, opts ...ResolveOption) (*DocResolution, error) +} + +type Registry interface { + Resolve(did string, opts ...ResolveOption) (*DocResolution, error) +} + +type VerificationMethod struct { + ID string + Type string + Controller string + Value []byte + // JSONWebKey() returns *jose.JSONWebKey +} +``` + +The `VerificationMethod.JSONWebKey()` method must return a `go-jose` compatible JWK (or our own type that can be serialized to JSON and parsed by `lestrrat-go/jwx`). + +Verification: `go test ./did/... -v` with real did:key/did:jwk test vectors and mocked HTTP for did:web. + +--- + +## Step 3: Replace DID resolution in `verifier/jwt_verifier.go` + +Switch `JWTVerfificationMethodResolver.ResolveVerificationMethod()` from trustbloc VDR to our custom `did/` package. + +**Before** (current): +```go +import "github.com/trustbloc/did-go/method/web" +import "github.com/trustbloc/did-go/method/key" +import "github.com/trustbloc/did-go/method/jwk" +import "github.com/trustbloc/did-go/vdr" +import "github.com/trustbloc/vc-go/vermethod" + +registry := vdr.New(vdr.WithVDR(web.New()), vdr.WithVDR(key.New()), vdr.WithVDR(jwk.New())) +didDocument, err := registry.Resolve(expectedProofIssuer) +``` + +**After**: Use `did.NewRegistry(did.WithVDR(...))` from step 2. + +Also replace the `vermethod.VerificationMethod` return type. Define a local proof-checking compatible type. + +Files to modify: `verifier/jwt_verifier.go` +Verification: All tests in `verifier/jwt_verifier_test.go` pass + new integration-style tests with the custom resolver. + +--- + +## Step 4: Replace DID resolution in `verifier/key_resolver.go` + +Switch `VdrKeyResolver` from `trustbloc/did-go/vdr/api.VDR` to our custom `did.VDR`. + +**Before**: `Vdr []api.VDR` field using trustbloc's `did.DocResolution`, `did.VerificationMethod` +**After**: `Vdr []did.VDR` using our `did.DocResolution`, `did.VerificationMethod` + +Files to modify: `verifier/key_resolver.go`, `verifier/key_resolver_test.go` +Also update: `verifier/request_object_client.go` (uses same VDR pattern), `openapi/api_api.go` (constructs `VdrKeyResolver`) +Verification: `go test ./verifier/... ./openapi/... -v` + +--- + +## Step 5: Replace DID resolution in `gaiax/gaiaXClient.go` + +Switch from trustbloc VDR to custom `did/` package. + +Files to modify: `gaiax/gaiaXClient.go`, `gaiax/gaiaXClient_test.go` +Verification: `go test ./gaiax/... -v` + +--- + +## Step 6: Migrate production code to local credential/presentation types + +This is the largest step. Replace `trustbloc/vc-go/verifiable` types with `common.Credential`, `common.Presentation`, etc. across all production files. + +Migrate in this order (each sub-step compilable): + +**6a.** `verifier/presentation_parser.go` — Change `PresentationParser` and `SdJwtParser` interfaces to return `*common.Presentation` / `*common.Credential`. Update `ConfigurablePresentationParser` and `ConfigurableSdJwtParser`. Internally still call trustbloc parsing and convert to local types. + +**6b.** `verifier/verifier.go` — Change `Verifier` interface and `CredentialVerifier` to use `*common.Credential`, `*common.Presentation`. Update `ValidationService` interface, `ValidationContext`, `extractCredentialTypes`, `buildInclusion`, `AuthenticationResponse`, `GenerateToken`. + +**6c.** `verifier/holder.go`, `verifier/trustedissuer.go`, `verifier/trustedparticipant.go`, `verifier/compliance.go`, `verifier/gaiax.go` — Update `ValidateVC` methods to accept `*common.Credential`. + +**6d.** `openapi/api_api.go`, `openapi/api_frontend.go` — Update handler code to use local types. + +**6e.** Update all test files to use local types for credential/presentation creation. + +At this point, the codebase uses local types everywhere but `presentation_parser.go` still internally calls trustbloc for parsing/proof-checking, and `jwt_verifier.go`'s `TrustBlocValidator` still calls trustbloc for content validation. + +Files to modify: All files listed above + their test files. +Verification: `go test ./... -v` — full suite passes. + +--- + +## Step 7: Custom VP/VC parsing (replace `verifiable.ParsePresentation`) + +Implement JSON-LD VP parsing in the local types. A VP is a JWT or JSON object containing: +- `type`, `holder`, `verifiableCredential` (array of VCs — each a JWT string or JSON object) +- Proof (JWT signature or LD-proof) + +The existing `ConfigurablePresentationParser.ParsePresentation()` currently delegates to `verifiable.ParsePresentation()`. Replace with custom logic: +1. Detect JWT vs JSON-LD format +2. For JWT: decode header+payload, verify signature using our DID resolver + `lestrrat-go/jwx` +3. Extract embedded credentials +4. For each VC: verify its JWT signature similarly +5. Return `*common.Presentation` + +Files to modify: `verifier/presentation_parser.go` +New file: `common/vp_parser.go` (or in `common/credential.go`) +Verification: `go test ./verifier/... -v` + +--- + +## Step 8: Custom SD-JWT verification (replace `vc-go/sdjwt/verifier`) + +Implement SD-JWT parsing per the SD-JWT specification: +1. Split combined format by `~` separator +2. Verify issuer JWT signature (using lestrrat-go/jwx + our DID resolver) +3. Decode each disclosure (base64url → JSON array `[salt, claim_name, claim_value]`) +4. Reconstruct full claims map from `_sd` digests + disclosures +5. Optionally verify key binding JWT + +New file: `common/sdjwt.go`, `common/sdjwt_test.go` +Files to modify: `verifier/presentation_parser.go` — replace `sdv.Parse()` call with custom implementation. +Verification: `go test ./common/... ./verifier/... -v` + +--- + +## Step 9: Custom credential content validation (replace `TrustBlocValidator`) + +Replace `verifiable.ValidateCredential()` calls in `jwt_verifier.go`. The validation modes are: +- `"none"`: no-op +- `"combined"`: basic JSON schema + JSON-LD validation +- `"jsonLd"`: JSON-LD only +- `"baseContext"`: validate only base context fields are present + +Implement these as methods on `common.Credential`. + +Files to modify: `verifier/jwt_verifier.go`, `common/credential.go` +Verification: `go test ./verifier/... ./common/... -v` + +--- + +## Step 10: Custom proof checking (replace trustbloc proof checker) + +Replace the `defaults.NewDefaultProofChecker()` and the `ElsiProofChecker` wrapper. The proof checker needs to: +1. For JWT proofs: extract `kid`/issuer from headers, resolve DID → JWK, verify signature using `lestrrat-go/jwx` +2. For did:elsi: delegate to JAdES validator (existing logic stays, just remove trustbloc checker dependency) + +This also removes the dependency on `trustbloc/vc-go/proof/checker`, `trustbloc/vc-go/proof/defaults`, `trustbloc/kms-go/doc/jose`. + +Files to modify: `verifier/elsi_proof_checker.go`, `verifier/presentation_parser.go` +New file: `common/proof_checker.go` +Verification: `go test ./verifier/... -v` + +--- + +## Step 11: Replace trustbloc in `tir/tokenProvider.go` (M2M — last step) + +Replace the LD-proof creation for M2M token signing: +- `verifiable.NewPresentation(WithCredentials(...))` → `common.NewPresentation()` +- `vp.AddLinkedDataProof(LinkedDataProofContext{...})` → custom LD-proof signing using `lestrrat-go/jwx` for JWS creation +- Remove `vc-go/proof/creator`, `vc-go/proof/jwtproofs/ps256`, `vc-go/proof/ldproofs/jsonwebsignature2020`, `kms-go/spi/kms` +- Also replace `verifiable.ParseCredential()` for loading the auth credential from file + +Files to modify: `tir/tokenProvider.go`, `tir/tokenProvider_test.go` +Verification: `go test ./tir/... -v` + +--- + +## Step 12: Remove trustbloc dependencies + +1. Remove all trustbloc imports from every `.go` file +2. Run `go mod tidy` to remove unused dependencies from `go.mod`/`go.sum` +3. Verify clean build: `go build ./...` +4. Verify all tests: `go test ./... -v` + +--- + +## Verification (end-to-end) + +After each step: +```bash +go build ./... # compiles +go test ./... -v # all tests pass +``` + +After step 12 (final): +```bash +go mod tidy +grep -r "trustbloc" --include="*.go" . # should return nothing +go build ./... +go test ./... -v -coverprofile=profile.cov +``` diff --git a/SUMMARY.md b/SUMMARY.md new file mode 100644 index 0000000..60aa267 --- /dev/null +++ b/SUMMARY.md @@ -0,0 +1,50 @@ +# Release Notes: Remove trustbloc dependencies + +## Summary + +All three `trustbloc` libraries (`did-go`, `vc-go`, `kms-go`) and their transitive dependencies (`bbs-signature-go`, `sidetree-go`) have been replaced with custom, in-project implementations. This eliminates a set of unmaintained third-party dependencies while preserving full functional equivalence. + +**Net change**: +5,213 lines / -1,146 lines across 59 files. + +## What changed + +### New packages + +- **`did/`** — Custom DID resolution for `did:key`, `did:web`, and `did:jwk` methods, with a pluggable multi-method registry. +- **`common/credential.go`** — Project-local Verifiable Credential and Verifiable Presentation types (`Credential`, `Presentation`, `CredentialContents`, etc.), replacing `trustbloc/vc-go/verifiable`. +- **`common/vc_parser.go`** — Custom VP/VC parsing for both JSON-LD and JWT formats, with signature verification via `lestrrat-go/jwx` and the custom DID resolver. +- **`common/sdjwt.go`** — Custom SD-JWT parsing and verification (disclosure decoding, `_sd` digest matching, issuer signature verification, key binding JWT support). +- **`common/ldproof.go`** — Linked Data Proof creation for M2M token signing, replacing `vc-go/proof/creator` and related proof libraries. +- **`verifier/jwt_proof_checker.go`** — JWT proof checker replacing `trustbloc/vc-go/proof/checker` and the old `elsi_proof_checker.go`. + +### Modified packages + +- **`verifier/`** — All core verifier code (`verifier.go`, `presentation_parser.go`, `jwt_verifier.go`, `key_resolver.go`, `holder.go`, `trustedissuer.go`, `trustedparticipant.go`, `compliance.go`, `gaiax.go`, `request_object_client.go`) migrated from trustbloc types to local types. +- **`openapi/`** — HTTP handlers updated to use local credential/presentation types. +- **`tir/tokenProvider.go`** — M2M token provider rewritten to use local LD-proof creation and credential parsing instead of trustbloc's proof creator and verifiable credential library. +- **`gaiax/gaiaXClient.go`** — DID resolution switched from trustbloc VDR to custom `did/` package. + +### Removed + +- `verifier/elsi_proof_checker.go` — Replaced by `verifier/jwt_proof_checker.go`. +- All `trustbloc` imports across the entire codebase. +- 5 direct/indirect trustbloc dependencies from `go.mod` (`did-go`, `vc-go`, `kms-go`, `bbs-signature-go`, `sidetree-go`), along with ~120 lines of transitive dependencies from `go.sum`. + +### Test coverage + +Comprehensive tests were added before and during the migration for all replaced code paths, including `key_resolver_test.go`, `jwt_verifier_test.go`, `presentation_parser_test.go`, `trustedissuer_test.go`, `jwt_proof_checker_test.go`, `sdjwt_test.go`, `credential_test.go`, and all DID resolver tests. + +## Migration steps (for reference) + +The work was done incrementally across 13 PRs (#1-#12): + +0. Added missing tests for trustbloc-dependent code +1. Introduced local credential/presentation types +2. Created custom DID resolution package +3-5. Replaced DID resolution across `jwt_verifier`, `key_resolver`, `request_object_client`, `api_api`, and `gaiax` +6. Migrated all production and test code to local types +7. Custom VP/VC parsing (replaced `verifiable.ParsePresentation`) +8. Custom SD-JWT verification +9. Custom credential content validation +11. Replaced trustbloc in `tir/tokenProvider.go` +12. Removed all trustbloc dependencies and ran `go mod tidy` diff --git a/openapi/api_api.go b/openapi/api_api.go index 0f572b5..040816e 100644 --- a/openapi/api_api.go +++ b/openapi/api_api.go @@ -327,7 +327,7 @@ func verifiyVPToken(c *gin.Context, vpToken string, clientId string, scopes []st c.AbortWithStatusJSON(http.StatusBadRequest, err) return } - response := TokenResponse{TokenType: "Bearer", IssuedTokenType: common.TYPE_ACCESS_TOKEN, ExpiresIn: float32(expiration), AccessToken: signedToken, Scope: strings.Join(scopes, ",")} + response := TokenResponse{TokenType: "Bearer", IssuedTokenType: common.TYPE_ACCESS_TOKEN, ExpiresIn: float32(expiration), IdToken: signedToken, AccessToken: signedToken, Scope: strings.Join(scopes, ",")} logging.Log().Infof("Generated and signed token: %v", response) c.JSON(http.StatusOK, response) } @@ -349,7 +349,7 @@ func handleTokenTypeCode(c *gin.Context) { c.AbortWithStatusJSON(http.StatusForbidden, ErrorMessage{Summary: err.Error()}) return } - c.JSON(http.StatusOK, TokenResponse{TokenType: "Bearer", ExpiresIn: float32(expiration), AccessToken: jwt}) + c.JSON(http.StatusOK, TokenResponse{TokenType: "Bearer", ExpiresIn: float32(expiration), IdToken: jwt, AccessToken: jwt}) return } if assertionTypeExists { @@ -444,7 +444,7 @@ func handleWithClientAssertion(c *gin.Context, assertionType string, code string c.AbortWithStatusJSON(http.StatusForbidden, ErrorMessage{Summary: err.Error()}) return } - c.JSON(http.StatusOK, TokenResponse{TokenType: "Bearer", ExpiresIn: float32(expiration), AccessToken: jwt}) + c.JSON(http.StatusOK, TokenResponse{TokenType: "Bearer", ExpiresIn: float32(expiration), IdToken: jwt, AccessToken: jwt}) } // StartSIOPSameDevice - Starts the siop flow for credentials hold by the same device From deb97ba5170661fd95268b47cbe84ce87530b921 Mon Sep 17 00:00:00 2001 From: Stefan Wiedemann Date: Fri, 6 Mar 2026 13:59:53 +0100 Subject: [PATCH 2/3] cleanup --- CLAUDE.md | 144 ------------------- IMPLEMENTATION_PLAN.md | 304 ----------------------------------------- SUMMARY.md | 50 ------- 3 files changed, 498 deletions(-) delete mode 100644 CLAUDE.md delete mode 100644 IMPLEMENTATION_PLAN.md delete mode 100644 SUMMARY.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index c39a4f5..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,144 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Commands - -```bash -# Run all tests -go test ./... -v - -# Run tests with coverage -go test ./... -v -coverprofile=profile.cov ./... - -# Run tests for a specific package -go test ./verifier/... -v -go test ./openapi/... -v - -# Run a single test -go test ./verifier/... -v -run TestFunctionName - -# Build the binary -go build -o VCVerifier . - -# Build Docker image -docker build -t vcverifier . - -# Run locally (requires server.yaml config) -./VCVerifier -# or with alternate config: -CONFIG_FILE=path/to/config.yaml ./VCVerifier -``` - -## Architecture - -VCVerifier implements SIOP-2/OIDC4VP authentication flows. It acts as a Relying Party that receives Verifiable Presentations from wallets, verifies the credentials inside them, and issues signed JWTs for downstream use. - -### Package structure - -- **`main.go`**: Entry point. Reads config, initializes the verifier singleton and presentation parser, sets up the Gin HTTP server with routes from `openapi/routers.go`. -- **`openapi/`**: HTTP layer (generated from OpenAPI spec). - - `routers.go`: Route definitions. - - `api_api.go`: REST API handlers (token exchange, authentication response, JWKS, SIOP flows). - - `api_frontend.go`: UI handlers (QR login page). - - `websocket.go`: WebSocket support for cross-device flows. -- **`verifier/`**: Core business logic. The `Verifier` interface (implemented by `CredentialVerifier`) is the central abstraction. - - `verifier.go`: Orchestrates the full flow — session management, QR/connection string generation, JWT issuance. - - `presentation_parser.go`: Parses VP tokens (both JSON-LD and SD-JWT formats) using trustbloc libraries. - - `trustedparticipant.go`: Validates credential issuer against Trusted Participants Lists (EBSI TIR or Gaia-X Registry). - - `trustedissuer.go`: Validates issuer claims against Trusted Issuers Lists (EBSI TIR). - - `compliance.go`: Validates Gaia-X compliance credentials via SHA-256 signature checking. - - `holder.go`: Optional holder binding verification. - - `jwt_verifier.go`: JWT signature verification. - - `key_resolver.go`: DID key resolution (did:key, did:web, did:jwk). - - `elsi_proof_checker.go`: JAdES proof checking for did:elsi. - - `gaiax.go`: Gaia-X-specific credential validation logic. - - `credentialsConfig.go`: Retrieves per-service credential/scope configuration. - - `caching_client.go`: HTTP client with caching for JSON-LD document loading. - - `request_object_client.go`: Stores/retrieves signed request objects for `byReference` mode. -- **`config/`**: Configuration model (`config.go`) and loading (`provider.go`). The `configClient.go` fetches service config from an external Credentials-Config-Service or falls back to static YAML. -- **`tir/`**: EBSI Trusted Issuers Registry client. `tirClient.go` checks participation/issuance rights. `tokenProvider.go` handles M2M OAuth tokens for TIR access. -- **`gaiax/`**: Gaia-X Digital Clearing House registry client (`gaiaXClient.go`, `registry.go`). -- **`jades/`**: JAdES signature validation for did:elsi support. -- **`common/`**: Shared utilities — `cache.go` (in-memory cache wrapper), `clock.go`, `httpUtils.go`, `tokenSigner.go`, `metadata.go` (OpenID provider metadata). -- **`logging/`**: Structured logging setup (zap-based) and Gin middleware. -- **`views/`**: HTML templates and static assets for the QR login page (`verifier_present_qr.html`). -- **`api/`**: OpenAPI spec (`api.yaml`) and supporting YAML specs. - -### Key flows - -**Cross-device flow** (wallet on separate device): -1. Frontend calls `/api/v1/loginQR` → verifier creates session, generates QR code containing `openid4vp://` URI -2. User scans QR with wallet → wallet POSTs VP to `/api/v1/authentication_response` -3. Verifier validates credentials (participant lists → issuer lists → optional compliance/holder checks) -4. Frontend polls via WebSocket or receives callback → exchanges authorization code for JWT at `/token` - -**Same-device flow**: -1. Call `/api/v1/samedevice` → redirected to wallet with `openid4vp://` URI -2. Wallet POSTs to `/api/v1/authentication_response` -3. Response redirects back with `code` → exchange at `/token` - -### Request modes - -Three modes for the `openid4vp://` URI (configurable per request via `requestMode` param): -- `urlEncoded`: Parameters directly in URL -- `byValue`: Signed JWT request object embedded in URL -- `byReference`: Signed JWT served from `/api/v1/request/:id` - -`byValue` and `byReference` require `verifier.clientIdentification` to be configured with a signing key. - -### Configuration - -Config file at `./server.yaml` (or `CONFIG_FILE` env var). Key sections: -- `verifier.did`: The verifier's DID -- `verifier.clientIdentification`: Key/cert for signing request objects -- `verifier.keyAlgorithm` / `verifier.generateKey`: JWT signing key setup -- `configRepo`: Either a `configEndpoint` (external Credentials-Config-Service) or static `services` list defining per-service credential trust and presentation definitions - -### Singletons - -`verifier.InitVerifier()` and `verifier.InitPresentationParser()` set package-level singletons accessed by the HTTP handlers. Tests must call these or set the singletons directly. - -### Testing patterns - -Tests use `github.com/stretchr/testify/assert` and table-driven test cases. Mock implementations of interfaces (e.g. `Verifier`, `TirClient`, `GaiaXClient`) are defined inline in test files. The `common.Cache` and `common.Clock` interfaces exist specifically to enable time-controlled testing. - -## Gitea workflow - -The repository has a local Gitea instance for code review. Use this for all branch pushes and pull requests. - -- **Gitea URL**: `http://localhost:3000` -- **Repo**: `wistefan/verifier` -- **Credentials**: user `claude`, password `password` -- **Remote name**: `gitea` (configured with `http://claude:password@localhost:3000/wistefan/verifier.git`) -- **Base branch for PRs**: `trustbloc` - -### Creating a step branch and PR - -```bash -# Create branch from trustbloc -git checkout trustbloc -git checkout -b step-N-description - -# ... make changes, commit ... - -# Push to gitea -git push -u gitea step-N-description - -# Create PR via Gitea API -curl -s -u claude:password -X POST \ - http://localhost:3000/api/v1/repos/wistefan/verifier/pulls \ - -H 'Content-Type: application/json' \ - -d '{ - "title": "Step N: Description", - "body": "...", - "head": "step-N-description", - "base": "trustbloc" - }' -``` - -After a PR is merged, update the local `trustbloc` branch before creating the next step branch: -```bash -git checkout trustbloc -git pull gitea trustbloc -``` diff --git a/IMPLEMENTATION_PLAN.md b/IMPLEMENTATION_PLAN.md deleted file mode 100644 index 9d8a328..0000000 --- a/IMPLEMENTATION_PLAN.md +++ /dev/null @@ -1,304 +0,0 @@ -# Plan: Replace trustbloc libraries with custom implementations - -## Context - -The VCVerifier project depends on three trustbloc libraries (`did-go`, `vc-go`, `kms-go`) for DID resolution, Verifiable Credential/Presentation handling, proof verification, and SD-JWT parsing. These libraries are no longer well maintained and need to be replaced with custom, in-project implementations. The replacement must be transparent to the rest of the codebase — the existing interfaces and data flow should remain unchanged wherever possible. - -**Approach**: All replacements will be custom implementations (no new third-party DID or SD-JWT libraries). The M2M token provider (`tir/tokenProvider.go`) LD-proof creation will be the last step. - -## Pre-work: Improve test coverage on trustbloc-dependent code - -Before any replacement work begins, add tests for currently untested trustbloc-dependent code paths. This ensures we have a safety net for verifying functional equivalence after replacement. - -### Step 0: Add missing tests - -**0a.** `verifier/key_resolver.go` — `VdrKeyResolver.ResolvePublicKeyFromDID()` -- Zero test coverage today. Add tests with mocked VDR implementations: - - Successful resolution with `kid` containing `#` fragment - - Successful resolution with bare `did:key:...` (no fragment) - - VDR resolution failure (all VDRs error) - - Key ID not found in verification methods - - JWK serialization error -- Create a mock VDR type implementing the `api.VDR` interface from `did-go/vdr/api` for test isolation. - -**0b.** `verifier/jwt_verifier.go` — `TrustBlocValidator.ValidateVC()` -- Add tests for all validation modes: `"none"`, `"combined"`, `"jsonLd"`, `"baseContext"` -- Test error return paths when `ValidateCredential()` fails - -**0c.** `verifier/presentation_parser.go` — `ConfigurableSdJwtParser.ParseWithSdJwt()` -- Test malformed base64 payload -- Test missing `vp` claim in JWT payload -- Test missing `verifiableCredential` in vp -- Test missing `holder` field -- Test proof check failure path - -**0d.** `verifier/presentation_parser.go` — `ConfigurableSdJwtParser.ClaimsToCredential()` -- Test missing `iss` claim -- Test missing `vct` claim -- Test successful conversion with extra custom fields - -**0e.** `verifier/trustedissuer.go` — `parseAttribute()` / `parseAttributes()` -- Test base64 decode errors -- Test JSON unmarshal errors -- Test empty attributes array - -Files to modify: -- `verifier/key_resolver_test.go` (new or extend) -- `verifier/jwt_verifier_test.go` (extend) -- `verifier/presentation_parser_test.go` (extend) -- `verifier/trustedissuer_test.go` (extend) - -Verification: `go test ./verifier/... -v` — all existing + new tests pass. - ---- - -## Step 1: Introduce local credential/presentation types (`common/credential.go`) - -Create project-local types that mirror the subset of trustbloc `verifiable` types actually used. These become the new domain types that the rest of the codebase will operate on. - -**New file**: `common/credential.go` - -Types to define: -```go -type Issuer struct { ID string } -type Subject struct { ID string; CustomFields map[string]interface{} } -type CustomFields map[string]interface{} -type JSONObject = map[string]interface{} - -type CredentialContents struct { - ID string - Types []string - Issuer *Issuer - Subject []Subject -} - -type Credential struct { /* internal fields for contents, rawJSON, customFields */ } -// Methods: Contents(), ToRawJSON(), MarshalJSON(), ValidateCredential(opts...) - -type Presentation struct { - ID string - Holder string - /* internal credentials slice */ -} -// Methods: Credentials(), AddCredentials(), MarshalJSON() - -func CreateCredential(contents CredentialContents, customFields CustomFields) (*Credential, error) -func NewPresentation(opts ...PresentationOpt) (*Presentation, error) -``` - -At this point, **no production code changes**. Just define the types and write unit tests for them. - -Files to create: `common/credential.go`, `common/credential_test.go` - -Verification: `go test ./common/... -v` - ---- - -## Step 2: Custom DID resolution (`did/` package) - -Create a new `did/` package with resolvers for `did:key`, `did:web`, and `did:jwk`. - -**New files**: -- `did/resolver.go` — `Registry` interface + multi-method registry -- `did/document.go` — `Document`, `VerificationMethod`, `DocResolution` types -- `did/did_key.go` — `did:key` resolver (multicodec decode → JWK) -- `did/did_web.go` — `did:web` resolver (HTTP fetch → JSON parse) -- `did/did_jwk.go` — `did:jwk` resolver (base64url decode → JWK) -- `did/resolver_test.go`, `did/did_key_test.go`, `did/did_web_test.go`, `did/did_jwk_test.go` - -Key interfaces: -```go -type VDR interface { - Accept(method string) bool - Read(did string, opts ...ResolveOption) (*DocResolution, error) -} - -type Registry interface { - Resolve(did string, opts ...ResolveOption) (*DocResolution, error) -} - -type VerificationMethod struct { - ID string - Type string - Controller string - Value []byte - // JSONWebKey() returns *jose.JSONWebKey -} -``` - -The `VerificationMethod.JSONWebKey()` method must return a `go-jose` compatible JWK (or our own type that can be serialized to JSON and parsed by `lestrrat-go/jwx`). - -Verification: `go test ./did/... -v` with real did:key/did:jwk test vectors and mocked HTTP for did:web. - ---- - -## Step 3: Replace DID resolution in `verifier/jwt_verifier.go` - -Switch `JWTVerfificationMethodResolver.ResolveVerificationMethod()` from trustbloc VDR to our custom `did/` package. - -**Before** (current): -```go -import "github.com/trustbloc/did-go/method/web" -import "github.com/trustbloc/did-go/method/key" -import "github.com/trustbloc/did-go/method/jwk" -import "github.com/trustbloc/did-go/vdr" -import "github.com/trustbloc/vc-go/vermethod" - -registry := vdr.New(vdr.WithVDR(web.New()), vdr.WithVDR(key.New()), vdr.WithVDR(jwk.New())) -didDocument, err := registry.Resolve(expectedProofIssuer) -``` - -**After**: Use `did.NewRegistry(did.WithVDR(...))` from step 2. - -Also replace the `vermethod.VerificationMethod` return type. Define a local proof-checking compatible type. - -Files to modify: `verifier/jwt_verifier.go` -Verification: All tests in `verifier/jwt_verifier_test.go` pass + new integration-style tests with the custom resolver. - ---- - -## Step 4: Replace DID resolution in `verifier/key_resolver.go` - -Switch `VdrKeyResolver` from `trustbloc/did-go/vdr/api.VDR` to our custom `did.VDR`. - -**Before**: `Vdr []api.VDR` field using trustbloc's `did.DocResolution`, `did.VerificationMethod` -**After**: `Vdr []did.VDR` using our `did.DocResolution`, `did.VerificationMethod` - -Files to modify: `verifier/key_resolver.go`, `verifier/key_resolver_test.go` -Also update: `verifier/request_object_client.go` (uses same VDR pattern), `openapi/api_api.go` (constructs `VdrKeyResolver`) -Verification: `go test ./verifier/... ./openapi/... -v` - ---- - -## Step 5: Replace DID resolution in `gaiax/gaiaXClient.go` - -Switch from trustbloc VDR to custom `did/` package. - -Files to modify: `gaiax/gaiaXClient.go`, `gaiax/gaiaXClient_test.go` -Verification: `go test ./gaiax/... -v` - ---- - -## Step 6: Migrate production code to local credential/presentation types - -This is the largest step. Replace `trustbloc/vc-go/verifiable` types with `common.Credential`, `common.Presentation`, etc. across all production files. - -Migrate in this order (each sub-step compilable): - -**6a.** `verifier/presentation_parser.go` — Change `PresentationParser` and `SdJwtParser` interfaces to return `*common.Presentation` / `*common.Credential`. Update `ConfigurablePresentationParser` and `ConfigurableSdJwtParser`. Internally still call trustbloc parsing and convert to local types. - -**6b.** `verifier/verifier.go` — Change `Verifier` interface and `CredentialVerifier` to use `*common.Credential`, `*common.Presentation`. Update `ValidationService` interface, `ValidationContext`, `extractCredentialTypes`, `buildInclusion`, `AuthenticationResponse`, `GenerateToken`. - -**6c.** `verifier/holder.go`, `verifier/trustedissuer.go`, `verifier/trustedparticipant.go`, `verifier/compliance.go`, `verifier/gaiax.go` — Update `ValidateVC` methods to accept `*common.Credential`. - -**6d.** `openapi/api_api.go`, `openapi/api_frontend.go` — Update handler code to use local types. - -**6e.** Update all test files to use local types for credential/presentation creation. - -At this point, the codebase uses local types everywhere but `presentation_parser.go` still internally calls trustbloc for parsing/proof-checking, and `jwt_verifier.go`'s `TrustBlocValidator` still calls trustbloc for content validation. - -Files to modify: All files listed above + their test files. -Verification: `go test ./... -v` — full suite passes. - ---- - -## Step 7: Custom VP/VC parsing (replace `verifiable.ParsePresentation`) - -Implement JSON-LD VP parsing in the local types. A VP is a JWT or JSON object containing: -- `type`, `holder`, `verifiableCredential` (array of VCs — each a JWT string or JSON object) -- Proof (JWT signature or LD-proof) - -The existing `ConfigurablePresentationParser.ParsePresentation()` currently delegates to `verifiable.ParsePresentation()`. Replace with custom logic: -1. Detect JWT vs JSON-LD format -2. For JWT: decode header+payload, verify signature using our DID resolver + `lestrrat-go/jwx` -3. Extract embedded credentials -4. For each VC: verify its JWT signature similarly -5. Return `*common.Presentation` - -Files to modify: `verifier/presentation_parser.go` -New file: `common/vp_parser.go` (or in `common/credential.go`) -Verification: `go test ./verifier/... -v` - ---- - -## Step 8: Custom SD-JWT verification (replace `vc-go/sdjwt/verifier`) - -Implement SD-JWT parsing per the SD-JWT specification: -1. Split combined format by `~` separator -2. Verify issuer JWT signature (using lestrrat-go/jwx + our DID resolver) -3. Decode each disclosure (base64url → JSON array `[salt, claim_name, claim_value]`) -4. Reconstruct full claims map from `_sd` digests + disclosures -5. Optionally verify key binding JWT - -New file: `common/sdjwt.go`, `common/sdjwt_test.go` -Files to modify: `verifier/presentation_parser.go` — replace `sdv.Parse()` call with custom implementation. -Verification: `go test ./common/... ./verifier/... -v` - ---- - -## Step 9: Custom credential content validation (replace `TrustBlocValidator`) - -Replace `verifiable.ValidateCredential()` calls in `jwt_verifier.go`. The validation modes are: -- `"none"`: no-op -- `"combined"`: basic JSON schema + JSON-LD validation -- `"jsonLd"`: JSON-LD only -- `"baseContext"`: validate only base context fields are present - -Implement these as methods on `common.Credential`. - -Files to modify: `verifier/jwt_verifier.go`, `common/credential.go` -Verification: `go test ./verifier/... ./common/... -v` - ---- - -## Step 10: Custom proof checking (replace trustbloc proof checker) - -Replace the `defaults.NewDefaultProofChecker()` and the `ElsiProofChecker` wrapper. The proof checker needs to: -1. For JWT proofs: extract `kid`/issuer from headers, resolve DID → JWK, verify signature using `lestrrat-go/jwx` -2. For did:elsi: delegate to JAdES validator (existing logic stays, just remove trustbloc checker dependency) - -This also removes the dependency on `trustbloc/vc-go/proof/checker`, `trustbloc/vc-go/proof/defaults`, `trustbloc/kms-go/doc/jose`. - -Files to modify: `verifier/elsi_proof_checker.go`, `verifier/presentation_parser.go` -New file: `common/proof_checker.go` -Verification: `go test ./verifier/... -v` - ---- - -## Step 11: Replace trustbloc in `tir/tokenProvider.go` (M2M — last step) - -Replace the LD-proof creation for M2M token signing: -- `verifiable.NewPresentation(WithCredentials(...))` → `common.NewPresentation()` -- `vp.AddLinkedDataProof(LinkedDataProofContext{...})` → custom LD-proof signing using `lestrrat-go/jwx` for JWS creation -- Remove `vc-go/proof/creator`, `vc-go/proof/jwtproofs/ps256`, `vc-go/proof/ldproofs/jsonwebsignature2020`, `kms-go/spi/kms` -- Also replace `verifiable.ParseCredential()` for loading the auth credential from file - -Files to modify: `tir/tokenProvider.go`, `tir/tokenProvider_test.go` -Verification: `go test ./tir/... -v` - ---- - -## Step 12: Remove trustbloc dependencies - -1. Remove all trustbloc imports from every `.go` file -2. Run `go mod tidy` to remove unused dependencies from `go.mod`/`go.sum` -3. Verify clean build: `go build ./...` -4. Verify all tests: `go test ./... -v` - ---- - -## Verification (end-to-end) - -After each step: -```bash -go build ./... # compiles -go test ./... -v # all tests pass -``` - -After step 12 (final): -```bash -go mod tidy -grep -r "trustbloc" --include="*.go" . # should return nothing -go build ./... -go test ./... -v -coverprofile=profile.cov -``` diff --git a/SUMMARY.md b/SUMMARY.md deleted file mode 100644 index 60aa267..0000000 --- a/SUMMARY.md +++ /dev/null @@ -1,50 +0,0 @@ -# Release Notes: Remove trustbloc dependencies - -## Summary - -All three `trustbloc` libraries (`did-go`, `vc-go`, `kms-go`) and their transitive dependencies (`bbs-signature-go`, `sidetree-go`) have been replaced with custom, in-project implementations. This eliminates a set of unmaintained third-party dependencies while preserving full functional equivalence. - -**Net change**: +5,213 lines / -1,146 lines across 59 files. - -## What changed - -### New packages - -- **`did/`** — Custom DID resolution for `did:key`, `did:web`, and `did:jwk` methods, with a pluggable multi-method registry. -- **`common/credential.go`** — Project-local Verifiable Credential and Verifiable Presentation types (`Credential`, `Presentation`, `CredentialContents`, etc.), replacing `trustbloc/vc-go/verifiable`. -- **`common/vc_parser.go`** — Custom VP/VC parsing for both JSON-LD and JWT formats, with signature verification via `lestrrat-go/jwx` and the custom DID resolver. -- **`common/sdjwt.go`** — Custom SD-JWT parsing and verification (disclosure decoding, `_sd` digest matching, issuer signature verification, key binding JWT support). -- **`common/ldproof.go`** — Linked Data Proof creation for M2M token signing, replacing `vc-go/proof/creator` and related proof libraries. -- **`verifier/jwt_proof_checker.go`** — JWT proof checker replacing `trustbloc/vc-go/proof/checker` and the old `elsi_proof_checker.go`. - -### Modified packages - -- **`verifier/`** — All core verifier code (`verifier.go`, `presentation_parser.go`, `jwt_verifier.go`, `key_resolver.go`, `holder.go`, `trustedissuer.go`, `trustedparticipant.go`, `compliance.go`, `gaiax.go`, `request_object_client.go`) migrated from trustbloc types to local types. -- **`openapi/`** — HTTP handlers updated to use local credential/presentation types. -- **`tir/tokenProvider.go`** — M2M token provider rewritten to use local LD-proof creation and credential parsing instead of trustbloc's proof creator and verifiable credential library. -- **`gaiax/gaiaXClient.go`** — DID resolution switched from trustbloc VDR to custom `did/` package. - -### Removed - -- `verifier/elsi_proof_checker.go` — Replaced by `verifier/jwt_proof_checker.go`. -- All `trustbloc` imports across the entire codebase. -- 5 direct/indirect trustbloc dependencies from `go.mod` (`did-go`, `vc-go`, `kms-go`, `bbs-signature-go`, `sidetree-go`), along with ~120 lines of transitive dependencies from `go.sum`. - -### Test coverage - -Comprehensive tests were added before and during the migration for all replaced code paths, including `key_resolver_test.go`, `jwt_verifier_test.go`, `presentation_parser_test.go`, `trustedissuer_test.go`, `jwt_proof_checker_test.go`, `sdjwt_test.go`, `credential_test.go`, and all DID resolver tests. - -## Migration steps (for reference) - -The work was done incrementally across 13 PRs (#1-#12): - -0. Added missing tests for trustbloc-dependent code -1. Introduced local credential/presentation types -2. Created custom DID resolution package -3-5. Replaced DID resolution across `jwt_verifier`, `key_resolver`, `request_object_client`, `api_api`, and `gaiax` -6. Migrated all production and test code to local types -7. Custom VP/VC parsing (replaced `verifiable.ParsePresentation`) -8. Custom SD-JWT verification -9. Custom credential content validation -11. Replaced trustbloc in `tir/tokenProvider.go` -12. Removed all trustbloc dependencies and ran `go mod tidy` From cb46f78f8d11b750eb1e7e77bd4a1338be23dbbe Mon Sep 17 00:00:00 2001 From: Stefan Wiedemann Date: Mon, 9 Mar 2026 16:12:02 +0100 Subject: [PATCH 3/3] fix test --- openapi/api_api_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openapi/api_api_test.go b/openapi/api_api_test.go index ff99515..7063261 100644 --- a/openapi/api_api_test.go +++ b/openapi/api_api_test.go @@ -111,7 +111,7 @@ func TestGetToken(t *testing.T) { expectedError ErrorMessage } tests := []test{ - {testName: "If a valid authorization_code request is received a token should be responded.", proofCheck: false, testGrantType: "authorization_code", testCode: "my-auth-code", testRedirectUri: "http://my-redirect.org", mockJWTString: "theJWT", mockExpiration: 10, mockError: nil, expectedStatusCode: 200, expectedResponse: TokenResponse{TokenType: "Bearer", ExpiresIn: 10, AccessToken: "theJWT"}, expectedError: ErrorMessage{}}, + {testName: "If a valid authorization_code request is received a token should be responded.", proofCheck: false, testGrantType: "authorization_code", testCode: "my-auth-code", testRedirectUri: "http://my-redirect.org", mockJWTString: "theJWT", mockExpiration: 10, mockError: nil, expectedStatusCode: 200, expectedResponse: TokenResponse{TokenType: "Bearer", ExpiresIn: 10, AccessToken: "theJWT", IdToken: "theJWT"}, expectedError: ErrorMessage{}}, {testName: "If no grant type is provided, the request should fail.", proofCheck: false, testGrantType: "", testCode: "my-auth-code", testRedirectUri: "http://my-redirect.org", expectedStatusCode: 400, expectedError: ErrorMessagNoGrantType}, {testName: "If an invalid grant type is provided, the request should fail.", proofCheck: false, testGrantType: "my_special_code", testCode: "my-auth-code", testRedirectUri: "http://my-redirect.org", expectedStatusCode: 400, expectedError: ErrorMessageUnsupportedGrantType}, {testName: "If no auth code is provided, the request should fail.", proofCheck: false, testGrantType: "authorization_code", testCode: "", testRedirectUri: "http://my-redirect.org", expectedStatusCode: 400, expectedError: ErrorMessageNoCode}, @@ -119,11 +119,11 @@ func TestGetToken(t *testing.T) { {testName: "If the verify returns an error, a 403 should be answerd.", proofCheck: false, testGrantType: "authorization_code", testCode: "my-auth-code", testRedirectUri: "http://my-redirect.org", mockError: errors.New("invalid"), expectedStatusCode: 403, expectedError: ErrorMessage{}}, {testName: "If no valid scope is provided, the request should be executed in the default scope.", proofCheck: false, testVPToken: getValidVPToken(), testGrantType: "vp_token", expectedStatusCode: 200}, - {testName: "If a valid vp_token request is received a token should be responded.", proofCheck: false, testGrantType: "vp_token", testVPToken: getValidVPToken(), testScope: "tir_read", mockJWTString: "theJWT", mockExpiration: 10, expectedStatusCode: 200, expectedResponse: TokenResponse{TokenType: "Bearer", ExpiresIn: 10, AccessToken: "theJWT", Scope: "tir_read", IssuedTokenType: common.TYPE_ACCESS_TOKEN}}, - {testName: "If a valid signed vp_token request is received a token should be responded.", proofCheck: true, testGrantType: "vp_token", testVPToken: getValidSignedDidKeyVPToken(), testScope: "tir_read", mockJWTString: "theJWT", mockExpiration: 10, expectedStatusCode: 200, expectedResponse: TokenResponse{TokenType: "Bearer", ExpiresIn: 10, AccessToken: "theJWT", Scope: "tir_read", IssuedTokenType: common.TYPE_ACCESS_TOKEN}}, + {testName: "If a valid vp_token request is received a token should be responded.", proofCheck: false, testGrantType: "vp_token", testVPToken: getValidVPToken(), testScope: "tir_read", mockJWTString: "theJWT", mockExpiration: 10, expectedStatusCode: 200, expectedResponse: TokenResponse{TokenType: "Bearer", ExpiresIn: 10, AccessToken: "theJWT", IdToken: "theJWT", Scope: "tir_read", IssuedTokenType: common.TYPE_ACCESS_TOKEN}}, + {testName: "If a valid signed vp_token request is received a token should be responded.", proofCheck: true, testGrantType: "vp_token", testVPToken: getValidSignedDidKeyVPToken(), testScope: "tir_read", mockJWTString: "theJWT", mockExpiration: 10, expectedStatusCode: 200, expectedResponse: TokenResponse{TokenType: "Bearer", ExpiresIn: 10, AccessToken: "theJWT", IdToken: "theJWT", Scope: "tir_read", IssuedTokenType: common.TYPE_ACCESS_TOKEN}}, {testName: "If no valid vp_token is provided, the request should fail.", proofCheck: false, testGrantType: "vp_token", testScope: "tir_read", expectedStatusCode: 400, expectedError: ErrorMessageNoToken}, // token-exchange - {testName: "If a valid token-exchange request is received a token should be responded.", proofCheck: false, testGrantType: "urn:ietf:params:oauth:grant-type:token-exchange", testVPToken: getValidVPToken(), testScope: "tir_read", testResource: "my-client-id", testSubjectTokenType: "urn:eu:oidf:vp_token", mockJWTString: "theJWT", mockExpiration: 10, expectedStatusCode: 200, expectedResponse: TokenResponse{TokenType: "Bearer", ExpiresIn: 10, AccessToken: "theJWT", Scope: "tir_read", IssuedTokenType: common.TYPE_ACCESS_TOKEN}}, + {testName: "If a valid token-exchange request is received a token should be responded.", proofCheck: false, testGrantType: "urn:ietf:params:oauth:grant-type:token-exchange", testVPToken: getValidVPToken(), testScope: "tir_read", testResource: "my-client-id", testSubjectTokenType: "urn:eu:oidf:vp_token", mockJWTString: "theJWT", mockExpiration: 10, expectedStatusCode: 200, expectedResponse: TokenResponse{TokenType: "Bearer", ExpiresIn: 10, AccessToken: "theJWT", IdToken: "theJWT", Scope: "tir_read", IssuedTokenType: common.TYPE_ACCESS_TOKEN}}, {testName: "If a token-exchange request is received without resource, it should fail.", proofCheck: false, testGrantType: "urn:ietf:params:oauth:grant-type:token-exchange", testVPToken: getValidVPToken(), testScope: "tir_read", testSubjectTokenType: "urn:eu:oidf:vp_token", expectedStatusCode: 400, expectedError: ErrorMessageNoResource}, {testName: "If a token-exchange request is received with invalid subject_token_type, it should fail.", proofCheck: false, testGrantType: "urn:ietf:params:oauth:grant-type:token-exchange", testVPToken: getValidVPToken(), testScope: "tir_read", testResource: "my-client-id", testSubjectTokenType: "invalid_type", expectedStatusCode: 400, expectedError: ErrorMessageInvalidSubjectTokenType}, {testName: "If a token-exchange request is received with invalid requested_token_type, it should fail.", proofCheck: false, testGrantType: "urn:ietf:params:oauth:grant-type:token-exchange", testVPToken: getValidVPToken(), testScope: "tir_read", testResource: "my-client-id", testSubjectTokenType: "urn:eu:oidf:vp_token", testRequestedTokenType: "invalid_type", expectedStatusCode: 400, expectedError: ErrorMessageInvalidRequestedTokenType},