Conversation
|
Coverage Impact ⬇️ Merging this pull request will decrease total coverage on Modified Files with Diff Coverage (7)
🤖 Increase coverage with AI coding...🚦 See full report on Qlty Cloud » 🛟 Help
|
There was a problem hiding this comment.
Pull request overview
Adds support for supplying an id_token (Dezi JWT) in the service access token request and converting it into a DeziIDTokenCredential so it can participate in VC/VP-based authorization.
Changes:
- Extend IAM request model/API/docs to accept
id_token, and append a derived Dezi credential to the requested credentials set. - Introduce
DeziIDTokenCredentialtype, creation helper, and a validator hook (currently stubbed). - Add unit/e2e tests and allow
DeziIDJWTas an OpenIDldp_vc/ldp_vpproof type.
Reviewed changes
Copilot reviewed 24 out of 24 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| vcr/verifier/verifier_test.go | Adds verifier test and helper to create a Dezi credential from a signed JWT. |
| vcr/verifier/verifier.go | Skips VC signature verification when credential is DeziIDTokenCredential. |
| vcr/pe/presentation_definition_test.go | Adds presentation-exchange matching tests for DeziIDTokenCredential. |
| vcr/credential/validator.go | Adds deziIDTokenCredentialValidator (currently not performing real JWT validation). |
| vcr/credential/util.go | Allows auto-correction for Dezi credentials with “derived proof” (DeziIDJWT). |
| vcr/credential/types.go | Introduces DeziIDTokenCredentialTypeURI. |
| vcr/credential/resolver.go | Registers deziIDTokenCredentialValidator based on VC type. |
| vcr/credential/dezi_test.go | Adds unit test for CreateDeziIDTokenCredential. |
| vcr/credential/dezi.go | Implements CreateDeziIDTokenCredential to derive a VC from an id_token. |
| e2e-tests/oauth-flow/dezi_idtoken/run-test.sh | Adds e2e flow script to request/introspect tokens using id_token. |
| e2e-tests/oauth-flow/dezi_idtoken/generate-jwt.sh | Adds JWT generation helper for the e2e test. |
| e2e-tests/oauth-flow/dezi_idtoken/docker-compose.yml | Adds docker-compose setup for the new e2e test scenario. |
| e2e-tests/oauth-flow/dezi_idtoken/certs/nodeA.pem | Adds test certificate for the e2e environment. |
| e2e-tests/oauth-flow/dezi_idtoken/certs/nodeA.key | Adds test private key for the e2e environment. |
| e2e-tests/oauth-flow/dezi_idtoken/certs/nodeA-chain.pem | Adds test certificate chain for issuing X509Credential in e2e. |
| e2e-tests/oauth-flow/dezi_idtoken/certs/dezi_signing.pem | Adds test signing certificate for Dezi JWT generation. |
| e2e-tests/oauth-flow/dezi_idtoken/certs/dezi_signing.key | Adds test signing private key for Dezi JWT generation. |
| e2e-tests/oauth-flow/dezi_idtoken/certs/README.md | Documents how e2e cert material was generated. |
| e2e-tests/oauth-flow/dezi_idtoken/accesspolicy.json | Adds policy requiring DeziIDTokenCredential and mapping fields into claims. |
| e2e-tests/browser/client/iam/generated.go | Updates generated client model to include id_token. |
| docs/_static/auth/v2.yaml | Documents the new id_token request field in the API spec. |
| auth/oauth/openid.go | Adds DeziIDJWT as supported proof type for ldp_vc/ldp_vp. |
| auth/api/iam/generated.go | Updates generated server model to include id_token. |
| auth/api/iam/api.go | Appends a derived Dezi credential when id_token is provided. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| func CreateDeziIDTokenCredential(idTokenSerialized string) (*vc.VerifiableCredential, error) { | ||
| idToken, err := jwt.Parse([]byte(idTokenSerialized), jwt.WithVerify(false), jwt.WithAcceptableSkew(time.Hour*24*365*10)) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("parsing id_token: %w", err) | ||
| } |
There was a problem hiding this comment.
CreateDeziIDTokenCredential parses the id_token with signature verification explicitly disabled and with an extremely large acceptable clock skew (10 years). This turns untrusted input into a credential that will later be treated as “verified”. Parse and verify the JWT signature (using the x5c/x5t headers as per spec) and enforce reasonable skew / required time claims (nbf/exp) so expired/not-yet-valid tokens are rejected.
| if len(credential.Proof) > 0 { | ||
| return credential | ||
| var proof []proof.LDProof | ||
| _ = credential.UnmarshalProofValue(&proof) |
There was a problem hiding this comment.
AutoCorrectSelfAttestedCredential ignores the error from UnmarshalProofValue. If proof unmarshalling fails, this silently skips DeziIDJWT detection and can lead to credentials not being auto-corrected when expected. Handle the error explicitly (e.g., treat it as non-Dezi and return early, or return an error to the caller) instead of discarding it.
| _ = credential.UnmarshalProofValue(&proof) | |
| if err := credential.UnmarshalProofValue(&proof); err != nil { | |
| // If the proof cannot be unmarshalled, treat it as non-Dezi and return without auto-correction. | |
| return credential | |
| } |
vcr/verifier/verifier_test.go
Outdated
| err = token.Set("Dezi_id", "900000009") | ||
| require.NoError(t, err) | ||
| err = token.Set("x5c", []string{base64.StdEncoding.EncodeToString(keyPair.Leaf.Raw)}) | ||
| require.NoError(t, err) |
There was a problem hiding this comment.
The Dezi JWT certificate chain is added as an x5c claim in the payload, but the (currently commented) validator code is written to extract x5c from the JWS protected headers. To avoid tests diverging from the intended validation approach, put x5c in the JWT header (or update the validator to use the claim if that’s what the Dezi spec requires).
| "jwt": idTokenSerialized, | ||
| }, | ||
| } | ||
| data, _ := json.Marshal(credentialMap) |
There was a problem hiding this comment.
json.Marshal errors are ignored here. While unlikely, marshaling can fail (e.g., if a claim value ends up being a non-marshalable type), and ignoring it can lead to confusing downstream parse errors. Handle and return the marshal error explicitly.
| data, _ := json.Marshal(credentialMap) | |
| data, err := json.Marshal(credentialMap) | |
| if err != nil { | |
| return nil, fmt.Errorf("marshaling credential map: %w", err) | |
| } |
| // DeziIDTokenCredential: signature is verified by Dezi id_token inside the credential. Signature verification is skipped here. | ||
| if credentialToVerify.IsType(credential.DeziIDTokenCredentialTypeURI) { | ||
| checkSignature = false | ||
| } |
There was a problem hiding this comment.
Disabling VC signature verification for DeziIDTokenCredential makes the credential effectively unverifiable unless the embedded id_token is cryptographically verified elsewhere. The current Dezi validator does not verify the JWT signature, so this becomes an integrity/authenticity bypass. Instead of skipping signature verification here, verify the embedded id_token signature (and its trust chain) as part of credential validation, or keep VC signature verification enabled when no embedded-token verification is performed.
| // DeziIDTokenCredential: signature is verified by Dezi id_token inside the credential. Signature verification is skipped here. | |
| if credentialToVerify.IsType(credential.DeziIDTokenCredentialTypeURI) { | |
| checkSignature = false | |
| } |
vcr/credential/validator.go
Outdated
| func (d deziIDTokenCredentialValidator) Validate(credential vc.VerifiableCredential) error { | ||
| type proofType struct { | ||
| Type string `json:"type"` | ||
| JWT string `json:"jwt"` | ||
| } | ||
| proofs := []proofType{} | ||
| if err := credential.UnmarshalProofValue(&proofs); err != nil { | ||
| return fmt.Errorf("%w: invalid proof format: %w", errValidation, err) | ||
| } | ||
| if len(proofs) != 1 { | ||
| return fmt.Errorf("%w: expected exactly one proof, got %d", errValidation, len(proofs)) | ||
| } | ||
| proof := proofs[0] | ||
| if proof.Type != "DeziIDJWT" { | ||
| return fmt.Errorf("%w: invalid proof type: expected 'DeziIDToken', got '%s'", errValidation, proof.Type) | ||
| } | ||
| if err := d.validateDeziToken(credential, proof.JWT); err != nil { | ||
| return fmt.Errorf("%w: invalid Dezi id_token: %w", errValidation, err) | ||
| } | ||
| return (defaultCredentialValidator{}).Validate(credential) | ||
| } |
There was a problem hiding this comment.
deziIDTokenCredentialValidator currently accepts any embedded JWT: validateDeziToken always returns nil and all signature/claim checks are commented out. This means an attacker can forge a DeziIDTokenCredential with arbitrary subject/roles and it will pass validation. Implement actual JWT parsing with signature verification (e.g., using x5c from the JWS header) and validate required claims (exp/nbf/iss/aud) before accepting the credential.
vcr/verifier/verifier_test.go
Outdated
| keyPair, err := tls.LoadX509KeyPair("../../test/pki/certificate-and-key.pem", "../../test/pki/certificate-and-key.pem") | ||
| require.NoError(t, err) | ||
|
|
||
| key, err := jwk.FromRaw(keyPair.PrivateKey) | ||
| require.NoError(t, err) | ||
|
|
||
| // Set the key ID and x5t (X.509 thumbprint) | ||
| x5t := sha1.Sum(keyPair.Leaf.Raw) | ||
| err = key.Set(jwk.KeyIDKey, base64.StdEncoding.EncodeToString(x5t[:])) |
There was a problem hiding this comment.
tls.LoadX509KeyPair does not reliably populate keyPair.Leaf (the codebase sets it explicitly elsewhere, e.g. core/server_config.go:113). Accessing keyPair.Leaf.Raw here can panic with a nil pointer. Parse the leaf certificate from keyPair.Certificate[0] and assign it to keyPair.Leaf before using it.
vcr/verifier/verifier_test.go
Outdated
| println(string(signed)) | ||
|
|
There was a problem hiding this comment.
This test helper prints the signed JWT to stdout. That adds noise to go test output and can leak potentially sensitive test material in CI logs. Please remove the println or gate it behind an explicit debug flag.
| println(string(signed)) |
| privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) | ||
| token := jwt.New() | ||
| _ = token.Set(jwt.NotBeforeKey, 1701933627) | ||
| _ = token.Set(jwt.ExpirationKey, 1701933697) | ||
| _ = token.Set("initials", "B.B.") | ||
| _ = token.Set("surname", "Jansen") | ||
| _ = token.Set("surname_prefix", "van der") | ||
| _ = token.Set("Dezi_id", "900000009") | ||
| _ = token.Set("relations", []map[string]interface{}{ | ||
| { | ||
| "entity_name": "Zorgaanbieder", | ||
| "roles": []string{"01.041", "30.000"}, | ||
| "ura": "87654321", | ||
| }, | ||
| }) | ||
| signedToken, _ := jwt.Sign(token, jwt.WithKey(jwa.ES256, privateKey)) |
There was a problem hiding this comment.
This test ignores errors from key generation, claim setting, signing, and JSON marshaling (using _ = / _ , _ :=). If any of these steps fails, the test may pass/fail for the wrong reason. Use require.NoError for each operation so failures are surfaced clearly.
| privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) | |
| token := jwt.New() | |
| _ = token.Set(jwt.NotBeforeKey, 1701933627) | |
| _ = token.Set(jwt.ExpirationKey, 1701933697) | |
| _ = token.Set("initials", "B.B.") | |
| _ = token.Set("surname", "Jansen") | |
| _ = token.Set("surname_prefix", "van der") | |
| _ = token.Set("Dezi_id", "900000009") | |
| _ = token.Set("relations", []map[string]interface{}{ | |
| { | |
| "entity_name": "Zorgaanbieder", | |
| "roles": []string{"01.041", "30.000"}, | |
| "ura": "87654321", | |
| }, | |
| }) | |
| signedToken, _ := jwt.Sign(token, jwt.WithKey(jwa.ES256, privateKey)) | |
| privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) | |
| require.NoError(t, err) | |
| token := jwt.New() | |
| err = token.Set(jwt.NotBeforeKey, 1701933627) | |
| require.NoError(t, err) | |
| err = token.Set(jwt.ExpirationKey, 1701933697) | |
| require.NoError(t, err) | |
| err = token.Set("initials", "B.B.") | |
| require.NoError(t, err) | |
| err = token.Set("surname", "Jansen") | |
| require.NoError(t, err) | |
| err = token.Set("surname_prefix", "van der") | |
| require.NoError(t, err) | |
| err = token.Set("Dezi_id", "900000009") | |
| require.NoError(t, err) | |
| err = token.Set("relations", []map[string]interface{}{ | |
| { | |
| "entity_name": "Zorgaanbieder", | |
| "roles": []string{"01.041", "30.000"}, | |
| "ura": "87654321", | |
| }, | |
| }) | |
| require.NoError(t, err) | |
| signedToken, err := jwt.Sign(token, jwt.WithKey(jwa.ES256, privateKey)) | |
| require.NoError(t, err) |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
❌ 25 blocking issues (39 total)
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 29 out of 31 changed files in this pull request and generated 15 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| _, err = leafCert.Verify(x509.VerifyOptions{ | ||
| Roots: core.NewCertPool(d.trustStore.RootCAs), | ||
| CurrentTime: d.clock(), | ||
| Intermediates: core.NewCertPool(d.trustStore.IntermediateCAs), | ||
| KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, // TODO: use more specific key usage if possible |
There was a problem hiding this comment.
validateSignature dereferences d.trustStore without a nil check (RootCAs/IntermediateCAs). If the validator is used without an initialized trust store, this will panic instead of returning a validation error. Add an explicit nil check and return a clear error (and ensure callers initialize the trust store).
|
|
||
| signingKeyCert, err := tls.LoadX509KeyPair("../../test/pki/certificate-and-key.pem", "../../test/pki/certificate-and-key.pem") | ||
| require.NoError(t, err) | ||
|
|
There was a problem hiding this comment.
tls.LoadX509KeyPair does not populate signingKeyCert.Leaf by default, so signingKeyCert.Leaf.PublicKey can be nil and cause a panic. Parse the leaf certificate from signingKeyCert.Certificate[0] (as done later in the 2024 test) before accessing Leaf.PublicKey.
| if signingKeyCert.Leaf == nil { | |
| signingKeyCert.Leaf, err = x509.ParseCertificate(signingKeyCert.Certificate[0]) | |
| require.NoError(t, err) | |
| } |
| idTokenCredential, err := credential.CreateDeziIDTokenCredential(*request.Body.IdToken) | ||
| if err != nil { | ||
| return nil, core.InvalidInputError("failed to create id_token credential: %w", err) | ||
| } | ||
| credentials = append(credentials, *idTokenCredential) |
There was a problem hiding this comment.
The credential created from id_token is appended to the credentials slice, but the subsequent “self-asserted credentials MUST NOT contain an issuer/credentialSubject.id” validation loop will reject it because CreateDeziIDTokenCredential sets a non-empty issuer (and potentially other fields). To make id_token usable, the self-asserted restrictions should only apply to truly self-asserted input credentials (or explicitly skip DeziIDTokenCredential / DeziIDJWT-proof credentials).
| "id": "organization_ura_dezi", | ||
| "path": [ | ||
| "$.credentialSubject.identifier" | ||
| ], | ||
| "filter": { | ||
| "type": "string" | ||
| } |
There was a problem hiding this comment.
The Dezi credentialSubject is represented as an array in the VC model elsewhere in this codebase (e.g., tests access CredentialSubject[0], and other policies use credentialSubject[0]). These JSONPaths omit [0] (e.g., $.credentialSubject.identifier), which may not match the marshalled VC JSON (likely credentialSubject[0].identifier). Consider updating the paths to consistently index [0] to avoid policy evaluation failures.
| "$.credentialSubject.employee.roles" | ||
| ], | ||
| "filter": { | ||
| "type": "string" |
There was a problem hiding this comment.
The user_roles field points to an array (credentialSubject.employee.roles), but the filter declares type "string". This mismatch can cause presentation-evaluation to fail or silently not enforce the intended constraint. Update the filter schema to expect an array (and, if needed, constrain array item types).
| "type": "string" | |
| "type": "array", | |
| "items": { | |
| "type": "string" | |
| } |
| REQUEST=$( | ||
| cat << EOF | ||
| { | ||
| "authorization_server": "https://nodeA/oauth2/vendorA", | ||
| "token_type": "bearer", | ||
| "scope": "test", | ||
| "id_token": "$IDTOKEN" | ||
| } | ||
| EOF | ||
| ) | ||
| # Request access token | ||
| RESPONSE=$(echo $REQUEST | curl -X POST -s --data-binary @- http://localhost:18081/internal/auth/v2/vendorA/request-service-access-token -H "Content-Type: application/json") | ||
| if echo $RESPONSE | grep -q "access_token"; then |
There was a problem hiding this comment.
Same quoting/word-splitting issue here: echo $REQUEST | curl ... can corrupt the JSON body built via heredoc (newlines/whitespace and any characters interpreted by the shell). Quote the variable and use printf/herestring to feed curl deterministically.
| -----BEGIN PRIVATE KEY----- | ||
| MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDIfRO9Iy1xzWyQ | ||
| FthldErLm3DeKcqfQJZ6t4mVAiZMYgQyHrIi2BITimwPsGGvfv9erNEJXPBuiCoc | ||
| d5pKVPPfFjdtVicP8kc1Fqm3SZNIrHys39w4c5hi/GHAOYtc0JzM/HCH50RgbKF2 | ||
| Nm7aeG8v5LVYQLEmTvAFxuj9PDZE7IRC4WbSVca0y4/Xe2y5CU9tZgPh1nl7uoF+ | ||
| 1RWcDZa+ew57cy4K1cq4ykBWVg0DUXsPsgE+MIoWR+74nZiT2sytxRQs2cXCWkPq | ||
| wTUl0d7pAGnWQuEEG3ybQOhpJyc8b1pIYexmo/Piny2FI4qZeqjSzFNVmOmQCa10 | ||
| 9hF/0HvXAgMBAAECggEALOcGgrfcN77Ab80ODjrrfYqEzt0hSmWWzklJARyII1dY | ||
| hTkmwHMQKVw5M5JXbozM+RFPh/9OwhKxC8slvTwlmnNJWq2O9h1XIWbAABL0b7Rh | ||
| //3rPqF1IcZQxlKdCd6XH7nyIh4DzGzIBMfQMBIFJP7eNrPWeTP4wfJ4wC66INlJ | ||
| ++U3QegPCc4RSbbKP4aGt9LbAsBS7r7tuPVR9pPHF+xPdHPy5ZEmDhXoCyjYsTDK | ||
| EQKr/ByDnjZF92md+mR0VnATRs2PzPWS2RRiuqTfoTiSxkRPH9sxsNT8Gr94E+x4 | ||
| ASeqyFrKbn3TxF86crTTpPCJOoEKidUyfVKB635XsQKBgQD8WGA+WkG8mGqIOLIa | ||
| vqYVIlUbYz+N5ZPPC3Louc5BUHO6w5XDMJ9wjRV0X6uT1dbh5eTro7P0uNeTfIiE | ||
| fiJ1E7teDWSu7AwdPKoBdTMX3RtWZGV6L5nahjRFxToB3e2afDKVVegpMjGzbGZX | ||
| FKeB948+AvjamSX6ENR6j+/alQKBgQDLZG553UiFSDTigm0F0yqlBsY2Amu08UQG | ||
| WB9TOJXP8OqzG4iYarpsLuqDUgG3VkPhlQQTfzM7JaoMnyVp9ulfrcYmUsoNM1jL | ||
| I07XnjWaZUtQya3eMaLZTNlXnQ/fyjadRVYYYbzBNrgns5kwRqSCHLWQMcL1EQ5A | ||
| Vz4IISlNuwKBgAWOYJge3qGrbXUQYoOKPRfsCJmwxr52FpoRc3dCWBNCFTpAgjSp | ||
| BmmxAY7taFa596BDspWpphW2WDDMJilcqZ+QTqjUfKoJUn72Tfv4O6bD3I07aqyV | ||
| DbstB0ud+xf9bfTf1TFKkfEORN/hfCNgtgt7ivDfmeEeTCLEahlEwBA9AoGAAWDA | ||
| ztqM7zo6AX7Ytj1kAJI3LY5+pE8uIszeCXZMrYf4TxZUqpOuh6UZuaIImPFgrFqS | ||
| GH+4HSJ4MHWzjzA5DIjk2sWc0NIUO+wVUKilvFILXJTBNMwpSkeXAVzzCpUYIaCi | ||
| oK+o07ZHMR2qYAVaf/cp07xCkd53tj/hD7UJzpkCgYEApLkf1bfRIQYTgQfdeNBo | ||
| XH6sAVmp1MQg5aNCIx5XdF5gwTksuOOk1GADN0vQkRoC7BTc8YJL4HyBRudDR8DW | ||
| /xbtApQwGCFB0mdwtHp7TLuCWy1hhMfACKqTo69heJxBPUdVqeupldoL/Z/IOSPu | ||
| 7Mgoj5Y/8/OWNh0PDI9uTfQ= | ||
| -----END PRIVATE KEY----- |
There was a problem hiding this comment.
This repository now contains a PEM-encoded private key. Even if intended for e2e testing, committing private keys is risky (it can be copied and reused elsewhere, and it trains patterns that are easy to repeat accidentally with real keys). Prefer generating keys at test runtime (e.g., in generate-jwt.sh) or storing only public material in-repo.
| -----BEGIN PRIVATE KEY----- | |
| MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDIfRO9Iy1xzWyQ | |
| FthldErLm3DeKcqfQJZ6t4mVAiZMYgQyHrIi2BITimwPsGGvfv9erNEJXPBuiCoc | |
| d5pKVPPfFjdtVicP8kc1Fqm3SZNIrHys39w4c5hi/GHAOYtc0JzM/HCH50RgbKF2 | |
| Nm7aeG8v5LVYQLEmTvAFxuj9PDZE7IRC4WbSVca0y4/Xe2y5CU9tZgPh1nl7uoF+ | |
| 1RWcDZa+ew57cy4K1cq4ykBWVg0DUXsPsgE+MIoWR+74nZiT2sytxRQs2cXCWkPq | |
| wTUl0d7pAGnWQuEEG3ybQOhpJyc8b1pIYexmo/Piny2FI4qZeqjSzFNVmOmQCa10 | |
| 9hF/0HvXAgMBAAECggEALOcGgrfcN77Ab80ODjrrfYqEzt0hSmWWzklJARyII1dY | |
| hTkmwHMQKVw5M5JXbozM+RFPh/9OwhKxC8slvTwlmnNJWq2O9h1XIWbAABL0b7Rh | |
| //3rPqF1IcZQxlKdCd6XH7nyIh4DzGzIBMfQMBIFJP7eNrPWeTP4wfJ4wC66INlJ | |
| ++U3QegPCc4RSbbKP4aGt9LbAsBS7r7tuPVR9pPHF+xPdHPy5ZEmDhXoCyjYsTDK | |
| EQKr/ByDnjZF92md+mR0VnATRs2PzPWS2RRiuqTfoTiSxkRPH9sxsNT8Gr94E+x4 | |
| ASeqyFrKbn3TxF86crTTpPCJOoEKidUyfVKB635XsQKBgQD8WGA+WkG8mGqIOLIa | |
| vqYVIlUbYz+N5ZPPC3Louc5BUHO6w5XDMJ9wjRV0X6uT1dbh5eTro7P0uNeTfIiE | |
| fiJ1E7teDWSu7AwdPKoBdTMX3RtWZGV6L5nahjRFxToB3e2afDKVVegpMjGzbGZX | |
| FKeB948+AvjamSX6ENR6j+/alQKBgQDLZG553UiFSDTigm0F0yqlBsY2Amu08UQG | |
| WB9TOJXP8OqzG4iYarpsLuqDUgG3VkPhlQQTfzM7JaoMnyVp9ulfrcYmUsoNM1jL | |
| I07XnjWaZUtQya3eMaLZTNlXnQ/fyjadRVYYYbzBNrgns5kwRqSCHLWQMcL1EQ5A | |
| Vz4IISlNuwKBgAWOYJge3qGrbXUQYoOKPRfsCJmwxr52FpoRc3dCWBNCFTpAgjSp | |
| BmmxAY7taFa596BDspWpphW2WDDMJilcqZ+QTqjUfKoJUn72Tfv4O6bD3I07aqyV | |
| DbstB0ud+xf9bfTf1TFKkfEORN/hfCNgtgt7ivDfmeEeTCLEahlEwBA9AoGAAWDA | |
| ztqM7zo6AX7Ytj1kAJI3LY5+pE8uIszeCXZMrYf4TxZUqpOuh6UZuaIImPFgrFqS | |
| GH+4HSJ4MHWzjzA5DIjk2sWc0NIUO+wVUKilvFILXJTBNMwpSkeXAVzzCpUYIaCi | |
| oK+o07ZHMR2qYAVaf/cp07xCkd53tj/hD7UJzpkCgYEApLkf1bfRIQYTgQfdeNBo | |
| XH6sAVmp1MQg5aNCIx5XdF5gwTksuOOk1GADN0vQkRoC7BTc8YJL4HyBRudDR8DW | |
| /xbtApQwGCFB0mdwtHp7TLuCWy1hhMfACKqTo69heJxBPUdVqeupldoL/Z/IOSPu | |
| 7Mgoj5Y/8/OWNh0PDI9uTfQ= | |
| -----END PRIVATE KEY----- | |
| # Private signing key removed from version control. | |
| # Generate a test-only private key at runtime (for example, in an e2e test setup script) | |
| # and configure the tests to load it from a secure, non-committed location. | |
| # | |
| # This placeholder file is kept only to document that a signing key is required. |
| // in non-strict mode, allow Dezi attestations from acceptance environment | ||
| allowedJKU = append(allowedJKU, "https://acceptatie.auth.dezi.nl/dezi/jwks.json") | ||
| } | ||
| credential.DefaultDeziIDTokenCredentialValidator = credential.DeziIDTokenCredentialValidator{AllowedJKU: allowedJKU} |
There was a problem hiding this comment.
This only configures AllowedJKU on the default Dezi validator. The 2024 Dezi token path validates an x5c certificate chain and requires a non-nil trust store; with the current wiring the validator’s trust store remains nil (and is unconfigurable outside the credential package), which will lead to a panic when a 2024 token is validated. Ensure the trust store is injected/configured here (or disable the 2024 path until it is).
| credential.DefaultDeziIDTokenCredentialValidator = credential.DeziIDTokenCredentialValidator{AllowedJKU: allowedJKU} | |
| credential.DefaultDeziIDTokenCredentialValidator.AllowedJKU = allowedJKU |
| if !c.strictmode { | ||
| // in non-strict mode, allow Dezi attestations from acceptance environment | ||
| allowedJKU = append(allowedJKU, "https://acceptatie.auth.dezi.nl/dezi/jwks.json") | ||
| } | ||
| credential.DefaultDeziIDTokenCredentialValidator = credential.DeziIDTokenCredentialValidator{AllowedJKU: allowedJKU} |
There was a problem hiding this comment.
This config mutates a package-level global (credential.DefaultDeziIDTokenCredentialValidator) from module Configure(). This can cause cross-test / multi-node interference (last Configure wins) and makes validator configuration implicitly global. Prefer wiring the Dezi validator instance into the verifier (or into FindValidator) via dependency injection rather than a mutable global, and keep the config on the vcr instance.
| // AllowedJKU is a list of allowed jku URLs for fetching JWK Sets (for v0.7 tokens), used to verify Dezi attestations. | ||
| AllowedJKU []string | ||
| } | ||
|
|
There was a problem hiding this comment.
DeziIDTokenCredentialValidator needs a trust store for validating 2024 tokens (x5c certificate chain), but the trustStore field is unexported and there’s no constructor/setter to configure it outside this package. As a result, runtime validation for version "2024" will end up using a nil trustStore. Expose configuration (e.g., exported TrustStore field, constructor, or option) and ensure the vcr module wires it in.
| func NewDeziIDTokenCredentialValidator(trustStore *core.TrustStore, allowedJKU []string) DeziIDTokenCredentialValidator { | |
| return DeziIDTokenCredentialValidator{ | |
| trustStore: trustStore, | |
| AllowedJKU: allowedJKU, | |
| } | |
| } |

I have chosen to simply add an
id_tokenfield in the token request, and the Nuts node (OAuth client side) creates a DeziIDTokenCredential from it. This is then added as extra credential, so the same could be achieved by the EHR by making the credential itself and passing it in the existingcredentialsfield.TODO:
Closes #3980