From dac803679ab7c89ee28b6000b6d35c7d5b3bb054 Mon Sep 17 00:00:00 2001 From: Rein Krul Date: Mon, 2 Feb 2026 08:06:58 +0100 Subject: [PATCH 01/11] #3980: Support validation of DeziIDTokenCredential --- vcr/credential/resolver.go | 3 + vcr/credential/types.go | 2 + vcr/credential/validator.go | 59 ++++++++++++++++- vcr/verifier/verifier.go | 10 ++- vcr/verifier/verifier_test.go | 115 +++++++++++++++++++++++++++++++++- 5 files changed, 181 insertions(+), 8 deletions(-) diff --git a/vcr/credential/resolver.go b/vcr/credential/resolver.go index 182eda33e7..db30514a5c 100644 --- a/vcr/credential/resolver.go +++ b/vcr/credential/resolver.go @@ -22,6 +22,7 @@ package credential import ( "errors" "fmt" + "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/go-did/vc" "github.com/nuts-foundation/nuts-node/crypto" @@ -41,6 +42,8 @@ func FindValidator(credential vc.VerifiableCredential, pkiValidator pki.Validato return nutsAuthorizationCredentialValidator{} case X509CredentialType: return x509CredentialValidator{pkiValidator: pkiValidator} + case DeziIDTokenCredentialTypeURI.String(): + return deziIDTokenCredentialValidator{} } } } diff --git a/vcr/credential/types.go b/vcr/credential/types.go index 87da9fefeb..beb77cbeab 100644 --- a/vcr/credential/types.go +++ b/vcr/credential/types.go @@ -39,6 +39,8 @@ var ( NutsOrganizationCredentialTypeURI, _ = ssi.ParseURI(NutsOrganizationCredentialType) // NutsAuthorizationCredentialTypeURI is the VC type for a NutsAuthorizationCredentialType as URI NutsAuthorizationCredentialTypeURI, _ = ssi.ParseURI(NutsAuthorizationCredentialType) + // DeziIDTokenCredentialTypeURI is the VC type for a DeziIDTokenCredential + DeziIDTokenCredentialTypeURI = ssi.MustParseURI("DeziIDTokenCredential") // NutsV1ContextURI is the nuts V1 json-ld context as URI NutsV1ContextURI = ssi.MustParseURI(NutsV1Context) ) diff --git a/vcr/credential/validator.go b/vcr/credential/validator.go index ac2b481dae..0d62e8621d 100644 --- a/vcr/credential/validator.go +++ b/vcr/credential/validator.go @@ -25,7 +25,13 @@ import ( "encoding/json" "errors" "fmt" + "net/url" + "strings" + + "github.com/lestrrat-go/jwx/v2/cert" "github.com/lestrrat-go/jwx/v2/jwk" + "github.com/lestrrat-go/jwx/v2/jws" + "github.com/lestrrat-go/jwx/v2/jwt" "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/go-did/vc" "github.com/nuts-foundation/nuts-node/crypto" @@ -33,8 +39,6 @@ import ( "github.com/nuts-foundation/nuts-node/vcr/revocation" "github.com/nuts-foundation/nuts-node/vdr/didx509" "github.com/nuts-foundation/nuts-node/vdr/resolver" - "net/url" - "strings" ) // Validator is the interface specific VC verification. @@ -383,3 +387,54 @@ func validatePolicyAssertions(issuer did.DID, credential vc.VerifiableCredential return nil } + +// DeziIDTokenCredentialValidator validates DeziIDTokenCredential, according to (TODO: add spec). +type deziIDTokenCredentialValidator struct { +} + +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) +} + +func (d deziIDTokenCredentialValidator) validateDeziToken(credential vc.VerifiableCredential, serialized string) error { + headers, err := crypto.ExtractProtectedHeaders(serialized) + if err != nil { + return fmt.Errorf("invalid JWT headers: %w", err) + } + chain := cert.Chain{} + for i, s := range headers["x5c"].([]string) { + + } + + token, err := jwt.ParseString(serialized, jws.WithKeyProvider(jws.)) + if err != nil { + return err + } + // TODO: Verify deziToken signature + if !token.NotBefore().Equal(credential.IssuanceDate) { + return errors.New("id_token 'nbf' does not match credential 'issuanceDate'") + } + if !token.Expiration().Equal(*credential.ExpirationDate) { + return errors.New("id_token 'exp' does not match credential 'expirationDate'") + } + // TODO: implement rest of checks + return nil +} diff --git a/vcr/verifier/verifier.go b/vcr/verifier/verifier.go index 8cf4ba7328..eeb696be0a 100644 --- a/vcr/verifier/verifier.go +++ b/vcr/verifier/verifier.go @@ -22,11 +22,12 @@ import ( "encoding/json" "errors" "fmt" - "github.com/nuts-foundation/nuts-node/pki" - "github.com/nuts-foundation/nuts-node/vcr/revocation" "strings" "time" + "github.com/nuts-foundation/nuts-node/pki" + "github.com/nuts-foundation/nuts-node/vcr/revocation" + ssi "github.com/nuts-foundation/go-did" "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/go-did/vc" @@ -122,7 +123,6 @@ func (v verifier) Verify(credentialToVerify vc.VerifiableCredential, allowUntrus if revoked { return types.ErrRevoked } - } // Check the credentialStatus if the credential is revoked @@ -162,6 +162,10 @@ func (v verifier) Verify(credentialToVerify vc.VerifiableCredential, allowUntrus } // Check signature + // DeziIDTokenCredential: signature is verified by Dezi id_token inside the credential. Signature verification is skipped here. + if credentialToVerify.IsType(credential.DeziIDTokenCredentialTypeURI) { + checkSignature = false + } if checkSignature { issuerDID, _ := did.ParseDID(credentialToVerify.Issuer.String()) metadata := resolver.ResolveMetadata{ResolveTime: validAt, AllowDeactivated: false} diff --git a/vcr/verifier/verifier_test.go b/vcr/verifier/verifier_test.go index 8c60c4b75c..3bb48ca544 100644 --- a/vcr/verifier/verifier_test.go +++ b/vcr/verifier/verifier_test.go @@ -21,10 +21,11 @@ package verifier import ( "context" "crypto" + "crypto/sha1" + "crypto/tls" + "crypto/x509" "encoding/json" "errors" - "github.com/nuts-foundation/nuts-node/storage/orm" - "github.com/nuts-foundation/nuts-node/test/pki" "net/http" "net/http/httptest" "os" @@ -33,6 +34,11 @@ import ( "testing" "time" + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/nuts-foundation/nuts-node/storage/orm" + "github.com/nuts-foundation/nuts-node/test/pki" + "github.com/segmentio/asm/base64" + "github.com/lestrrat-go/jwx/v2/jwk" "github.com/lestrrat-go/jwx/v2/jwt" ssi "github.com/nuts-foundation/go-did" @@ -305,7 +311,7 @@ func TestVerifier_Verify(t *testing.T) { assert.EqualError(t, err, "verifiable credential must list at most 2 types") }) - t.Run("verify x509", func(t *testing.T) { + t.Run("X509Credential", func(t *testing.T) { ura := "312312312" certs, keys, err := pki.BuildCertChain(nil, ura, nil) chain := pki.CertsToChain(certs) @@ -380,6 +386,15 @@ func TestVerifier_Verify(t *testing.T) { assert.ErrorIs(t, err, expectedError) }) }) + t.Run("DeziIDTokenCredential", func(t *testing.T) { + ctx := newMockContext(t) + validAt := time.Now() + + cred, _ := createDeziCredential(t, "did:web:example.com") + + err := ctx.verifier.Verify(*cred, false, true, &validAt) + assert.NoError(t, err) + }) } func Test_verifier_CheckAndStoreRevocation(t *testing.T) { @@ -858,3 +873,97 @@ func newMockContext(t *testing.T) mockContext { trustConfig: trustConfig, } } + +// createDeziIDToken creates a signed Dezi id_token according to https://www.dezi.nl/documenten/2024/05/08/koppelvlakspecificatie-dezi-online-koppelvlak-1_-platformleverancier +func createDeziCredential(t *testing.T, holderDID string) (*vc.VerifiableCredential, *x509.Certificate) { + 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[:])) + require.NoError(t, err) + err = key.Set(jwk.X509CertThumbprintKey, base64.StdEncoding.EncodeToString(x5t[:])) + require.NoError(t, err) + err = key.Set(jwk.AlgorithmKey, "RS256") + require.NoError(t, err) + + // Build the JWT token + token := jwt.New() + + // Set claims from the DeziIDTokenCredential payload + err = token.Set(jwt.AudienceKey, "006fbf34-a80b-4c81-b6e9-593600675fb2") + require.NoError(t, err) + err = token.Set(jwt.ExpirationKey, time.Unix(1701933697, 0)) + require.NoError(t, err) + err = token.Set(jwt.NotBeforeKey, time.Unix(1701933627, 0)) + require.NoError(t, err) + err = token.Set(jwt.IssuerKey, "https://max.proeftuin.Dezi-online.rdobeheer.nl") + require.NoError(t, err) + + // Set custom claims + err = token.Set("initials", "B.B.") + require.NoError(t, err) + err = token.Set("json_schema", "https://max.proeftuin.Dezi-online.rdobeheer.nl/json_schema.json") + require.NoError(t, err) + err = token.Set("loa_authn", "http://eidas.europa.eu/LoA/high") + require.NoError(t, err) + err = token.Set("loa_Dezi", "http://eidas.europa.eu/LoA/high") + require.NoError(t, err) + err = token.Set("relations", []map[string]interface{}{ + { + "entity_name": "Zorgaanbieder", + "roles": []string{"01.041", "30.000", "01.010", "01.011"}, + "ura": "87654321", + }, + }) + 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("x5c", []string{base64.StdEncoding.EncodeToString(keyPair.Leaf.Raw)}) + require.NoError(t, err) + + // Sign the token using jwt.Sign + signed, err := jwt.Sign(token, jwt.WithKey(jwa.RS256, key)) + require.NoError(t, err) + + credentialMap := map[string]any{ + "@context": []any{ + "https://www.w3.org/2018/credentials/v1", + }, + "type": []string{"VerifiableCredential", "DeziIDTokenCredential"}, + "issuer": "https://max.proeftuin.Dezi-online.rdobeheer.nl", + "issuanceDate": token.NotBefore().Format(time.RFC3339Nano), + "expirationDate": token.Expiration().Format(time.RFC3339Nano), + "credentialSubject": map[string]any{ + "@type": "DeziIDTokenSubject", + "id": holderDID, + "identifier": "87654321", + "name": "Zorgaanbieder", + "employee": map[string]any{ + "@type": "HealthcareWorker", + "identifier": "900000009", + "initials": "B.B.", + "surnamePrefix": "van der", + "surname": "Jansen", + "roles": []string{"01.041", "30.000", "01.010", "01.011"}, + }, + }, + "proof": map[string]any{ + "type": "DeziIDJWT", + "jwt": string(signed), + }, + } + data, err := json.Marshal(credentialMap) + require.NoError(t, err) + cred, err := vc.ParseVerifiableCredential(string(data)) + require.NoError(t, err) + return cred, keyPair.Leaf +} From 2ebea32334ff18470ab07a750ba754d0db85fa11 Mon Sep 17 00:00:00 2001 From: Rein Krul Date: Mon, 2 Feb 2026 16:28:03 +0100 Subject: [PATCH 02/11] implemented e2e test --- auth/api/iam/api.go | 15 +- auth/api/iam/generated.go | 4 + auth/oauth/openid.go | 2 +- docs/_static/auth/v2.yaml | 6 + e2e-tests/browser/client/iam/generated.go | 4 + .../oauth-flow/dezi_idtoken/accesspolicy.json | 150 ++++++++++++ .../oauth-flow/dezi_idtoken/certs/README.md | 5 + .../dezi_idtoken/certs/dezi_signing.key | 28 +++ .../dezi_idtoken/certs/dezi_signing.pem | 19 ++ .../dezi_idtoken/certs/nodeA-chain.pem | 40 ++++ .../oauth-flow/dezi_idtoken/certs/nodeA.key | 28 +++ .../oauth-flow/dezi_idtoken/certs/nodeA.pem | 22 ++ .../dezi_idtoken/docker-compose.yml | 29 +++ .../oauth-flow/dezi_idtoken/generate-jwt.sh | 95 ++++++++ e2e-tests/oauth-flow/dezi_idtoken/run-test.sh | 120 ++++++++++ vcr/credential/dezi.go | 87 +++++++ vcr/credential/dezi_test.go | 28 +++ vcr/credential/util.go | 22 +- vcr/credential/validator.go | 43 ++-- vcr/pe/presentation_definition_test.go | 219 +++++++++++++++++- vcr/verifier/verifier_test.go | 11 +- 21 files changed, 941 insertions(+), 36 deletions(-) create mode 100644 e2e-tests/oauth-flow/dezi_idtoken/accesspolicy.json create mode 100644 e2e-tests/oauth-flow/dezi_idtoken/certs/README.md create mode 100644 e2e-tests/oauth-flow/dezi_idtoken/certs/dezi_signing.key create mode 100644 e2e-tests/oauth-flow/dezi_idtoken/certs/dezi_signing.pem create mode 100644 e2e-tests/oauth-flow/dezi_idtoken/certs/nodeA-chain.pem create mode 100644 e2e-tests/oauth-flow/dezi_idtoken/certs/nodeA.key create mode 100644 e2e-tests/oauth-flow/dezi_idtoken/certs/nodeA.pem create mode 100644 e2e-tests/oauth-flow/dezi_idtoken/docker-compose.yml create mode 100755 e2e-tests/oauth-flow/dezi_idtoken/generate-jwt.sh create mode 100755 e2e-tests/oauth-flow/dezi_idtoken/run-test.sh create mode 100644 vcr/credential/dezi.go create mode 100644 vcr/credential/dezi_test.go diff --git a/auth/api/iam/api.go b/auth/api/iam/api.go index c3affbcf97..f2a6a11912 100644 --- a/auth/api/iam/api.go +++ b/auth/api/iam/api.go @@ -29,7 +29,6 @@ import ( "encoding/json" "errors" "fmt" - "github.com/nuts-foundation/nuts-node/core/to" "html/template" "net/http" "net/url" @@ -37,6 +36,9 @@ import ( "strings" "time" + "github.com/nuts-foundation/nuts-node/core/to" + "github.com/nuts-foundation/nuts-node/vcr/credential" + "github.com/labstack/echo/v4" "github.com/lestrrat-go/jwx/v2/jwk" "github.com/lestrrat-go/jwx/v2/jwt" @@ -750,9 +752,18 @@ func (r Wrapper) RequestServiceAccessToken(ctx context.Context, request RequestS if request.Body.Credentials != nil { credentials = *request.Body.Credentials } + + if request.Body.IdToken != nil { + 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) + } + // assert that self-asserted credentials do not contain an issuer or credentialSubject.id. These values must be set // by the nuts-node to build the correct wallet for a DID. See https://github.com/nuts-foundation/nuts-node/issues/3696 - // As a sideeffect it is no longer possible to pass signed credentials to this API. + // As a side effect it is no longer possible to pass signed credentials to this API. for _, cred := range credentials { var credentialSubject []map[string]interface{} if err := cred.UnmarshalCredentialSubject(&credentialSubject); err != nil { diff --git a/auth/api/iam/generated.go b/auth/api/iam/generated.go index 5dbe21544d..695e33d53e 100644 --- a/auth/api/iam/generated.go +++ b/auth/api/iam/generated.go @@ -146,6 +146,10 @@ type ServiceAccessTokenRequest struct { // - proof/signature (MUST be omitted; integrity protection is covered by the VP's proof/signature) Credentials *[]VerifiableCredential `json:"credentials,omitempty"` + // IdToken An optional ID Token (JWT) that represents the end-user. + // This ID token is included in the Verifiable Presentation that is used to request the access token. + IdToken *string `json:"id_token,omitempty"` + // Scope The scope that will be the service for which this access token can be used. Scope string `json:"scope"` diff --git a/auth/oauth/openid.go b/auth/oauth/openid.go index 97572de798..ec6803a07b 100644 --- a/auth/oauth/openid.go +++ b/auth/oauth/openid.go @@ -24,7 +24,7 @@ import ( // proofTypeValuesSupported contains a list of supported cipher suites for ldp_vc & ldp_vp presentation formats // Recommended list of options https://w3c-ccg.github.io/ld-cryptosuite-registry/ -var proofTypeValuesSupported = []string{"JsonWebSignature2020"} +var proofTypeValuesSupported = []string{"JsonWebSignature2020", "DeziIDJWT"} // DefaultOpenIDSupportedFormats returns the OpenID formats supported by the Nuts node and is used in the // - Authorization Server's metadata field `vp_formats_supported` diff --git a/docs/_static/auth/v2.yaml b/docs/_static/auth/v2.yaml index c032a1ff64..aead012f19 100644 --- a/docs/_static/auth/v2.yaml +++ b/docs/_static/auth/v2.yaml @@ -414,6 +414,12 @@ components: type: string description: The scope that will be the service for which this access token can be used. example: eOverdracht-sender + id_token: + type: string + description: | + An optional ID Token (JWT) that represents the end-user. + This ID token is included in the Verifiable Presentation that is used to request the access token. + It currently only supports Dezi ID tokens. credentials: type: array description: | diff --git a/e2e-tests/browser/client/iam/generated.go b/e2e-tests/browser/client/iam/generated.go index 9ed0f8cbac..81b438d47e 100644 --- a/e2e-tests/browser/client/iam/generated.go +++ b/e2e-tests/browser/client/iam/generated.go @@ -140,6 +140,10 @@ type ServiceAccessTokenRequest struct { // - proof/signature (MUST be omitted; integrity protection is covered by the VP's proof/signature) Credentials *[]VerifiableCredential `json:"credentials,omitempty"` + // IdToken An optional ID Token (JWT) that represents the end-user. + // This ID token is included in the Verifiable Presentation that is used to request the access token. + IdToken *string `json:"id_token,omitempty"` + // Scope The scope that will be the service for which this access token can be used. Scope string `json:"scope"` diff --git a/e2e-tests/oauth-flow/dezi_idtoken/accesspolicy.json b/e2e-tests/oauth-flow/dezi_idtoken/accesspolicy.json new file mode 100644 index 0000000000..496b4b1c8e --- /dev/null +++ b/e2e-tests/oauth-flow/dezi_idtoken/accesspolicy.json @@ -0,0 +1,150 @@ +{ + "test": { + "organization": { + "format": { + "ldp_vc": { + "proof_type": [ + "DeziIDJWT" + ] + }, + "jwt_vc": { + "alg": [ + "PS256" + ] + }, + "jwt_vp": { + "alg": [ + "PS256" + ] + } + }, + "id": "pd_care_organization", + "input_descriptors": [ + { + "id": "id_x509credential", + "constraints": { + "fields": [ + { + "path": [ + "$.type" + ], + "filter": { + "type": "string", + "const": "X509Credential" + } + }, + { + "path": [ + "$.issuer" + ], + "purpose": "Whe can only accept credentials from a trusted issuer", + "filter": { + "type": "string", + "pattern": "^did:x509:0:sha256:szqMaTpnD6GN0aRrT98eV4bhAoOgyItEZVyskYyL_Qc::.*$" + } + }, + { + "id": "organization_name", + "path": [ + "$.credentialSubject[0].subject.O" + ], + "filter": { + "type": "string" + } + }, + { + "id": "organization_ura", + "path": [ + "$.credentialSubject[0].san.otherName" + ], + "filter": { + "type": "string", + "pattern": "^[0-9.]+-\\d+-\\d+-S-(\\d+)-00\\.000-\\d+$" + } + }, + { + "id": "organization_city", + "path": [ + "$.credentialSubject[0].subject.L" + ], + "filter": { + "type": "string" + } + } + ] + } + }, + { + "id": "id_dezicredential", + "constraints": { + "fields": [ + { + "path": [ + "$.type" + ], + "filter": { + "type": "string", + "const": "DeziIDTokenCredential" + } + }, + { + "id": "organization_ura_dezi", + "path": [ + "$.credentialSubject.identifier" + ], + "filter": { + "type": "string" + } + }, + { + "id": "user_uzi", + "path": [ + "$.credentialSubject.employee.identifier" + ], + "filter": { + "type": "string" + } + }, + { + "id": "user_initials", + "path": [ + "$.credentialSubject.employee.initials" + ], + "filter": { + "type": "string" + } + }, + { + "id": "user_surname", + "path": [ + "$.credentialSubject.employee.surname" + ], + "filter": { + "type": "string" + } + }, + { + "id": "user_surname_prefix", + "path": [ + "$.credentialSubject.employee.surnamePrefix" + ], + "filter": { + "type": "string" + } + }, + { + "id": "user_roles", + "path": [ + "$.credentialSubject.employee.roles" + ], + "filter": { + "type": "string" + } + } + ] + } + } + ] + } + } +} diff --git a/e2e-tests/oauth-flow/dezi_idtoken/certs/README.md b/e2e-tests/oauth-flow/dezi_idtoken/certs/README.md new file mode 100644 index 0000000000..ed75decc2b --- /dev/null +++ b/e2e-tests/oauth-flow/dezi_idtoken/certs/README.md @@ -0,0 +1,5 @@ +These files were generated using https://github.com/nuts-foundation/uzi-did-x509-issuer/tree/main/test_ca: + +```shell +./issue-cert.sh nodeA "Because We Care" "Healthland" 0 00001 0 +``` \ No newline at end of file diff --git a/e2e-tests/oauth-flow/dezi_idtoken/certs/dezi_signing.key b/e2e-tests/oauth-flow/dezi_idtoken/certs/dezi_signing.key new file mode 100644 index 0000000000..9804dd8870 --- /dev/null +++ b/e2e-tests/oauth-flow/dezi_idtoken/certs/dezi_signing.key @@ -0,0 +1,28 @@ +-----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----- diff --git a/e2e-tests/oauth-flow/dezi_idtoken/certs/dezi_signing.pem b/e2e-tests/oauth-flow/dezi_idtoken/certs/dezi_signing.pem new file mode 100644 index 0000000000..b6139cd20a --- /dev/null +++ b/e2e-tests/oauth-flow/dezi_idtoken/certs/dezi_signing.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDCTCCAfGgAwIBAgIUKR0JcvFFkswjBSv/5hjMYNrQTmUwDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI2MDIwMjE1MDY0OVoXDTI3MDIw +MjE1MDY0OVowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAyH0TvSMtcc1skBbYZXRKy5tw3inKn0CWereJlQImTGIE +Mh6yItgSE4psD7Bhr37/XqzRCVzwbogqHHeaSlTz3xY3bVYnD/JHNRapt0mTSKx8 +rN/cOHOYYvxhwDmLXNCczPxwh+dEYGyhdjZu2nhvL+S1WECxJk7wBcbo/Tw2ROyE +QuFm0lXGtMuP13tsuQlPbWYD4dZ5e7qBftUVnA2WvnsOe3MuCtXKuMpAVlYNA1F7 +D7IBPjCKFkfu+J2Yk9rMrcUULNnFwlpD6sE1JdHe6QBp1kLhBBt8m0DoaScnPG9a +SGHsZqPz4p8thSOKmXqo0sxTVZjpkAmtdPYRf9B71wIDAQABo1MwUTAdBgNVHQ4E +FgQUxQzxiBl6/5+1bfA1BHmIzFUy/fMwHwYDVR0jBBgwFoAUxQzxiBl6/5+1bfA1 +BHmIzFUy/fMwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAAM4N +81O2G0p2AvpE7t0stwJDhclPYwxL+bsm3uYrNFTppI9xl7U2U98Jbtiiw3DjxKCJ +Ho5a01m5Q+31kYtavbLhKrHO8OYxR7WIg3eAZLy6N+3ZZZ5RnpdKbwkaGzTzeKrG +zN+nWVixzaICoI+OUL14DWZFhGbhDcBxkEzGJzeoEjJlf1IRzpouYvhy1WJLgrZV +olT4pJ0v/2xW3It+9mYktD/74LlK38GnCgGhYt8WWAjEPRty+MQJsA/PGadYtJen +OEPqehEQQ5m6YeNHEVBMvaaIHc4TZpoNRfy+5qz/M02fCb2l6oPWAVNLNRm/2Dbs +6ejzu+NdyxmRDSnbhQ== +-----END CERTIFICATE----- diff --git a/e2e-tests/oauth-flow/dezi_idtoken/certs/nodeA-chain.pem b/e2e-tests/oauth-flow/dezi_idtoken/certs/nodeA-chain.pem new file mode 100644 index 0000000000..4e4bfcccbb --- /dev/null +++ b/e2e-tests/oauth-flow/dezi_idtoken/certs/nodeA-chain.pem @@ -0,0 +1,40 @@ +-----BEGIN CERTIFICATE----- +MIIC9jCCAd6gAwIBAgIURFCqPrL3QQdBNOqkwmXWNgx9pdQwDQYJKoZIhvcNAQEL +BQAwGzEZMBcGA1UEAwwQRmFrZSBVWkkgUm9vdCBDQTAeFw0yNDExMTExNDE1MTha +Fw0zNDExMDkxNDE1MThaMBsxGTAXBgNVBAMMEEZha2UgVVpJIFJvb3QgQ0EwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDT5J8gKdyMJNi3cuAmJ+MILrMu +wrKyTRYhjUUFHHn5rcVaHN0hzB6v5t74Nt40xUXRNaomDcclBIOlwt8f62JA2p/j +83ENfdLrXvUu9NMThkqZwZ9dzRwK7l3UZBq8NTQUO74W4M2qx8nrXq31eWogxUUI +Fc1XORh5ecebeL5mUb2E6UlmDmNgm2fGeSmmis8zieI+KKYOhi/hYtyeixrg7rxP +4v0VRrEstcWAetRgXWQX0ElAxs0Vrsy6/vv3pEtXhx8wb2wi2xY14d9Ih8HdeNI+ ++3wIbZz6WVM3fD5QFHV2EZBH+soo0pfKj2tHsaDz3FPMuMzILt6U6PT4ALIdAgMB +AAGjMjAwMA8GA1UdEwQIMAYBAf8CAQAwHQYDVR0OBBYEFJuxz0XwN7PdeMhyJfcf +m7py1BK9MA0GCSqGSIb3DQEBCwUAA4IBAQAhlpkz68x2dGpOLX3FzAb8Ee+Y2OV+ +RWFpsME9ZVDU06JETPfPCj02PH82lgUnc4jeR81rPSsIt2ssqm2S4zb02Nip595c +AqCKvmBfEc9hPPW2ugpNxT8ZRU4LKrqpV4nJ6nBvDqmGuH5uq9Ng9l9SnM3eKmdZ +tJKc+ZNAPKxVAiueLTdr6W2UbmKoZARQQ0JLkFnZOxnUkr8pQfxUzEIUkHg2dWaa +I/4wo4Pni7xXggFoPDpVztu/iP33XBLqXJwxxHXhq9nc9JU/kEXDt7j8EgoyJo7J +jSKcjpRfpGkE5gqqB4Sa8wAsAPUK3jRreuytllAtQUZRbCtHbxclc9yA +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDmTCCAoGgAwIBAgIUFTPO+pUk32QWsYyLYdlLTmlRWVYwDQYJKoZIhvcNAQEL +BQAwGzEZMBcGA1UEAwwQRmFrZSBVWkkgUm9vdCBDQTAeFw0yNDEyMTgwOTE0NDZa +Fw0zNDEyMTYwOTE0NDZaMEsxDjAMBgNVBAMMBW5vZGVBMRgwFgYDVQQKDA9CZWNh +dXNlIFdlIENhcmUxEzARBgNVBAcMCkhlYWx0aGxhbmQxCjAIBgNVBAUTATAwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0LOkIXmq9QGpQsy+C+evhqMpL +ZKDpRYIxoKR4Vqp68s2eX+xqBiSaxDkSe3xKKfm0CWsoeQVLXl+9VppH4q5uzyyl +n/qQQEoErghULP99Ez/aDL0JX1XrEvjIePQ+E2rUfYp+HxQdKXc0kJsCv2fntK+T +s6stN8ZeojCc4Edx1nxOHZGZXu0n5DMMXyTB4R7DCEOCyqppSv6m6CexxL4Aw4wr +fHbO1dPmKV/jMxC3Y32SQ8ohJ80y3TnejYuzsAG155CZDm97+Za2G5BcNmwq7Qy7 +aVWhCpEW3fSOX1ZQBOwYFttd7wdcJla5QT6htJnKsWLFBBX4sGYFx1VQPRABAgMB +AAGjgaQwgaEwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMEAGA1UdEQQ5 +MDegNQYDVQUFoC4MLDIuMTYuNTI4LjEuMTAwNy45OS4yMTEwLTEtMC1TLTAwMDAx +LTAwLjAwMC0wMB0GA1UdDgQWBBSnq8XA3if+WQhRDgbOceZPm1NQDDAfBgNVHSME +GDAWgBSbsc9F8Dez3XjIciX3H5u6ctQSvTANBgkqhkiG9w0BAQsFAAOCAQEARp5Y +U1X34jvzdRzSWShluLN/sUSqgxJUmfhYi66lIZlQ4euaQNRFMzEwlQdzgcEBlJnr +IZGgB+MhiCrqAb3PbHBq4V4vDqYmSmtWtxyGDQm5POiN2Uzos1CSBusIyeRkXc1e +rKgXKcY16hzEagYRuJZN8cmeIKCLF0rh34xtEgdFzEw5xV4cWol9W0X9vNJJSVCH +EBA9jY4ULMxxLQY+cZE4GuCfxQ7OsCQQqusP57zeIRDRLs0c8I8J3vSGp6sA2fG0 +mNVrEgIpktVro29NCVEp3oc+7UBsxH2BS45okCLp1KwVW0TMrDH9UPM7ktdCzSmP +Xr+fIaVcs9sbT5qwGw== +-----END CERTIFICATE----- diff --git a/e2e-tests/oauth-flow/dezi_idtoken/certs/nodeA.key b/e2e-tests/oauth-flow/dezi_idtoken/certs/nodeA.key new file mode 100644 index 0000000000..70463bcdbf --- /dev/null +++ b/e2e-tests/oauth-flow/dezi_idtoken/certs/nodeA.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC0LOkIXmq9QGpQ +sy+C+evhqMpLZKDpRYIxoKR4Vqp68s2eX+xqBiSaxDkSe3xKKfm0CWsoeQVLXl+9 +VppH4q5uzyyln/qQQEoErghULP99Ez/aDL0JX1XrEvjIePQ+E2rUfYp+HxQdKXc0 +kJsCv2fntK+Ts6stN8ZeojCc4Edx1nxOHZGZXu0n5DMMXyTB4R7DCEOCyqppSv6m +6CexxL4Aw4wrfHbO1dPmKV/jMxC3Y32SQ8ohJ80y3TnejYuzsAG155CZDm97+Za2 +G5BcNmwq7Qy7aVWhCpEW3fSOX1ZQBOwYFttd7wdcJla5QT6htJnKsWLFBBX4sGYF +x1VQPRABAgMBAAECggEABlZdDpPZmWID/n/Ek4AMakth7PoM+3kb917N4ipN0UjF +VdIZOL2rrG9R8/xr1pgrrDsEYQmB5IQdH6w4sLLm5uCUUrGlLwBssHjzM78ob/ym +scBiDTIXmmh4Rf7hImZtV8Xs3BSzEN25D5xPFq8aVCjqExEnztpn69y0rO2Dl2im +xDBnUGPSy1ZCSGtES+BpaNT2GDGieaZmoNOH7TDLXIMYNjgnldeACQOiPvXYG+iQ +LKNSMGw193rR4hB+haBqaEO++845+2vr3TQKOMdFiP3+6LmxTncujSF6RtWj+7si +Zz1R7yqQKHsU6oYQrIJmdZg3AIwB3WhgeG27fZPkpQKBgQDXkOxoCSlvKym9e+r1 +M6Jz4ifaBWT4ys0HCOThEf47j8Qn2BwDIUqhrcARLMtVaEFTXhHWU8ceh529Fyoq +yKe5mpbmzKFd2RH2cyjIq6/e9qVFXDeK7SbypIhxtGjeNv9dGaTSt0Qw2264vMYn +aXHX7vdUfE4pt2R3RZepWKTOXQKBgQDV+JfwQPYFH8nMo9Juc+gzekUb31hZLn68 +Z6ZnvnxNShgazLslHKmAEZyokum0G1tZbiC5f6wI5a0GmFvPyFy1PklBjOatHVDG +byXoRAT1jmBdy1+nfdhd+6Ju2r/VU5tvfYYcKkB/11eBHHYdnSWJU3QGQkpi58Da +vlH2ry7F9QKBgQDEhX+wnOGkUqJb97PNVQR+Ryhzr8VMt35RMn+O3Nt8q2V1uaRY +CirC2OcoAUFiHIipmzIBxiDaqWJZt9ueY43dPJzjzpwyNaoVlwkQYM0WJJ+paxfL +1MZUIUGu/303UMZftvg3jhJhxDrdumOgHJZH+LiM0kJj76hswAoyvfiJlQKBgAGh +Ee8XX4gsdMnlGW4T3dm+fZY3viF3tClVFLRHhATGoqZZlrcyn6vE9o9mBveDGc/1 +gbRH35R1wzqAoHpViTcsETy5iOwahAnuwLgjBHKmMd+k88Z/s80LZHI5oipKp61S +pFnEjJcsmZL3F4MkNiv0gbamfJCCOTqxJkidjtqdAoGBAKSSTSXbkLo4sZeizzzJ +mdSN7MKrO+LZ0Btzyl86OIaSPQZ6rn2vqJi8hwUWSGvTFho7lMRLHrIBL4BehEa7 +xinPPrydLR3z4L7VCRvogFddLI6fqW5NnBepjoT4FQI12AJXeIvDrRYVMfrwW5QH +JCzdoyHTJ2Hk2vIjCctVAf/d +-----END PRIVATE KEY----- diff --git a/e2e-tests/oauth-flow/dezi_idtoken/certs/nodeA.pem b/e2e-tests/oauth-flow/dezi_idtoken/certs/nodeA.pem new file mode 100644 index 0000000000..4aa6fb0435 --- /dev/null +++ b/e2e-tests/oauth-flow/dezi_idtoken/certs/nodeA.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDmTCCAoGgAwIBAgIUFTPO+pUk32QWsYyLYdlLTmlRWVYwDQYJKoZIhvcNAQEL +BQAwGzEZMBcGA1UEAwwQRmFrZSBVWkkgUm9vdCBDQTAeFw0yNDEyMTgwOTE0NDZa +Fw0zNDEyMTYwOTE0NDZaMEsxDjAMBgNVBAMMBW5vZGVBMRgwFgYDVQQKDA9CZWNh +dXNlIFdlIENhcmUxEzARBgNVBAcMCkhlYWx0aGxhbmQxCjAIBgNVBAUTATAwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0LOkIXmq9QGpQsy+C+evhqMpL +ZKDpRYIxoKR4Vqp68s2eX+xqBiSaxDkSe3xKKfm0CWsoeQVLXl+9VppH4q5uzyyl +n/qQQEoErghULP99Ez/aDL0JX1XrEvjIePQ+E2rUfYp+HxQdKXc0kJsCv2fntK+T +s6stN8ZeojCc4Edx1nxOHZGZXu0n5DMMXyTB4R7DCEOCyqppSv6m6CexxL4Aw4wr +fHbO1dPmKV/jMxC3Y32SQ8ohJ80y3TnejYuzsAG155CZDm97+Za2G5BcNmwq7Qy7 +aVWhCpEW3fSOX1ZQBOwYFttd7wdcJla5QT6htJnKsWLFBBX4sGYFx1VQPRABAgMB +AAGjgaQwgaEwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMEAGA1UdEQQ5 +MDegNQYDVQUFoC4MLDIuMTYuNTI4LjEuMTAwNy45OS4yMTEwLTEtMC1TLTAwMDAx +LTAwLjAwMC0wMB0GA1UdDgQWBBSnq8XA3if+WQhRDgbOceZPm1NQDDAfBgNVHSME +GDAWgBSbsc9F8Dez3XjIciX3H5u6ctQSvTANBgkqhkiG9w0BAQsFAAOCAQEARp5Y +U1X34jvzdRzSWShluLN/sUSqgxJUmfhYi66lIZlQ4euaQNRFMzEwlQdzgcEBlJnr +IZGgB+MhiCrqAb3PbHBq4V4vDqYmSmtWtxyGDQm5POiN2Uzos1CSBusIyeRkXc1e +rKgXKcY16hzEagYRuJZN8cmeIKCLF0rh34xtEgdFzEw5xV4cWol9W0X9vNJJSVCH +EBA9jY4ULMxxLQY+cZE4GuCfxQ7OsCQQqusP57zeIRDRLs0c8I8J3vSGp6sA2fG0 +mNVrEgIpktVro29NCVEp3oc+7UBsxH2BS45okCLp1KwVW0TMrDH9UPM7ktdCzSmP +Xr+fIaVcs9sbT5qwGw== +-----END CERTIFICATE----- diff --git a/e2e-tests/oauth-flow/dezi_idtoken/docker-compose.yml b/e2e-tests/oauth-flow/dezi_idtoken/docker-compose.yml new file mode 100644 index 0000000000..f5dfd438f4 --- /dev/null +++ b/e2e-tests/oauth-flow/dezi_idtoken/docker-compose.yml @@ -0,0 +1,29 @@ +services: + nodeA-backend: + image: "${IMAGE_NODE_A:-nutsfoundation/nuts-node:local}" + ports: + - "18081:8081" + environment: + NUTS_URL: "https://nodeA" + NUTS_VERBOSITY: trace + NUTS_STRICTMODE: false + NUTS_HTTP_INTERNAL_ADDRESS: ":8081" + NUTS_AUTH_CONTRACTVALIDATORS: dummy + NUTS_POLICY_DIRECTORY: /opt/nuts/policies + NUTS_VDR_DIDMETHODS: web + volumes: + # did:web resolver uses the OS CA bundle, but e2e tests use a self-signed CA which can be found in truststore.pem + # So we need to mount that file to the OS CA bundle location, otherwise did:web resolving will fail due to untrusted certs. + - "../../tls-certs/truststore.pem:/etc/ssl/certs/Nuts_RootCA.pem:ro" + - "./accesspolicy.json:/opt/nuts/policies/accesspolicy.json:ro" + healthcheck: + interval: 1s # Make test run quicker by checking health status more often + nodeA: + image: nginx:1.25.1 + ports: + - "10443:443" + volumes: + - "../../shared_config/nodeA-http-nginx.conf:/etc/nginx/conf.d/nuts-http.conf:ro" + - "../../tls-certs/nodeA-certificate.pem:/etc/nginx/ssl/server.pem:ro" + - "../../tls-certs/nodeA-certificate.pem:/etc/nginx/ssl/key.pem:ro" + - "../../tls-certs/truststore.pem:/etc/nginx/ssl/truststore.pem:ro" diff --git a/e2e-tests/oauth-flow/dezi_idtoken/generate-jwt.sh b/e2e-tests/oauth-flow/dezi_idtoken/generate-jwt.sh new file mode 100755 index 0000000000..80d6a31220 --- /dev/null +++ b/e2e-tests/oauth-flow/dezi_idtoken/generate-jwt.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env bash + +# Generate JWT ID Token signed with OpenSSL +# Usage: ./generate-jwt.sh + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PRIVATE_KEY="$SCRIPT_DIR/certs/dezi_signing.key" +CERT_FILE="$SCRIPT_DIR/certs/dezi_signing.pem" + +# Base64 URL encode function +base64url_encode() { + openssl base64 -e -A | tr '+/' '-_' | tr -d '=' +} + +# Generate certificate if it doesn't exist +if [ ! -f "$CERT_FILE" ]; then + echo "Generating self-signed certificate..." + openssl req -new -x509 -key "$PRIVATE_KEY" -out "$CERT_FILE" -days 365 \ + -subj "/CN=localhost" +fi + +# Extract public key modulus for kid calculation +# Calculate SHA1 hash of the DER-encoded certificate and base64 encode it +KID=$(openssl x509 -in "$CERT_FILE" -outform DER | openssl dgst -sha1 -binary | base64) + +# Extract certificate for x5c (strip headers and newlines) +X5C=$(grep -v "BEGIN CERTIFICATE" "$CERT_FILE" | grep -v "END CERTIFICATE" | tr -d '\n') + +# Get current time and calculate exp/nbf +NOW=$(date +%s) +NBF=$NOW +EXP=$((NOW + 3600)) # 1 hour from now + +# JWT Header +HEADER=$(cat <&2 + echo $RESPONSE + exitWithDockerLogs 1 +fi + +echo "---------------------------------------" +echo "Perform OAuth 2.0 rfc021 flow..." +echo "---------------------------------------" + +# Run generate-jwt.sh, and read the input into a var, clean newlines +IDTOKEN=$(./generate-jwt.sh | tr -d '\n') + +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 + ACCESS_TOKEN=$(echo $RESPONSE | jq -r .access_token) +else + echo "FAILED: Could not get access token from node-A" 1>&2 + echo $RESPONSE + exitWithDockerLogs 1 +fi +echo Access token: $ACCESS_TOKEN + +echo "------------------------------------" +echo "Introspect access token..." +echo "------------------------------------" +RESPONSE=$(curl -X POST -s --data "token=$ACCESS_TOKEN" http://localhost:18081/internal/auth/v2/accesstoken/introspect) +echo Introspection response: $RESPONSE + +# Check that it contains the following claims: +# - "organization_ura_dezi":"87654321" +# - "user_initials":"B.B." +# - "user_roles":["01.041","30.000","01.010","01.011"] +# - "user_surname":"Jansen" +# - "user_surname_prefix":"van der" +# - "user_uzi":"900000009" +if [ "$(echo $RESPONSE | jq -r .organization_ura_dezi)" != "87654321" ]; then + echo "FAILED: organization_ura_dezi invalid" 1>&2 + echo $RESPONSE + exitWithDockerLogs 1 +fi +if [ "$(echo $RESPONSE | jq -r .user_initials)" != "B.B." ]; then + echo "FAILED: user_initials invalid" 1>&2 + echo $RESPONSE + exitWithDockerLogs 1 +fi +USER_ROLES=$(echo $RESPONSE | jq -r '.user_roles | sort | join(",")') +if [ "$USER_ROLES" != "01.010,01.011,01.041,30.000" ]; then + echo "FAILED: user_roles invalid" 1>&2 + echo $RESPONSE + exitWithDockerLogs 1 +fi +if [ "$(echo $RESPONSE | jq -r .user_surname)" != "Jansen" ]; then + echo "FAILED: user_surname invalid" 1>&2 + echo $RESPONSE + exitWithDockerLogs 1 +fi +if [ "$(echo $RESPONSE | jq -r .user_surname_prefix)" != "van der" ]; then + echo "FAILED: user_surname_prefix invalid" 1>&2 + echo $RESPONSE + exitWithDockerLogs 1 +fi +if [ "$(echo $RESPONSE | jq -r .user_uzi)" != "900000009" ]; then + echo "FAILED: user_uzi invalid" 1>&2 + echo $RESPONSE + exitWithDockerLogs 1 +fi + +echo "------------------------------------" +echo "Stopping Docker containers..." +echo "------------------------------------" +docker compose down \ No newline at end of file diff --git a/vcr/credential/dezi.go b/vcr/credential/dezi.go new file mode 100644 index 0000000000..4a53006a4c --- /dev/null +++ b/vcr/credential/dezi.go @@ -0,0 +1,87 @@ +package credential + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/lestrrat-go/jwx/v2/jwt" + "github.com/nuts-foundation/go-did/vc" +) + +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) + } + relationsRaw, _ := idToken.Get("relations") + relations, ok := relationsRaw.([]any) + if !ok || len(relations) != 1 { + return nil, fmt.Errorf("id_token 'relations' claim invalid or missing (expected array of objects with single item)") + } + relation, ok := relations[0].(map[string]any) + if !ok { + return nil, fmt.Errorf("id_token 'relations' claim invalid or missing (expected array of objects with single item)") + } + roles, ok := relation["roles"].([]any) + if !ok { + return nil, fmt.Errorf("id_token 'relations[0].roles' claim invalid or missing (expected array of strings)") + } + orgURA, ok := relation["ura"].(string) + if !ok || orgURA == "" { + return nil, fmt.Errorf("id_token 'relations[0].ura' claim invalid or missing (expected non-empty string)") + } + getString := func(claim string) string { + value, ok := idToken.Get(claim) + if !ok { + return "" + } + result, _ := value.(string) + return result + } + userID := getString("Dezi_id") + if userID == "" { + return nil, fmt.Errorf("id_token missing 'Dezi_id' claim") + } + initials := getString("initials") + if initials == "" { + return nil, fmt.Errorf("id_token missing 'initials' claim") + } + surname := getString("surname") + if surname == "" { + return nil, fmt.Errorf("id_token missing 'surname' claim") + } + surnamePrefix := getString("surname_prefix") + if surnamePrefix == "" { + return nil, fmt.Errorf("id_token missing 'surname_prefix' claim") + } + + credentialMap := map[string]any{ + "@context": []any{ + "https://www.w3.org/2018/credentials/v1", + // TODO: Create JSON-LD context? + }, + "type": []string{"VerifiableCredential", "DeziIDTokenCredential"}, + "issuanceDate": idToken.NotBefore().Format(time.RFC3339Nano), + "expirationDate": idToken.Expiration().Format(time.RFC3339Nano), + "credentialSubject": map[string]any{ + "@type": "DeziIDTokenSubject", + "identifier": orgURA, + "name": relation["entity_name"], + "employee": map[string]any{ + "@type": "HealthcareWorker", + "identifier": userID, + "initials": initials, + "surnamePrefix": surnamePrefix, + "surname": surname, + "roles": roles, + }, + }, + "proof": map[string]any{ + "type": "DeziIDJWT", + "jwt": idTokenSerialized, + }, + } + data, _ := json.Marshal(credentialMap) + return vc.ParseVerifiableCredential(string(data)) +} diff --git a/vcr/credential/dezi_test.go b/vcr/credential/dezi_test.go new file mode 100644 index 0000000000..ea9b970cab --- /dev/null +++ b/vcr/credential/dezi_test.go @@ -0,0 +1,28 @@ +package credential + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestCreateDeziIDToken(t *testing.T) { + t.Run("ok", func(t *testing.T) { + const input = "eyJhbGciOiJSUzI1NiIsImtpZCI6IlVhd3AwY2lRck1PSENadW04MlA2dkphNU8xND0iLCJ0eXAiOiJKV1QifQ.eyJEZXppX2lkIjoiOTAwMDAwMDA5IiwiYXVkIjpbIjAwNmZiZjM0LWE4MGItNGM4MS1iNmU5LTU5MzYwMDY3NWZiMiJdLCJleHAiOjE3MDE5MzM2OTcsImluaXRpYWxzIjoiQi5CLiIsImlzcyI6Imh0dHBzOi8vbWF4LnByb2VmdHVpbi5EZXppLW9ubGluZS5yZG9iZWhlZXIubmwiLCJqc29uX3NjaGVtYSI6Imh0dHBzOi8vbWF4LnByb2VmdHVpbi5EZXppLW9ubGluZS5yZG9iZWhlZXIubmwvanNvbl9zY2hlbWEuanNvbiIsImxvYV9EZXppIjoiaHR0cDovL2VpZGFzLmV1cm9wYS5ldS9Mb0EvaGlnaCIsImxvYV9hdXRobiI6Imh0dHA6Ly9laWRhcy5ldXJvcGEuZXUvTG9BL2hpZ2giLCJuYmYiOjE3MDE5MzM2MjcsInJlbGF0aW9ucyI6W3siZW50aXR5X25hbWUiOiJab3JnYWFuYmllZGVyIiwicm9sZXMiOlsiMDEuMDQxIiwiMzAuMDAwIiwiMDEuMDEwIiwiMDEuMDExIl0sInVyYSI6Ijg3NjU0MzIxIn1dLCJzdXJuYW1lIjoiSmFuc2VuIiwic3VybmFtZV9wcmVmaXgiOiJ2YW4gZGVyIiwieDVjIjpbIk1JSURWRENDQWp5Z0F3SUJBZ0lVRFdHVVJUNDJGWFdZbWR2Y1ExQXJxSUQrQUFBd0RRWUpLb1pJaHZjTkFRRUxCUUF3RWpFUU1BNEdBMVVFQXd3SFVtOXZkQ0JEUVRBZUZ3MHlOVEV5TWpReE16SXdOVEZhRncweU9EQXpNamd4TXpJd05URmFNQlF4RWpBUUJnTlZCQU1NQ1d4dlkyRnNhRzl6ZERDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTFJzeFRzaDhRTTdGTkFVUnRlb2pGTUhFNjdSd2dsL3NvdmJnWWlNMVZXTVZGYW9MT0UzYWxzaE9HMkh6d2ZkNUI4Q1VYOFg0K0ZudnZnazkrd0FrMU9vTENTSFA0Ri9LVGk1ZTJwNjRWdllzQUlXWDVOMjBldE5NeWFKRVBIM0lHMHl0OTNZbllZWHF0Y2hvUzNOV01lZjdMUFhJNnlOUXQvNnp0dzg5Qm5vY21lRDVqNmtKaWVtY0krTWttYmE2cHU0TitKT2dXS05ENTlVZlVuUmlDRlVtUEVoZ3VxRzd6WmJROGM3bFVkN1hPRVc1eGZQNUtmNjZ5eUsxaitUb3plUHlmelJIMVBuUHBDaDRWa2lmM1o2elJtc2t6Z0h6RDZjWmpaZDY0b1MwMExGOUczNFlMMkNuRElOdHNaUnZWTW5icmdpelJBY1lXOG83WUc5TzRjQ0F3RUFBYU9CbnpDQm5EQWZCZ05WSFNNRUdEQVdnQlF6VUFRZ0l4ZXBuWjhrSUhRQ0tLdWVIVUhaQ1RBSkJnTlZIUk1FQWpBQU1Bc0dBMVVkRHdRRUF3SUU4REFkQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0l3WURWUjBSQkJ3d0dvSUpiRzlqWVd4b2IzTjBnZ2R1ZFhSekxtNXNod1IvQUFBQk1CMEdBMVVkRGdRV0JCUUZBY016U1pCYWdUQWNwZ2wvcmllSmV0S3J3VEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBSzEvRHAxaWk2RWdhRk9NUmJFbWVLMWJrS1dvNDQyYVZHTUdXQzQ0TzUwTm9nSzM1aFM2eFdEdDNYb3B5eUEzcUFKd2FDeDc3ckNkNkVleXpSSlFucXdPRktjUGlqVERkL1hQa25xQzhIUTlqSGVqeWpUY2VaZjFOVVpvU1NteStiS3duK080NHBzdkhmWkpMajN0OFViSkRVd0ZWVm5tL0NMVlh0Q0tETHhnUU9LMWdKUkgrMmxvTmZvRHR0REE3c29TMGJrL2VFQ0FML3Bsdm9meE1LUUxBV0ZobWhLUkhnWC9TSVdORWhkdkcxYlNGVG5HOFVtTmh1RGkrU1dnV3RLTnE4SW5mL3hTSlRyT09ycndFU3V4Wk50aEp5bUVyaWFrT084VEN6VE9icGRoNVFydHo3SmRGV3l1ZDZ0ZnU3VnNVbHRHSEpSeW4zVU5TZCtIYlJnPT0iXX0.alxktj1V-sQHq-bCPdD5GB3E8YqKHgglPEXuYoATXIbo0CBUWARMP8x_Q-gyGdJvU3kpLHVhQK5Y7Y6N6Tpkw46jZiDEVmBEWPltbqKOdCjYGiEie4VB1Sw0lvKwXsiKP_xMGAI6LQ8nrS9_y8fT9JsRWBtqEYR8wb84B727FJiY3SVwks1lnqljW2-qHlS-Z-ecdT7GF_7VBsz5a5UITqQSX0-Q7ccvoXGl8QFtqYjkG9D0oYWRu46l6AtCqyrIc90Iq44nIQf38U46Fohz_ED-J0xSFoiQoWOuiv7vzkdDiO4RHnaGVJeCAiesa3-TdUzE69ZSiPzx8AV_pTRfiw" + + actual, err := CreateDeziIDTokenCredential(input) + require.NoError(t, err) + + require.Len(t, actual.CredentialSubject, 1) + subject := actual.CredentialSubject[0] + employee := subject["employee"].(map[string]interface{}) + assert.Equal(t, "87654321", subject["identifier"]) + assert.Equal(t, "Zorgaanbieder", subject["name"]) + assert.Equal(t, "900000009", employee["identifier"]) + assert.Equal(t, "B.B.", employee["initials"]) + assert.Equal(t, "Jansen", employee["surname"]) + assert.Equal(t, "van der", employee["surnamePrefix"]) + assert.Equal(t, []any{"01.041", "30.000", "01.010", "01.011"}, employee["roles"]) + }) +} diff --git a/vcr/credential/util.go b/vcr/credential/util.go index 41643548f7..25e299f19e 100644 --- a/vcr/credential/util.go +++ b/vcr/credential/util.go @@ -20,12 +20,14 @@ package credential import ( "errors" + "slices" + "time" + "github.com/google/uuid" ssi "github.com/nuts-foundation/go-did" "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/go-did/vc" - "slices" - "time" + "github.com/nuts-foundation/nuts-node/vcr/signature/proof" ) // ResolveSubjectDID resolves the subject DID from the given credentials. @@ -114,10 +116,22 @@ func PresentationExpirationDate(presentation vc.VerifiablePresentation) *time.Ti // AutoCorrectSelfAttestedCredential sets the required fields for a self-attested credential. // These are provided through the API, and for convenience we set the required fields, if not already set. -// It only does this for unsigned JSON-LD credentials. DO NOT USE THIS WITH JWT_VC CREDENTIALS. +// It only does this for unsigned JSON-LD credentials and DeziIDTokenCredentials (derived proof). DO NOT USE THIS WITH JWT_VC CREDENTIALS. func AutoCorrectSelfAttestedCredential(credential vc.VerifiableCredential, requester did.DID) vc.VerifiableCredential { if len(credential.Proof) > 0 { - return credential + var proof []proof.LDProof + _ = credential.UnmarshalProofValue(&proof) + isDeziTokenCredential := false + for _, p := range proof { + if p.Type == "DeziIDJWT" { + // derived proof, do the auto-correction + isDeziTokenCredential = true + break + } + } + if !isDeziTokenCredential { + return credential + } } if credential.ID == nil { credential.ID, _ = ssi.ParseURI(uuid.NewString()) diff --git a/vcr/credential/validator.go b/vcr/credential/validator.go index 0d62e8621d..962d041002 100644 --- a/vcr/credential/validator.go +++ b/vcr/credential/validator.go @@ -28,10 +28,7 @@ import ( "net/url" "strings" - "github.com/lestrrat-go/jwx/v2/cert" "github.com/lestrrat-go/jwx/v2/jwk" - "github.com/lestrrat-go/jwx/v2/jws" - "github.com/lestrrat-go/jwx/v2/jwt" "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/go-did/vc" "github.com/nuts-foundation/nuts-node/crypto" @@ -415,26 +412,26 @@ func (d deziIDTokenCredentialValidator) Validate(credential vc.VerifiableCredent } func (d deziIDTokenCredentialValidator) validateDeziToken(credential vc.VerifiableCredential, serialized string) error { - headers, err := crypto.ExtractProtectedHeaders(serialized) - if err != nil { - return fmt.Errorf("invalid JWT headers: %w", err) - } - chain := cert.Chain{} - for i, s := range headers["x5c"].([]string) { - - } - - token, err := jwt.ParseString(serialized, jws.WithKeyProvider(jws.)) - if err != nil { - return err - } - // TODO: Verify deziToken signature - if !token.NotBefore().Equal(credential.IssuanceDate) { - return errors.New("id_token 'nbf' does not match credential 'issuanceDate'") - } - if !token.Expiration().Equal(*credential.ExpirationDate) { - return errors.New("id_token 'exp' does not match credential 'expirationDate'") - } + //headers, err := crypto.ExtractProtectedHeaders(serialized) + //if err != nil { + // return fmt.Errorf("invalid JWT headers: %w", err) + //} + //chain := cert.Chain{} + //for i, s := range headers["x5c"].([]string) { + // + //} + // + //token, err := jwt.ParseString(serialized, jws.WithKeyProvider(jws.)) + //if err != nil { + // return err + //} + //// TODO: Verify deziToken signature + //if !token.NotBefore().Equal(credential.IssuanceDate) { + // return errors.New("id_token 'nbf' does not match credential 'issuanceDate'") + //} + //if !token.Expiration().Equal(*credential.ExpirationDate) { + // return errors.New("id_token 'exp' does not match credential 'expirationDate'") + //} // TODO: implement rest of checks return nil } diff --git a/vcr/pe/presentation_definition_test.go b/vcr/pe/presentation_definition_test.go index 9c220c3bee..c8544dd893 100644 --- a/vcr/pe/presentation_definition_test.go +++ b/vcr/pe/presentation_definition_test.go @@ -24,11 +24,12 @@ import ( "crypto/rand" "embed" "encoding/json" + "strings" + "testing" + "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/nuts-node/core/to" vcrTest "github.com/nuts-foundation/nuts-node/vcr/test" - "strings" - "testing" "github.com/lestrrat-go/jwx/v2/jwa" "github.com/lestrrat-go/jwx/v2/jwt" @@ -102,6 +103,220 @@ func TestParsePresentationDefinition(t *testing.T) { }) } +func TestDeziIDTokenCredential(t *testing.T) { + // Create a simple Dezi id_token (JWT) + 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)) + + // Create DeziIDTokenCredential using the helper function + credentialMap := map[string]any{ + "@context": []any{ + "https://www.w3.org/2018/credentials/v1", + }, + "type": []string{"VerifiableCredential", "DeziIDTokenCredential"}, + "issuanceDate": token.NotBefore().Format("2006-01-02T15:04:05Z07:00"), + "expirationDate": token.Expiration().Format("2006-01-02T15:04:05Z07:00"), + "credentialSubject": map[string]any{ + "@type": "DeziIDTokenSubject", + "identifier": "87654321", + "name": "Zorgaanbieder", + "employee": map[string]any{ + "@type": "HealthcareWorker", + "identifier": "900000009", + "initials": "B.B.", + "surnamePrefix": "van der", + "surname": "Jansen", + "roles": []string{"01.041", "30.000"}, + }, + }, + "proof": map[string]any{ + "type": "DeziIDJWT", + "jwt": string(signedToken), + }, + } + data, _ := json.Marshal(credentialMap) + cred, err := vc.ParseVerifiableCredential(string(data)) + require.NoError(t, err) + + t.Run("matching credential", func(t *testing.T) { + // Create a presentation definition that matches DeziIDTokenCredential + pd, err := ParsePresentationDefinition([]byte(`{ + "id": "pd_dezi_id_token_credential", + "name": "Dezi ID Token", + "purpose": "Request a Dezi ID Token credential", + "input_descriptors": [ + { + "id": "id_dezi_credential", + "constraints": { + "fields": [ + { + "path": [ + "$.type" + ], + "filter": { + "type": "string", + "const": "DeziIDTokenCredential" + } + }, + { + "id": "employee_identifier", + "path": [ + "$.credentialSubject.employee.identifier" + ], + "filter": { + "type": "string" + } + }, + { + "id": "employee_initials", + "path": [ + "$.credentialSubject.employee.initials" + ], + "filter": { + "type": "string" + } + } + ] + } + } + ] + }`)) + require.NoError(t, err) + + // Test matching + credentials, mappingObjects, err := pd.Match([]vc.VerifiableCredential{*cred}) + + require.NoError(t, err) + require.Len(t, credentials, 1) + require.Len(t, mappingObjects, 1) + + // Test field resolution + credMap := map[string]vc.VerifiableCredential{ + "id_dezi_credential": *cred, + } + fieldValues, err := pd.ResolveConstraintsFields(credMap) + require.NoError(t, err) + require.Len(t, fieldValues, 2) + assert.Equal(t, "900000009", fieldValues["employee_identifier"]) + assert.Equal(t, "B.B.", fieldValues["employee_initials"]) + }) + + t.Run("non-matching credential type", func(t *testing.T) { + pd, err := ParsePresentationDefinition([]byte(`{ + "id": "pd_other_credential", + "input_descriptors": [ + { + "id": "other_credential", + "constraints": { + "fields": [ + { + "path": ["$.type"], + "filter": { + "type": "string", + "const": "SomeOtherCredential" + } + } + ] + } + } + ] + }`)) + require.NoError(t, err) + + credentials, mappingObjects, err := pd.Match([]vc.VerifiableCredential{*cred}) + + assert.Error(t, err) + assert.Empty(t, credentials) + assert.Empty(t, mappingObjects) + }) + + t.Run("matching with organization identifier", func(t *testing.T) { + pd, err := ParsePresentationDefinition([]byte(`{ + "id": "pd_dezi_with_org", + "input_descriptors": [ + { + "id": "dezi_org_credential", + "constraints": { + "fields": [ + { + "path": ["$.type"], + "filter": { + "type": "string", + "const": "DeziIDTokenCredential" + } + }, + { + "id": "organization_identifier", + "path": ["$.credentialSubject.identifier"], + "filter": { + "type": "string", + "const": "87654321" + } + } + ] + } + } + ] + }`)) + require.NoError(t, err) + + credentials, mappingObjects, err := pd.Match([]vc.VerifiableCredential{*cred}) + + require.NoError(t, err) + require.Len(t, credentials, 1) + require.Len(t, mappingObjects, 1) + }) + + t.Run("matching employee roles", func(t *testing.T) { + pd, err := ParsePresentationDefinition([]byte(`{ + "id": "pd_dezi_with_roles", + "input_descriptors": [ + { + "id": "dezi_roles_credential", + "constraints": { + "fields": [ + { + "path": ["$.type"], + "filter": { + "type": "string", + "const": "DeziIDTokenCredential" + } + }, + { + "id": "employee_roles", + "path": ["$.credentialSubject.employee.roles[*]"], + "filter": { + "type": "string" + } + } + ] + } + } + ] + }`)) + require.NoError(t, err) + + credentials, _, err := pd.Match([]vc.VerifiableCredential{*cred}) + + require.NoError(t, err) + require.Len(t, credentials, 1) + }) +} + func TestEmployeeCredential(t *testing.T) { pd, err := ParsePresentationDefinition([]byte(`{ "format": { diff --git a/vcr/verifier/verifier_test.go b/vcr/verifier/verifier_test.go index 3bb48ca544..45f79fd934 100644 --- a/vcr/verifier/verifier_test.go +++ b/vcr/verifier/verifier_test.go @@ -388,11 +388,12 @@ func TestVerifier_Verify(t *testing.T) { }) t.Run("DeziIDTokenCredential", func(t *testing.T) { ctx := newMockContext(t) - validAt := time.Now() + ctx.store.EXPECT().GetRevocations(gomock.Any()).Return(nil, ErrNotFound) + validAt := time.Date(2023, 12, 7, 7, 20, 27, 0, time.UTC) cred, _ := createDeziCredential(t, "did:web:example.com") - err := ctx.verifier.Verify(*cred, false, true, &validAt) + err := ctx.verifier.Verify(*cred, true, true, &validAt) assert.NoError(t, err) }) } @@ -934,17 +935,19 @@ func createDeziCredential(t *testing.T, holderDID string) (*vc.VerifiableCredent signed, err := jwt.Sign(token, jwt.WithKey(jwa.RS256, key)) require.NoError(t, err) + println(string(signed)) + credentialMap := map[string]any{ "@context": []any{ "https://www.w3.org/2018/credentials/v1", }, "type": []string{"VerifiableCredential", "DeziIDTokenCredential"}, - "issuer": "https://max.proeftuin.Dezi-online.rdobeheer.nl", + "issuer": holderDID, + "id": holderDID + "#1", "issuanceDate": token.NotBefore().Format(time.RFC3339Nano), "expirationDate": token.Expiration().Format(time.RFC3339Nano), "credentialSubject": map[string]any{ "@type": "DeziIDTokenSubject", - "id": holderDID, "identifier": "87654321", "name": "Zorgaanbieder", "employee": map[string]any{ From 6552dfae10c2d7547a90c015f68d8187b471ddfe Mon Sep 17 00:00:00 2001 From: reinkrul Date: Mon, 2 Feb 2026 17:20:54 +0100 Subject: [PATCH 03/11] Update vcr/credential/validator.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- vcr/credential/validator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vcr/credential/validator.go b/vcr/credential/validator.go index 962d041002..1e4fb5e75c 100644 --- a/vcr/credential/validator.go +++ b/vcr/credential/validator.go @@ -403,7 +403,7 @@ func (d deziIDTokenCredentialValidator) Validate(credential vc.VerifiableCredent } proof := proofs[0] if proof.Type != "DeziIDJWT" { - return fmt.Errorf("%w: invalid proof type: expected 'DeziIDToken', got '%s'", errValidation, proof.Type) + return fmt.Errorf("%w: invalid proof type: expected 'DeziIDJWT', 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) From ea7ffac170d272e8ce956f0c1b91ea733c79edce Mon Sep 17 00:00:00 2001 From: Rein Krul Date: Mon, 2 Feb 2026 17:53:02 +0100 Subject: [PATCH 04/11] cleanup --- .../dezi_idtoken/docker-compose.yml | 2 +- vcr/credential/test.go | 55 ++++++++++++ vcr/pe/presentation_definition_test.go | 29 ++----- vcr/verifier/verifier_test.go | 83 +++---------------- 4 files changed, 76 insertions(+), 93 deletions(-) create mode 100644 vcr/credential/test.go diff --git a/e2e-tests/oauth-flow/dezi_idtoken/docker-compose.yml b/e2e-tests/oauth-flow/dezi_idtoken/docker-compose.yml index f5dfd438f4..c4f374168d 100644 --- a/e2e-tests/oauth-flow/dezi_idtoken/docker-compose.yml +++ b/e2e-tests/oauth-flow/dezi_idtoken/docker-compose.yml @@ -1,6 +1,6 @@ services: nodeA-backend: - image: "${IMAGE_NODE_A:-nutsfoundation/nuts-node:local}" + image: "${IMAGE_NODE_A:-nutsfoundation/nuts-node:master}" ports: - "18081:8081" environment: diff --git a/vcr/credential/test.go b/vcr/credential/test.go new file mode 100644 index 0000000000..ed69ef43a4 --- /dev/null +++ b/vcr/credential/test.go @@ -0,0 +1,55 @@ +package credential + +import ( + "crypto/sha1" + "crypto/tls" + "encoding/base64" + "time" + + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwk" + "github.com/lestrrat-go/jwx/v2/jwt" +) + +func CreateTestDeziIDToken(issuedAt time.Time, validUntil time.Time) ([]byte, error) { + keyPair, err := tls.LoadX509KeyPair("../../test/pki/certificate-and-key.pem", "../../test/pki/certificate-and-key.pem") + if err != nil { + return nil, err + } + key, err := jwk.FromRaw(keyPair.PrivateKey) + if err != nil { + return nil, err + } + x5t := sha1.Sum(keyPair.Leaf.Raw) + claims := map[string]any{ + jwk.KeyIDKey: base64.StdEncoding.EncodeToString(x5t[:]), + jwk.X509CertThumbprintKey: base64.StdEncoding.EncodeToString(x5t[:]), + jwk.AlgorithmKey: "RS256", + jwt.AudienceKey: "006fbf34-a80b-4c81-b6e9-593600675fb2", + jwt.ExpirationKey: validUntil.Unix(), + jwt.NotBeforeKey: issuedAt.Unix(), + jwt.IssuerKey: "https://max.proeftuin.Dezi-online.rdobeheer.nl", + "initials": "B.B.", + "surname": "Jansen", + "surname_prefix": "van der", + "Dezi_id": "900000009", + "json_schema": "https://max.proeftuin.Dezi-online.rdobeheer.nl/json_schema.json", + "loa_authn": "http://eidas.europa.eu/LoA/high", + "loa_Dezi": "http://eidas.europa.eu/LoA/high", + "x5c": []string{base64.StdEncoding.EncodeToString(keyPair.Leaf.Raw)}, + "relations": []map[string]interface{}{ + { + "entity_name": "Zorgaanbieder", + "roles": []string{"01.041", "30.000", "01.010", "01.011"}, + "ura": "87654321", + }, + }, + } + token := jwt.New() + for name, value := range claims { + if err := token.Set(name, value); err != nil { + return nil, err + } + } + return jwt.Sign(token, jwt.WithKey(jwa.RS256, key)) +} diff --git a/vcr/pe/presentation_definition_test.go b/vcr/pe/presentation_definition_test.go index c8544dd893..9e4378aa54 100644 --- a/vcr/pe/presentation_definition_test.go +++ b/vcr/pe/presentation_definition_test.go @@ -26,9 +26,11 @@ import ( "encoding/json" "strings" "testing" + "time" "github.com/nuts-foundation/go-did/did" "github.com/nuts-foundation/nuts-node/core/to" + "github.com/nuts-foundation/nuts-node/vcr/credential" vcrTest "github.com/nuts-foundation/nuts-node/vcr/test" "github.com/lestrrat-go/jwx/v2/jwa" @@ -104,23 +106,10 @@ func TestParsePresentationDefinition(t *testing.T) { } func TestDeziIDTokenCredential(t *testing.T) { - // Create a simple Dezi id_token (JWT) - 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)) + iat := time.Unix(1701933627, 0) + exp := time.Unix(1701933697, 0) + token, err := credential.CreateTestDeziIDToken(iat, exp) + require.NoError(t, err) // Create DeziIDTokenCredential using the helper function credentialMap := map[string]any{ @@ -128,8 +117,8 @@ func TestDeziIDTokenCredential(t *testing.T) { "https://www.w3.org/2018/credentials/v1", }, "type": []string{"VerifiableCredential", "DeziIDTokenCredential"}, - "issuanceDate": token.NotBefore().Format("2006-01-02T15:04:05Z07:00"), - "expirationDate": token.Expiration().Format("2006-01-02T15:04:05Z07:00"), + "issuanceDate": iat.Format("2006-01-02T15:04:05Z07:00"), + "expirationDate": exp.Format("2006-01-02T15:04:05Z07:00"), "credentialSubject": map[string]any{ "@type": "DeziIDTokenSubject", "identifier": "87654321", @@ -145,7 +134,7 @@ func TestDeziIDTokenCredential(t *testing.T) { }, "proof": map[string]any{ "type": "DeziIDJWT", - "jwt": string(signedToken), + "jwt": string(token), }, } data, _ := json.Marshal(credentialMap) diff --git a/vcr/verifier/verifier_test.go b/vcr/verifier/verifier_test.go index 45f79fd934..ff1cc31f68 100644 --- a/vcr/verifier/verifier_test.go +++ b/vcr/verifier/verifier_test.go @@ -21,9 +21,6 @@ package verifier import ( "context" "crypto" - "crypto/sha1" - "crypto/tls" - "crypto/x509" "encoding/json" "errors" "net/http" @@ -34,11 +31,6 @@ import ( "testing" "time" - "github.com/lestrrat-go/jwx/v2/jwa" - "github.com/nuts-foundation/nuts-node/storage/orm" - "github.com/nuts-foundation/nuts-node/test/pki" - "github.com/segmentio/asm/base64" - "github.com/lestrrat-go/jwx/v2/jwk" "github.com/lestrrat-go/jwx/v2/jwt" ssi "github.com/nuts-foundation/go-did" @@ -47,7 +39,9 @@ import ( "github.com/nuts-foundation/nuts-node/crypto/storage/spi" "github.com/nuts-foundation/nuts-node/jsonld" "github.com/nuts-foundation/nuts-node/storage" + "github.com/nuts-foundation/nuts-node/storage/orm" "github.com/nuts-foundation/nuts-node/test/io" + "github.com/nuts-foundation/nuts-node/test/pki" "github.com/nuts-foundation/nuts-node/vcr/credential" "github.com/nuts-foundation/nuts-node/vcr/revocation" "github.com/nuts-foundation/nuts-node/vcr/signature/proof" @@ -391,7 +385,7 @@ func TestVerifier_Verify(t *testing.T) { ctx.store.EXPECT().GetRevocations(gomock.Any()).Return(nil, ErrNotFound) validAt := time.Date(2023, 12, 7, 7, 20, 27, 0, time.UTC) - cred, _ := createDeziCredential(t, "did:web:example.com") + cred := createDeziCredential(t, "did:web:example.com") err := ctx.verifier.Verify(*cred, true, true, &validAt) assert.NoError(t, err) @@ -876,66 +870,11 @@ func newMockContext(t *testing.T) mockContext { } // createDeziIDToken creates a signed Dezi id_token according to https://www.dezi.nl/documenten/2024/05/08/koppelvlakspecificatie-dezi-online-koppelvlak-1_-platformleverancier -func createDeziCredential(t *testing.T, holderDID string) (*vc.VerifiableCredential, *x509.Certificate) { - 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[:])) - require.NoError(t, err) - err = key.Set(jwk.X509CertThumbprintKey, base64.StdEncoding.EncodeToString(x5t[:])) - require.NoError(t, err) - err = key.Set(jwk.AlgorithmKey, "RS256") - require.NoError(t, err) - - // Build the JWT token - token := jwt.New() - - // Set claims from the DeziIDTokenCredential payload - err = token.Set(jwt.AudienceKey, "006fbf34-a80b-4c81-b6e9-593600675fb2") - require.NoError(t, err) - err = token.Set(jwt.ExpirationKey, time.Unix(1701933697, 0)) - require.NoError(t, err) - err = token.Set(jwt.NotBeforeKey, time.Unix(1701933627, 0)) +func createDeziCredential(t *testing.T, holderDID string) *vc.VerifiableCredential { + exp := time.Unix(1701933697, 0) + iat := time.Unix(1701933627, 0) + idToken, err := credential.CreateTestDeziIDToken(iat, exp) require.NoError(t, err) - err = token.Set(jwt.IssuerKey, "https://max.proeftuin.Dezi-online.rdobeheer.nl") - require.NoError(t, err) - - // Set custom claims - err = token.Set("initials", "B.B.") - require.NoError(t, err) - err = token.Set("json_schema", "https://max.proeftuin.Dezi-online.rdobeheer.nl/json_schema.json") - require.NoError(t, err) - err = token.Set("loa_authn", "http://eidas.europa.eu/LoA/high") - require.NoError(t, err) - err = token.Set("loa_Dezi", "http://eidas.europa.eu/LoA/high") - require.NoError(t, err) - err = token.Set("relations", []map[string]interface{}{ - { - "entity_name": "Zorgaanbieder", - "roles": []string{"01.041", "30.000", "01.010", "01.011"}, - "ura": "87654321", - }, - }) - 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("x5c", []string{base64.StdEncoding.EncodeToString(keyPair.Leaf.Raw)}) - require.NoError(t, err) - - // Sign the token using jwt.Sign - signed, err := jwt.Sign(token, jwt.WithKey(jwa.RS256, key)) - require.NoError(t, err) - - println(string(signed)) credentialMap := map[string]any{ "@context": []any{ @@ -944,8 +883,8 @@ func createDeziCredential(t *testing.T, holderDID string) (*vc.VerifiableCredent "type": []string{"VerifiableCredential", "DeziIDTokenCredential"}, "issuer": holderDID, "id": holderDID + "#1", - "issuanceDate": token.NotBefore().Format(time.RFC3339Nano), - "expirationDate": token.Expiration().Format(time.RFC3339Nano), + "issuanceDate": iat.Format(time.RFC3339Nano), + "expirationDate": exp.Format(time.RFC3339Nano), "credentialSubject": map[string]any{ "@type": "DeziIDTokenSubject", "identifier": "87654321", @@ -961,12 +900,12 @@ func createDeziCredential(t *testing.T, holderDID string) (*vc.VerifiableCredent }, "proof": map[string]any{ "type": "DeziIDJWT", - "jwt": string(signed), + "jwt": string(idToken), }, } data, err := json.Marshal(credentialMap) require.NoError(t, err) cred, err := vc.ParseVerifiableCredential(string(data)) require.NoError(t, err) - return cred, keyPair.Leaf + return cred } From 649b8392469b1970a24cdbf44c369bd7df5e1949 Mon Sep 17 00:00:00 2001 From: Rein Krul Date: Thu, 26 Feb 2026 16:17:25 +0100 Subject: [PATCH 05/11] wipo --- vcr/credential/dezi.go | 178 ++++++++++++++++---- vcr/credential/dezi_test.go | 218 ++++++++++++++++++++++++- vcr/credential/test.go | 68 ++++++-- vcr/credential/validator.go | 51 ------ vcr/pe/presentation_definition_test.go | 2 + vcr/verifier/verifier_test.go | 3 +- 6 files changed, 412 insertions(+), 108 deletions(-) diff --git a/vcr/credential/dezi.go b/vcr/credential/dezi.go index 4a53006a4c..d6d1b77a09 100644 --- a/vcr/credential/dezi.go +++ b/vcr/credential/dezi.go @@ -2,35 +2,23 @@ package credential import ( "encoding/json" + "errors" "fmt" + "net/http" "time" + "github.com/lestrrat-go/jwx/v2/jwk" "github.com/lestrrat-go/jwx/v2/jwt" "github.com/nuts-foundation/go-did/vc" ) func CreateDeziIDTokenCredential(idTokenSerialized string) (*vc.VerifiableCredential, error) { - idToken, err := jwt.Parse([]byte(idTokenSerialized), jwt.WithVerify(false), jwt.WithAcceptableSkew(time.Hour*24*365*10)) + // Parse without signature or time validation - those are validated elsewhere + idToken, err := jwt.Parse([]byte(idTokenSerialized), jwt.WithVerify(false), jwt.WithValidate(false)) if err != nil { return nil, fmt.Errorf("parsing id_token: %w", err) } - relationsRaw, _ := idToken.Get("relations") - relations, ok := relationsRaw.([]any) - if !ok || len(relations) != 1 { - return nil, fmt.Errorf("id_token 'relations' claim invalid or missing (expected array of objects with single item)") - } - relation, ok := relations[0].(map[string]any) - if !ok { - return nil, fmt.Errorf("id_token 'relations' claim invalid or missing (expected array of objects with single item)") - } - roles, ok := relation["roles"].([]any) - if !ok { - return nil, fmt.Errorf("id_token 'relations[0].roles' claim invalid or missing (expected array of strings)") - } - orgURA, ok := relation["ura"].(string) - if !ok || orgURA == "" { - return nil, fmt.Errorf("id_token 'relations[0].ura' claim invalid or missing (expected non-empty string)") - } + getString := func(claim string) string { value, ok := idToken.Get(claim) if !ok { @@ -39,21 +27,80 @@ func CreateDeziIDTokenCredential(idTokenSerialized string) (*vc.VerifiableCreden result, _ := value.(string) return result } - userID := getString("Dezi_id") - if userID == "" { - return nil, fmt.Errorf("id_token missing 'Dezi_id' claim") - } - initials := getString("initials") - if initials == "" { - return nil, fmt.Errorf("id_token missing 'initials' claim") - } - surname := getString("surname") - if surname == "" { - return nil, fmt.Errorf("id_token missing 'surname' claim") - } - surnamePrefix := getString("surname_prefix") - if surnamePrefix == "" { - return nil, fmt.Errorf("id_token missing 'surname_prefix' claim") + + // Check if this is v0.7 format (has abonnee_nummer) or old format (has relations) + isV07 := getString("abonnee_nummer") != "" + + var orgURA, orgName, userID, initials, surname, surnamePrefix string + var roles []any + + if isV07 { + // v0.7 spec format + orgURA = getString("abonnee_nummer") + if orgURA == "" { + return nil, fmt.Errorf("id_token missing 'abonnee_nummer' claim") + } + orgName = getString("abonnee_naam") + if orgName == "" { + return nil, fmt.Errorf("id_token missing 'abonnee_naam' claim") + } + + userID = getString("dezi_nummer") + if userID == "" { + return nil, fmt.Errorf("id_token missing 'dezi_nummer' claim") + } + initials = getString("voorletters") + if initials == "" { + return nil, fmt.Errorf("id_token missing 'voorletters' claim") + } + surname = getString("achternaam") + if surname == "" { + return nil, fmt.Errorf("id_token missing 'achternaam' claim") + } + surnamePrefix = getString("voorvoegsel") // Can be null/empty in v0.7 + + // In v0.7, rol_code is a single string, not an array + rolCode := getString("rol_code") + if rolCode != "" { + roles = []any{rolCode} + } + } else { + // Old format with relations + relationsRaw, _ := idToken.Get("relations") + relations, ok := relationsRaw.([]any) + if !ok || len(relations) != 1 { + return nil, fmt.Errorf("id_token 'relations' claim invalid or missing (expected array of objects with single item)") + } + relation, ok := relations[0].(map[string]any) + if !ok { + return nil, fmt.Errorf("id_token 'relations' claim invalid or missing (expected array of objects with single item)") + } + roles, ok = relation["roles"].([]any) + if !ok { + return nil, fmt.Errorf("id_token 'relations[0].roles' claim invalid or missing (expected array of strings)") + } + orgURA, ok = relation["ura"].(string) + if !ok || orgURA == "" { + return nil, fmt.Errorf("id_token 'relations[0].ura' claim invalid or missing (expected non-empty string)") + } + orgName, _ = relation["entity_name"].(string) + + userID = getString("Dezi_id") + if userID == "" { + return nil, fmt.Errorf("id_token missing 'Dezi_id' claim") + } + initials = getString("initials") + if initials == "" { + return nil, fmt.Errorf("id_token missing 'initials' claim") + } + surname = getString("surname") + if surname == "" { + return nil, fmt.Errorf("id_token missing 'surname' claim") + } + surnamePrefix = getString("surname_prefix") + if surnamePrefix == "" { + return nil, fmt.Errorf("id_token missing 'surname_prefix' claim") + } } credentialMap := map[string]any{ @@ -62,12 +109,14 @@ func CreateDeziIDTokenCredential(idTokenSerialized string) (*vc.VerifiableCreden // TODO: Create JSON-LD context? }, "type": []string{"VerifiableCredential", "DeziIDTokenCredential"}, + "id": idToken.JwtID(), + "issuer": idToken.Issuer(), "issuanceDate": idToken.NotBefore().Format(time.RFC3339Nano), "expirationDate": idToken.Expiration().Format(time.RFC3339Nano), "credentialSubject": map[string]any{ "@type": "DeziIDTokenSubject", "identifier": orgURA, - "name": relation["entity_name"], + "name": orgName, "employee": map[string]any{ "@type": "HealthcareWorker", "identifier": userID, @@ -85,3 +134,64 @@ func CreateDeziIDTokenCredential(idTokenSerialized string) (*vc.VerifiableCreden data, _ := json.Marshal(credentialMap) return vc.ParseVerifiableCredential(string(data)) } + +// deziIDTokenCredentialValidator validates DeziIDTokenCredential, according to (TODO: add spec). +type deziIDTokenCredentialValidator struct { + clock func() time.Time + httpClient *http.Client // Optional HTTP client for fetching JWK Set (for testing) +} + +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 'DeziIDJWT', 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) +} + +func (d deziIDTokenCredentialValidator) validateDeziToken(credential vc.VerifiableCredential, serialized string) error { + // Parse and verify the JWT + // - WithVerifyAuto(nil, ...) uses default jwk.Fetch and automatically fetches the JWK Set from the jku header URL + // - WithFetchWhitelist allows fetching from any https:// URL (Dezi endpoints) + // - WithHTTPClient allows using a custom HTTP client (for testing with self-signed certs) + // - WithValidate(false) skips exp/nbf validation since we validate those against credential dates + fetchOptions := []jwk.FetchOption{jwk.WithFetchWhitelist(jwk.InsecureWhitelist{})} + if d.httpClient != nil { + fetchOptions = append(fetchOptions, jwk.WithHTTPClient(d.httpClient)) + } + + // TODO: Only allow specific domains for the jku + // TODO: make sure it's signed with a jku + token, err := jwt.Parse( + []byte(serialized), + jwt.WithVerifyAuto(nil, fetchOptions...), + jwt.WithValidate(false), + ) + if err != nil { + return fmt.Errorf("failed to verify JWT signature: %w", err) + } + + // Validate that token timestamps match credential dates + if !token.NotBefore().Equal(credential.IssuanceDate) { + return errors.New("'nbf' does not match credential 'issuanceDate'") + } + if !token.Expiration().Equal(*credential.ExpirationDate) { + return errors.New("'exp' does not match credential 'expirationDate'") + } + // TODO: implement rest of checks (claims) + return nil +} diff --git a/vcr/credential/dezi_test.go b/vcr/credential/dezi_test.go index ea9b970cab..efe2206d69 100644 --- a/vcr/credential/dezi_test.go +++ b/vcr/credential/dezi_test.go @@ -1,15 +1,27 @@ package credential import ( + "context" + "crypto/sha1" + "crypto/tls" + "encoding/base64" + "encoding/json" + "net/http" + "net/http/httptest" "testing" + "time" + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwk" + "github.com/lestrrat-go/jwx/v2/jws" + "github.com/lestrrat-go/jwx/v2/jwt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestCreateDeziIDToken(t *testing.T) { t.Run("ok", func(t *testing.T) { - const input = "eyJhbGciOiJSUzI1NiIsImtpZCI6IlVhd3AwY2lRck1PSENadW04MlA2dkphNU8xND0iLCJ0eXAiOiJKV1QifQ.eyJEZXppX2lkIjoiOTAwMDAwMDA5IiwiYXVkIjpbIjAwNmZiZjM0LWE4MGItNGM4MS1iNmU5LTU5MzYwMDY3NWZiMiJdLCJleHAiOjE3MDE5MzM2OTcsImluaXRpYWxzIjoiQi5CLiIsImlzcyI6Imh0dHBzOi8vbWF4LnByb2VmdHVpbi5EZXppLW9ubGluZS5yZG9iZWhlZXIubmwiLCJqc29uX3NjaGVtYSI6Imh0dHBzOi8vbWF4LnByb2VmdHVpbi5EZXppLW9ubGluZS5yZG9iZWhlZXIubmwvanNvbl9zY2hlbWEuanNvbiIsImxvYV9EZXppIjoiaHR0cDovL2VpZGFzLmV1cm9wYS5ldS9Mb0EvaGlnaCIsImxvYV9hdXRobiI6Imh0dHA6Ly9laWRhcy5ldXJvcGEuZXUvTG9BL2hpZ2giLCJuYmYiOjE3MDE5MzM2MjcsInJlbGF0aW9ucyI6W3siZW50aXR5X25hbWUiOiJab3JnYWFuYmllZGVyIiwicm9sZXMiOlsiMDEuMDQxIiwiMzAuMDAwIiwiMDEuMDEwIiwiMDEuMDExIl0sInVyYSI6Ijg3NjU0MzIxIn1dLCJzdXJuYW1lIjoiSmFuc2VuIiwic3VybmFtZV9wcmVmaXgiOiJ2YW4gZGVyIiwieDVjIjpbIk1JSURWRENDQWp5Z0F3SUJBZ0lVRFdHVVJUNDJGWFdZbWR2Y1ExQXJxSUQrQUFBd0RRWUpLb1pJaHZjTkFRRUxCUUF3RWpFUU1BNEdBMVVFQXd3SFVtOXZkQ0JEUVRBZUZ3MHlOVEV5TWpReE16SXdOVEZhRncweU9EQXpNamd4TXpJd05URmFNQlF4RWpBUUJnTlZCQU1NQ1d4dlkyRnNhRzl6ZERDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTFJzeFRzaDhRTTdGTkFVUnRlb2pGTUhFNjdSd2dsL3NvdmJnWWlNMVZXTVZGYW9MT0UzYWxzaE9HMkh6d2ZkNUI4Q1VYOFg0K0ZudnZnazkrd0FrMU9vTENTSFA0Ri9LVGk1ZTJwNjRWdllzQUlXWDVOMjBldE5NeWFKRVBIM0lHMHl0OTNZbllZWHF0Y2hvUzNOV01lZjdMUFhJNnlOUXQvNnp0dzg5Qm5vY21lRDVqNmtKaWVtY0krTWttYmE2cHU0TitKT2dXS05ENTlVZlVuUmlDRlVtUEVoZ3VxRzd6WmJROGM3bFVkN1hPRVc1eGZQNUtmNjZ5eUsxaitUb3plUHlmelJIMVBuUHBDaDRWa2lmM1o2elJtc2t6Z0h6RDZjWmpaZDY0b1MwMExGOUczNFlMMkNuRElOdHNaUnZWTW5icmdpelJBY1lXOG83WUc5TzRjQ0F3RUFBYU9CbnpDQm5EQWZCZ05WSFNNRUdEQVdnQlF6VUFRZ0l4ZXBuWjhrSUhRQ0tLdWVIVUhaQ1RBSkJnTlZIUk1FQWpBQU1Bc0dBMVVkRHdRRUF3SUU4REFkQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0l3WURWUjBSQkJ3d0dvSUpiRzlqWVd4b2IzTjBnZ2R1ZFhSekxtNXNod1IvQUFBQk1CMEdBMVVkRGdRV0JCUUZBY016U1pCYWdUQWNwZ2wvcmllSmV0S3J3VEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBSzEvRHAxaWk2RWdhRk9NUmJFbWVLMWJrS1dvNDQyYVZHTUdXQzQ0TzUwTm9nSzM1aFM2eFdEdDNYb3B5eUEzcUFKd2FDeDc3ckNkNkVleXpSSlFucXdPRktjUGlqVERkL1hQa25xQzhIUTlqSGVqeWpUY2VaZjFOVVpvU1NteStiS3duK080NHBzdkhmWkpMajN0OFViSkRVd0ZWVm5tL0NMVlh0Q0tETHhnUU9LMWdKUkgrMmxvTmZvRHR0REE3c29TMGJrL2VFQ0FML3Bsdm9meE1LUUxBV0ZobWhLUkhnWC9TSVdORWhkdkcxYlNGVG5HOFVtTmh1RGkrU1dnV3RLTnE4SW5mL3hTSlRyT09ycndFU3V4Wk50aEp5bUVyaWFrT084VEN6VE9icGRoNVFydHo3SmRGV3l1ZDZ0ZnU3VnNVbHRHSEpSeW4zVU5TZCtIYlJnPT0iXX0.alxktj1V-sQHq-bCPdD5GB3E8YqKHgglPEXuYoATXIbo0CBUWARMP8x_Q-gyGdJvU3kpLHVhQK5Y7Y6N6Tpkw46jZiDEVmBEWPltbqKOdCjYGiEie4VB1Sw0lvKwXsiKP_xMGAI6LQ8nrS9_y8fT9JsRWBtqEYR8wb84B727FJiY3SVwks1lnqljW2-qHlS-Z-ecdT7GF_7VBsz5a5UITqQSX0-Q7ccvoXGl8QFtqYjkG9D0oYWRu46l6AtCqyrIc90Iq44nIQf38U46Fohz_ED-J0xSFoiQoWOuiv7vzkdDiO4RHnaGVJeCAiesa3-TdUzE69ZSiPzx8AV_pTRfiw" + const input = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjMyNWRlOWFiLTQzMzAtNGMwMS04MjRlLWQ5YmQwYzM3Y2NhMCIsImprdSI6Imh0dHBzOi8vW2V4dGVybiBlbmRwb2ludF0vandrcy5qc29uIiwidHlwIjoiSldUIn0.eyJqdGkiOiI2MWIxZmFmYy00ZWM3LTQ0ODktYTI4MC04ZDBhNTBhM2Q1YTkiLCJpc3MiOiJhYm9ubmVlLmRlemkubmwiLCJleHAiOjE3NDAxMzExNzYsIm5iZiI6MTczMjE4MjM3NiwianNvbl9zY2hlbWEiOiJodHRwczovL3d3dy5kZXppLm5sL2pzb25fc2NoZW1hcy92ZXJrbGFyaW5nX3YxLmpzb24iLCJsb2FfZGV6aSI6Imh0dHA6Ly9laWRhcy5ldXJvcGUuZXUvTG9BL2hpZ2giLCJ2ZXJrbGFyaW5nX2lkIjoiODUzOWY3NWQtNjM0Yy00N2RiLWJiNDEtMjg3OTFkZmQxZjhkIiwiZGV6aV9udW1tZXIiOiIxMjM0NTY3ODkiLCJ2b29ybGV0dGVycyI6IkEuQi4iLCJ2b29ydm9lZ3NlbCI6bnVsbCwiYWNodGVybmFhbSI6IlpvcmdtZWRld2Vya2VyIiwiYWJvbm5lZV9udW1tZXIiOiI4NzY1NDMyMSIsImFib25uZWVfbmFhbSI6IlpvcmdhYW5iaWVkZXIiLCJyb2xfY29kZSI6IjAxLjAwMCIsInJvbF9uYWFtIjoiQXJ0cyIsInJvbF9jb2RlX2Jyb24iOiJodHRwOi8vd3d3LmRlemkubmwvcm9sX2NvZGVfYnJvbi9iaWciLCJyZXZvY2F0aWVfY29udHJvbGVfdXJpIjoiaHR0cHM6Ly9hdXRoLmRlemkubmwvcmV2b2NhdGllLXN0YXR1cy92MS92ZXJrbGFyaW5nLzg1MzlmNzVkLTYzNGMtNDdkYi1iYjQxLTI4NzkxZGZkMWY4ZCJ9.vegszRMWJjE-SBpfPO9lxN_fEY814ezsXRYhLXorPq3j_B_wlv4A92saasdEWrTALbl9Shux0i6JvkbouqvZ_oJpOUfJxWFGFfGGCuiMhiz4k1zm665i98e2xTqFzqjQySu_gup3wYm24FmnzbHxy02RzM3pXvQCsk_jIfQ1YcUZmNmXa5hR4DEn4Z9STLHd2HwyL6IKafEGl-R_kgbAnArSHQvuLw0Fpx62QD0tr5d3PbzPirBdkuy4G1l0umb69EjZMZ5MyIl8Y_irhQ9IFomAeSlU_zZp6UojVIOnCY2gL5EMc_8B1PDC6R_C--quGoh14jiSOJAeYSf_9ETjgQ" actual, err := CreateDeziIDTokenCredential(input) require.NoError(t, err) @@ -19,10 +31,204 @@ func TestCreateDeziIDToken(t *testing.T) { employee := subject["employee"].(map[string]interface{}) assert.Equal(t, "87654321", subject["identifier"]) assert.Equal(t, "Zorgaanbieder", subject["name"]) - assert.Equal(t, "900000009", employee["identifier"]) - assert.Equal(t, "B.B.", employee["initials"]) - assert.Equal(t, "Jansen", employee["surname"]) - assert.Equal(t, "van der", employee["surnamePrefix"]) - assert.Equal(t, []any{"01.041", "30.000", "01.010", "01.011"}, employee["roles"]) + assert.Equal(t, "123456789", employee["identifier"]) + assert.Equal(t, "A.B.", employee["initials"]) + assert.Equal(t, "Zorgmedewerker", employee["surname"]) + assert.Equal(t, "", employee["surnamePrefix"]) // voorvoegsel is null in this token + assert.Equal(t, []any{"01.000"}, employee["roles"]) + }) +} + +func TestDeziIDTokenCredentialValidator(t *testing.T) { + // Test constants + iat := time.Unix(1732182376, 0) // Nov 21, 2024 + exp := time.Unix(1740131176, 0) // Feb 21, 2025 + validAt := time.Date(2024, 12, 1, 0, 0, 0, 0, time.UTC) + + // Helper to create token with mocked JWK server + createTokenWithMockServer := func(t *testing.T, keySet jwk.Set) (string, *httptest.Server) { + // Create test token + tokenBytes, err := CreateTestDeziIDToken(iat, exp) + require.NoError(t, err) + + // Create mock HTTPS server (jku must be HTTPS) + server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(keySet) + })) + + // Parse token and update jku header + msg, err := jws.Parse(tokenBytes) + require.NoError(t, err) + + sig := msg.Signatures()[0] + headers := jws.NewHeaders() + for iter := sig.ProtectedHeaders().Iterate(context.Background()); iter.Next(context.Background()); { + pair := iter.Pair() + headers.Set(pair.Key.(string), pair.Value) + } + headers.Set("jku", server.URL+"/jwks.json") + + // Load key for re-signing + keyPair, err := tls.LoadX509KeyPair("../../test/pki/certificate-and-key.pem", "../../test/pki/certificate-and-key.pem") + require.NoError(t, err) + privateKey, err := jwk.FromRaw(keyPair.PrivateKey) + require.NoError(t, err) + + // Parse original token claims + origToken, err := jwt.Parse(tokenBytes, jwt.WithVerify(false), jwt.WithValidate(false)) + require.NoError(t, err) + + // Re-sign with new headers + signedToken, err := jwt.Sign(origToken, jwt.WithKey(jwa.RS256, privateKey, jws.WithProtectedHeaders(headers))) + require.NoError(t, err) + + return string(signedToken), server + } + + // Helper to extract JWK set from token + extractJWKSet := func(t *testing.T) jwk.Set { + tokenBytes, err := CreateTestDeziIDToken(iat, exp) + require.NoError(t, err) + + token, err := jwt.Parse(tokenBytes, jwt.WithVerify(false), jwt.WithValidate(false)) + require.NoError(t, err) + + jwksRaw, ok := token.Get("jwks") + require.True(t, ok) + + jwksJSON, err := json.Marshal(jwksRaw) + require.NoError(t, err) + + keySet, err := jwk.Parse(jwksJSON) + require.NoError(t, err) + + return keySet + } + + t.Run("ok - valid signature and timestamps", func(t *testing.T) { + keySet := extractJWKSet(t) + tokenStr, server := createTokenWithMockServer(t, keySet) + defer server.Close() + + cred, err := CreateDeziIDTokenCredential(tokenStr) + require.NoError(t, err) + + err = deziIDTokenCredentialValidator{ + clock: func() time.Time { + return validAt + }, + httpClient: server.Client(), // Use test server's client to trust its certificate + }.Validate(*cred) + require.NoError(t, err) + }) + + t.Run("error - wrong exp", func(t *testing.T) { + keySet := extractJWKSet(t) + tokenStr, server := createTokenWithMockServer(t, keySet) + defer server.Close() + + cred, err := CreateDeziIDTokenCredential(tokenStr) + require.NoError(t, err) + + // Modify credential expiration to be different from token + wrongExp := exp.Add(time.Hour) + cred.ExpirationDate = &wrongExp + + err = deziIDTokenCredentialValidator{ + clock: func() time.Time { + return validAt + }, + httpClient: server.Client(), + }.Validate(*cred) + assert.Error(t, err) + assert.Contains(t, err.Error(), "'exp' does not match credential 'expirationDate'") + }) + + t.Run("error - wrong nbf", func(t *testing.T) { + keySet := extractJWKSet(t) + tokenStr, server := createTokenWithMockServer(t, keySet) + defer server.Close() + + cred, err := CreateDeziIDTokenCredential(tokenStr) + require.NoError(t, err) + + // Modify credential issuance date to be different from token + wrongNbf := iat.Add(-time.Hour) + cred.IssuanceDate = wrongNbf + + err = deziIDTokenCredentialValidator{ + clock: func() time.Time { + return validAt + }, + httpClient: server.Client(), + }.Validate(*cred) + assert.Error(t, err) + assert.Contains(t, err.Error(), "'nbf' does not match credential 'issuanceDate'") + }) + + t.Run("error - invalid signature (wrong key in JWK set)", func(t *testing.T) { + // Create a different key set (wrong keys) + wrongKeySet := jwk.NewSet() + wrongKey, _ := jwk.FromRaw([]byte("wrong-secret-key-data")) + x5t := sha1.Sum([]byte("wrong-cert")) + kid := base64.StdEncoding.EncodeToString(x5t[:]) + wrongKey.Set(jwk.KeyIDKey, kid) + wrongKeySet.AddKey(wrongKey) + + tokenStr, server := createTokenWithMockServer(t, wrongKeySet) + defer server.Close() + + cred, err := CreateDeziIDTokenCredential(tokenStr) + require.NoError(t, err) + + err = deziIDTokenCredentialValidator{ + clock: func() time.Time { + return validAt + }, + httpClient: server.Client(), + }.Validate(*cred) + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to verify JWT signature") + }) + + t.Run("error - jku endpoint unreachable", func(t *testing.T) { + // Create token bytes + tokenBytes, err := CreateTestDeziIDToken(iat, exp) + require.NoError(t, err) + + // Parse and update jku to non-existent endpoint + msg, err := jws.Parse(tokenBytes) + require.NoError(t, err) + + sig := msg.Signatures()[0] + headers := jws.NewHeaders() + for iter := sig.ProtectedHeaders().Iterate(context.Background()); iter.Next(context.Background()); { + pair := iter.Pair() + headers.Set(pair.Key.(string), pair.Value) + } + headers.Set("jku", "https://localhost:9999/jwks.json") + + keyPair, err := tls.LoadX509KeyPair("../../test/pki/certificate-and-key.pem", "../../test/pki/certificate-and-key.pem") + require.NoError(t, err) + privateKey, err := jwk.FromRaw(keyPair.PrivateKey) + require.NoError(t, err) + + origToken, err := jwt.Parse(tokenBytes, jwt.WithVerify(false), jwt.WithValidate(false)) + require.NoError(t, err) + + signedToken, err := jwt.Sign(origToken, jwt.WithKey(jwa.RS256, privateKey, jws.WithProtectedHeaders(headers))) + require.NoError(t, err) + + cred, err := CreateDeziIDTokenCredential(string(signedToken)) + require.NoError(t, err) + + err = deziIDTokenCredentialValidator{ + clock: func() time.Time { + return validAt + }, + }.Validate(*cred) + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to verify JWT signature") }) } diff --git a/vcr/credential/test.go b/vcr/credential/test.go index ed69ef43a4..d96618bf75 100644 --- a/vcr/credential/test.go +++ b/vcr/credential/test.go @@ -4,10 +4,12 @@ import ( "crypto/sha1" "crypto/tls" "encoding/base64" + "encoding/json" "time" "github.com/lestrrat-go/jwx/v2/jwa" "github.com/lestrrat-go/jwx/v2/jwk" + "github.com/lestrrat-go/jwx/v2/jws" "github.com/lestrrat-go/jwx/v2/jwt" ) @@ -20,23 +22,49 @@ func CreateTestDeziIDToken(issuedAt time.Time, validUntil time.Time) ([]byte, er if err != nil { return nil, err } + + // Create public key for the JWK Set + // Extract the public key from the certificate + publicKey, err := jwk.FromRaw(keyPair.Leaf.PublicKey) + if err != nil { + return nil, err + } + + // Set the key ID x5t := sha1.Sum(keyPair.Leaf.Raw) + kid := base64.StdEncoding.EncodeToString(x5t[:]) + if err := publicKey.Set(jwk.KeyIDKey, kid); err != nil { + return nil, err + } + if err := publicKey.Set(jwk.AlgorithmKey, jwa.RS256); err != nil { + return nil, err + } + + // Create JWK Set with the public key + keySet := jwk.NewSet() + if err := keySet.AddKey(publicKey); err != nil { + return nil, err + } + + // Marshal the JWK Set to JSON + jwksJSON, err := json.Marshal(keySet) + if err != nil { + return nil, err + } + claims := map[string]any{ - jwk.KeyIDKey: base64.StdEncoding.EncodeToString(x5t[:]), - jwk.X509CertThumbprintKey: base64.StdEncoding.EncodeToString(x5t[:]), - jwk.AlgorithmKey: "RS256", - jwt.AudienceKey: "006fbf34-a80b-4c81-b6e9-593600675fb2", - jwt.ExpirationKey: validUntil.Unix(), - jwt.NotBeforeKey: issuedAt.Unix(), - jwt.IssuerKey: "https://max.proeftuin.Dezi-online.rdobeheer.nl", - "initials": "B.B.", - "surname": "Jansen", - "surname_prefix": "van der", - "Dezi_id": "900000009", - "json_schema": "https://max.proeftuin.Dezi-online.rdobeheer.nl/json_schema.json", - "loa_authn": "http://eidas.europa.eu/LoA/high", - "loa_Dezi": "http://eidas.europa.eu/LoA/high", - "x5c": []string{base64.StdEncoding.EncodeToString(keyPair.Leaf.Raw)}, + jwt.AudienceKey: "006fbf34-a80b-4c81-b6e9-593600675fb2", + jwt.ExpirationKey: validUntil.Unix(), + jwt.NotBeforeKey: issuedAt.Unix(), + jwt.IssuerKey: "https://max.proeftuin.Dezi-online.rdobeheer.nl", + "initials": "B.B.", + "surname": "Jansen", + "surname_prefix": "van der", + "Dezi_id": "900000009", + "json_schema": "https://max.proeftuin.Dezi-online.rdobeheer.nl/json_schema.json", + "loa_authn": "http://eidas.europa.eu/LoA/high", + "loa_Dezi": "http://eidas.europa.eu/LoA/high", + "jwks": json.RawMessage(jwksJSON), "relations": []map[string]interface{}{ { "entity_name": "Zorgaanbieder", @@ -51,5 +79,13 @@ func CreateTestDeziIDToken(issuedAt time.Time, validUntil time.Time) ([]byte, er return nil, err } } - return jwt.Sign(token, jwt.WithKey(jwa.RS256, key)) + + // Create headers with kid + headers := jws.NewHeaders() + if err := headers.Set(jwk.KeyIDKey, kid); err != nil { + return nil, err + } + + // Sign with the key ID in the header + return jwt.Sign(token, jwt.WithKey(jwa.RS256, key, jws.WithProtectedHeaders(headers))) } diff --git a/vcr/credential/validator.go b/vcr/credential/validator.go index 1e4fb5e75c..56be3476b3 100644 --- a/vcr/credential/validator.go +++ b/vcr/credential/validator.go @@ -384,54 +384,3 @@ func validatePolicyAssertions(issuer did.DID, credential vc.VerifiableCredential return nil } - -// DeziIDTokenCredentialValidator validates DeziIDTokenCredential, according to (TODO: add spec). -type deziIDTokenCredentialValidator struct { -} - -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 'DeziIDJWT', 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) -} - -func (d deziIDTokenCredentialValidator) validateDeziToken(credential vc.VerifiableCredential, serialized string) error { - //headers, err := crypto.ExtractProtectedHeaders(serialized) - //if err != nil { - // return fmt.Errorf("invalid JWT headers: %w", err) - //} - //chain := cert.Chain{} - //for i, s := range headers["x5c"].([]string) { - // - //} - // - //token, err := jwt.ParseString(serialized, jws.WithKeyProvider(jws.)) - //if err != nil { - // return err - //} - //// TODO: Verify deziToken signature - //if !token.NotBefore().Equal(credential.IssuanceDate) { - // return errors.New("id_token 'nbf' does not match credential 'issuanceDate'") - //} - //if !token.Expiration().Equal(*credential.ExpirationDate) { - // return errors.New("id_token 'exp' does not match credential 'expirationDate'") - //} - // TODO: implement rest of checks - return nil -} diff --git a/vcr/pe/presentation_definition_test.go b/vcr/pe/presentation_definition_test.go index 9e4378aa54..c35310c926 100644 --- a/vcr/pe/presentation_definition_test.go +++ b/vcr/pe/presentation_definition_test.go @@ -117,6 +117,8 @@ func TestDeziIDTokenCredential(t *testing.T) { "https://www.w3.org/2018/credentials/v1", }, "type": []string{"VerifiableCredential", "DeziIDTokenCredential"}, + "id": "urn:uuid:test-credential", + "issuer": "https://max.proeftuin.Dezi-online.rdobeheer.nl", "issuanceDate": iat.Format("2006-01-02T15:04:05Z07:00"), "expirationDate": exp.Format("2006-01-02T15:04:05Z07:00"), "credentialSubject": map[string]any{ diff --git a/vcr/verifier/verifier_test.go b/vcr/verifier/verifier_test.go index ff1cc31f68..b986b3adf0 100644 --- a/vcr/verifier/verifier_test.go +++ b/vcr/verifier/verifier_test.go @@ -383,7 +383,8 @@ func TestVerifier_Verify(t *testing.T) { t.Run("DeziIDTokenCredential", func(t *testing.T) { ctx := newMockContext(t) ctx.store.EXPECT().GetRevocations(gomock.Any()).Return(nil, ErrNotFound) - validAt := time.Date(2023, 12, 7, 7, 20, 27, 0, time.UTC) + // Token is valid from 1701933627 (Dec 7, 2023 07:20:27) to 1701933697 (Dec 7, 2023 07:21:37) + validAt := time.Date(2023, 12, 7, 7, 21, 0, 0, time.UTC) cred := createDeziCredential(t, "did:web:example.com") From a622b57608fb4b8b8176f7d2e9037976c6749e74 Mon Sep 17 00:00:00 2001 From: Rein Krul Date: Fri, 27 Feb 2026 09:18:04 +0100 Subject: [PATCH 06/11] Update to 2026 version (wip) --- vcr/credential/dezi_test.go | 217 +++++++++---------------- vcr/credential/test.go | 60 ++----- vcr/pe/presentation_definition_test.go | 2 +- vcr/verifier/verifier_test.go | 2 +- 4 files changed, 93 insertions(+), 188 deletions(-) diff --git a/vcr/credential/dezi_test.go b/vcr/credential/dezi_test.go index efe2206d69..68f56426dc 100644 --- a/vcr/credential/dezi_test.go +++ b/vcr/credential/dezi_test.go @@ -1,24 +1,39 @@ package credential import ( - "context" - "crypto/sha1" + "bytes" "crypto/tls" - "encoding/base64" "encoding/json" + "io" "net/http" - "net/http/httptest" "testing" "time" - "github.com/lestrrat-go/jwx/v2/jwa" "github.com/lestrrat-go/jwx/v2/jwk" - "github.com/lestrrat-go/jwx/v2/jws" - "github.com/lestrrat-go/jwx/v2/jwt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +// stubbedRoundTripper is a test helper that returns a mock JWK Set for any HTTP request +type stubbedRoundTripper struct { + keySet jwk.Set +} + +func (s *stubbedRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + // Marshal the key set to JSON + jwksJSON, err := json.Marshal(s.keySet) + if err != nil { + return nil, err + } + + // Return a mock HTTP response with the JWK Set + return &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(bytes.NewReader(jwksJSON)), + Header: http.Header{"Content-Type": []string{"application/json"}}, + }, nil +} + func TestCreateDeziIDToken(t *testing.T) { t.Run("ok", func(t *testing.T) { const input = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjMyNWRlOWFiLTQzMzAtNGMwMS04MjRlLWQ5YmQwYzM3Y2NhMCIsImprdSI6Imh0dHBzOi8vW2V4dGVybiBlbmRwb2ludF0vandrcy5qc29uIiwidHlwIjoiSldUIn0.eyJqdGkiOiI2MWIxZmFmYy00ZWM3LTQ0ODktYTI4MC04ZDBhNTBhM2Q1YTkiLCJpc3MiOiJhYm9ubmVlLmRlemkubmwiLCJleHAiOjE3NDAxMzExNzYsIm5iZiI6MTczMjE4MjM3NiwianNvbl9zY2hlbWEiOiJodHRwczovL3d3dy5kZXppLm5sL2pzb25fc2NoZW1hcy92ZXJrbGFyaW5nX3YxLmpzb24iLCJsb2FfZGV6aSI6Imh0dHA6Ly9laWRhcy5ldXJvcGUuZXUvTG9BL2hpZ2giLCJ2ZXJrbGFyaW5nX2lkIjoiODUzOWY3NWQtNjM0Yy00N2RiLWJiNDEtMjg3OTFkZmQxZjhkIiwiZGV6aV9udW1tZXIiOiIxMjM0NTY3ODkiLCJ2b29ybGV0dGVycyI6IkEuQi4iLCJ2b29ydm9lZ3NlbCI6bnVsbCwiYWNodGVybmFhbSI6IlpvcmdtZWRld2Vya2VyIiwiYWJvbm5lZV9udW1tZXIiOiI4NzY1NDMyMSIsImFib25uZWVfbmFhbSI6IlpvcmdhYW5iaWVkZXIiLCJyb2xfY29kZSI6IjAxLjAwMCIsInJvbF9uYWFtIjoiQXJ0cyIsInJvbF9jb2RlX2Jyb24iOiJodHRwOi8vd3d3LmRlemkubmwvcm9sX2NvZGVfYnJvbi9iaWciLCJyZXZvY2F0aWVfY29udHJvbGVfdXJpIjoiaHR0cHM6Ly9hdXRoLmRlemkubmwvcmV2b2NhdGllLXN0YXR1cy92MS92ZXJrbGFyaW5nLzg1MzlmNzVkLTYzNGMtNDdkYi1iYjQxLTI4NzkxZGZkMWY4ZCJ9.vegszRMWJjE-SBpfPO9lxN_fEY814ezsXRYhLXorPq3j_B_wlv4A92saasdEWrTALbl9Shux0i6JvkbouqvZ_oJpOUfJxWFGFfGGCuiMhiz4k1zm665i98e2xTqFzqjQySu_gup3wYm24FmnzbHxy02RzM3pXvQCsk_jIfQ1YcUZmNmXa5hR4DEn4Z9STLHd2HwyL6IKafEGl-R_kgbAnArSHQvuLw0Fpx62QD0tr5d3PbzPirBdkuy4G1l0umb69EjZMZ5MyIl8Y_irhQ9IFomAeSlU_zZp6UojVIOnCY2gL5EMc_8B1PDC6R_C--quGoh14jiSOJAeYSf_9ETjgQ" @@ -45,124 +60,69 @@ func TestDeziIDTokenCredentialValidator(t *testing.T) { exp := time.Unix(1740131176, 0) // Feb 21, 2025 validAt := time.Date(2024, 12, 1, 0, 0, 0, 0, time.UTC) - // Helper to create token with mocked JWK server - createTokenWithMockServer := func(t *testing.T, keySet jwk.Set) (string, *httptest.Server) { - // Create test token - tokenBytes, err := CreateTestDeziIDToken(iat, exp) - require.NoError(t, err) - - // Create mock HTTPS server (jku must be HTTPS) - server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(keySet) - })) - - // Parse token and update jku header - msg, err := jws.Parse(tokenBytes) - require.NoError(t, err) - - sig := msg.Signatures()[0] - headers := jws.NewHeaders() - for iter := sig.ProtectedHeaders().Iterate(context.Background()); iter.Next(context.Background()); { - pair := iter.Pair() - headers.Set(pair.Key.(string), pair.Value) - } - headers.Set("jku", server.URL+"/jwks.json") - - // Load key for re-signing - keyPair, err := tls.LoadX509KeyPair("../../test/pki/certificate-and-key.pem", "../../test/pki/certificate-and-key.pem") - require.NoError(t, err) - privateKey, err := jwk.FromRaw(keyPair.PrivateKey) - require.NoError(t, err) - - // Parse original token claims - origToken, err := jwt.Parse(tokenBytes, jwt.WithVerify(false), jwt.WithValidate(false)) - require.NoError(t, err) - - // Re-sign with new headers - signedToken, err := jwt.Sign(origToken, jwt.WithKey(jwa.RS256, privateKey, jws.WithProtectedHeaders(headers))) - require.NoError(t, err) - - return string(signedToken), server + // Load signing key, create JWK set + signingKeyCert, err := tls.LoadX509KeyPair("../../test/pki/certificate-and-key.pem", "../../test/pki/certificate-and-key.pem") + require.NoError(t, err) + signingKey := signingKeyCert.PrivateKey + signingKeyJWK, err := jwk.FromRaw(signingKey) + require.NoError(t, err) + require.NoError(t, signingKeyJWK.Set(jwk.KeyIDKey, "1")) + + // Create JWK set with the public key + publicKeyJWK, err := jwk.FromRaw(signingKeyCert.Leaf.PublicKey) + require.NoError(t, err) + require.NoError(t, publicKeyJWK.Set(jwk.KeyIDKey, "1")) + keySet := jwk.NewSet() + require.NoError(t, keySet.AddKey(publicKeyJWK)) + + validator := deziIDTokenCredentialValidator{ + clock: func() time.Time { + return validAt + }, + httpClient: &http.Client{ + Transport: &stubbedRoundTripper{keySet: keySet}, + }, } - // Helper to extract JWK set from token - extractJWKSet := func(t *testing.T) jwk.Set { - tokenBytes, err := CreateTestDeziIDToken(iat, exp) - require.NoError(t, err) - - token, err := jwt.Parse(tokenBytes, jwt.WithVerify(false), jwt.WithValidate(false)) - require.NoError(t, err) - - jwksRaw, ok := token.Get("jwks") - require.True(t, ok) - - jwksJSON, err := json.Marshal(jwksRaw) - require.NoError(t, err) - - keySet, err := jwk.Parse(jwksJSON) + t.Run("ok", func(t *testing.T) { + tokenBytes, err := CreateTestDeziIDToken(iat, exp, signingKey) require.NoError(t, err) - return keySet - } - - t.Run("ok - valid signature and timestamps", func(t *testing.T) { - keySet := extractJWKSet(t) - tokenStr, server := createTokenWithMockServer(t, keySet) - defer server.Close() - - cred, err := CreateDeziIDTokenCredential(tokenStr) + cred, err := CreateDeziIDTokenCredential(string(tokenBytes)) require.NoError(t, err) - err = deziIDTokenCredentialValidator{ - clock: func() time.Time { - return validAt - }, - httpClient: server.Client(), // Use test server's client to trust its certificate - }.Validate(*cred) + err = validator.Validate(*cred) require.NoError(t, err) }) t.Run("error - wrong exp", func(t *testing.T) { - keySet := extractJWKSet(t) - tokenStr, server := createTokenWithMockServer(t, keySet) - defer server.Close() + tokenBytes, err := CreateTestDeziIDToken(iat, exp, signingKey) + require.NoError(t, err) - cred, err := CreateDeziIDTokenCredential(tokenStr) + cred, err := CreateDeziIDTokenCredential(string(tokenBytes)) require.NoError(t, err) // Modify credential expiration to be different from token wrongExp := exp.Add(time.Hour) cred.ExpirationDate = &wrongExp - err = deziIDTokenCredentialValidator{ - clock: func() time.Time { - return validAt - }, - httpClient: server.Client(), - }.Validate(*cred) + err = validator.Validate(*cred) assert.Error(t, err) assert.Contains(t, err.Error(), "'exp' does not match credential 'expirationDate'") }) t.Run("error - wrong nbf", func(t *testing.T) { - keySet := extractJWKSet(t) - tokenStr, server := createTokenWithMockServer(t, keySet) - defer server.Close() + tokenBytes, err := CreateTestDeziIDToken(iat, exp, signingKey) + require.NoError(t, err) - cred, err := CreateDeziIDTokenCredential(tokenStr) + cred, err := CreateDeziIDTokenCredential(string(tokenBytes)) require.NoError(t, err) // Modify credential issuance date to be different from token wrongNbf := iat.Add(-time.Hour) cred.IssuanceDate = wrongNbf - err = deziIDTokenCredentialValidator{ - clock: func() time.Time { - return validAt - }, - httpClient: server.Client(), - }.Validate(*cred) + err = validator.Validate(*cred) assert.Error(t, err) assert.Contains(t, err.Error(), "'nbf' does not match credential 'issuanceDate'") }) @@ -171,63 +131,48 @@ func TestDeziIDTokenCredentialValidator(t *testing.T) { // Create a different key set (wrong keys) wrongKeySet := jwk.NewSet() wrongKey, _ := jwk.FromRaw([]byte("wrong-secret-key-data")) - x5t := sha1.Sum([]byte("wrong-cert")) - kid := base64.StdEncoding.EncodeToString(x5t[:]) + kid := "wrong-kid" wrongKey.Set(jwk.KeyIDKey, kid) wrongKeySet.AddKey(wrongKey) - tokenStr, server := createTokenWithMockServer(t, wrongKeySet) - defer server.Close() - - cred, err := CreateDeziIDTokenCredential(tokenStr) - require.NoError(t, err) - - err = deziIDTokenCredentialValidator{ + validatorWithWrongKeys := deziIDTokenCredentialValidator{ clock: func() time.Time { return validAt }, - httpClient: server.Client(), - }.Validate(*cred) - assert.Error(t, err) - assert.Contains(t, err.Error(), "failed to verify JWT signature") - }) + httpClient: &http.Client{ + Transport: &stubbedRoundTripper{keySet: wrongKeySet}, + }, + } - t.Run("error - jku endpoint unreachable", func(t *testing.T) { - // Create token bytes - tokenBytes, err := CreateTestDeziIDToken(iat, exp) + tokenBytes, err := CreateTestDeziIDToken(iat, exp, signingKey) require.NoError(t, err) - // Parse and update jku to non-existent endpoint - msg, err := jws.Parse(tokenBytes) + cred, err := CreateDeziIDTokenCredential(string(tokenBytes)) require.NoError(t, err) - sig := msg.Signatures()[0] - headers := jws.NewHeaders() - for iter := sig.ProtectedHeaders().Iterate(context.Background()); iter.Next(context.Background()); { - pair := iter.Pair() - headers.Set(pair.Key.(string), pair.Value) - } - headers.Set("jku", "https://localhost:9999/jwks.json") - - keyPair, err := tls.LoadX509KeyPair("../../test/pki/certificate-and-key.pem", "../../test/pki/certificate-and-key.pem") - require.NoError(t, err) - privateKey, err := jwk.FromRaw(keyPair.PrivateKey) - require.NoError(t, err) + err = validatorWithWrongKeys.Validate(*cred) + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to verify JWT signature") + }) - origToken, err := jwt.Parse(tokenBytes, jwt.WithVerify(false), jwt.WithValidate(false)) - require.NoError(t, err) + t.Run("error - jku endpoint unreachable", func(t *testing.T) { + // Use HTTP client that returns an error for any request + validatorWithBrokenClient := deziIDTokenCredentialValidator{ + clock: func() time.Time { + return validAt + }, + httpClient: &http.Client{ + Transport: &stubbedRoundTripper{keySet: nil}, // Will fail when trying to marshal nil + }, + } - signedToken, err := jwt.Sign(origToken, jwt.WithKey(jwa.RS256, privateKey, jws.WithProtectedHeaders(headers))) + tokenBytes, err := CreateTestDeziIDToken(iat, exp, signingKey) require.NoError(t, err) - cred, err := CreateDeziIDTokenCredential(string(signedToken)) + cred, err := CreateDeziIDTokenCredential(string(tokenBytes)) require.NoError(t, err) - err = deziIDTokenCredentialValidator{ - clock: func() time.Time { - return validAt - }, - }.Validate(*cred) + err = validatorWithBrokenClient.Validate(*cred) assert.Error(t, err) assert.Contains(t, err.Error(), "failed to verify JWT signature") }) diff --git a/vcr/credential/test.go b/vcr/credential/test.go index d96618bf75..cf95e583cc 100644 --- a/vcr/credential/test.go +++ b/vcr/credential/test.go @@ -1,57 +1,15 @@ package credential import ( - "crypto/sha1" - "crypto/tls" - "encoding/base64" - "encoding/json" + "crypto" "time" "github.com/lestrrat-go/jwx/v2/jwa" - "github.com/lestrrat-go/jwx/v2/jwk" "github.com/lestrrat-go/jwx/v2/jws" "github.com/lestrrat-go/jwx/v2/jwt" ) -func CreateTestDeziIDToken(issuedAt time.Time, validUntil time.Time) ([]byte, error) { - keyPair, err := tls.LoadX509KeyPair("../../test/pki/certificate-and-key.pem", "../../test/pki/certificate-and-key.pem") - if err != nil { - return nil, err - } - key, err := jwk.FromRaw(keyPair.PrivateKey) - if err != nil { - return nil, err - } - - // Create public key for the JWK Set - // Extract the public key from the certificate - publicKey, err := jwk.FromRaw(keyPair.Leaf.PublicKey) - if err != nil { - return nil, err - } - - // Set the key ID - x5t := sha1.Sum(keyPair.Leaf.Raw) - kid := base64.StdEncoding.EncodeToString(x5t[:]) - if err := publicKey.Set(jwk.KeyIDKey, kid); err != nil { - return nil, err - } - if err := publicKey.Set(jwk.AlgorithmKey, jwa.RS256); err != nil { - return nil, err - } - - // Create JWK Set with the public key - keySet := jwk.NewSet() - if err := keySet.AddKey(publicKey); err != nil { - return nil, err - } - - // Marshal the JWK Set to JSON - jwksJSON, err := json.Marshal(keySet) - if err != nil { - return nil, err - } - +func CreateTestDeziIDToken(issuedAt time.Time, validUntil time.Time, key crypto.PrivateKey) ([]byte, error) { claims := map[string]any{ jwt.AudienceKey: "006fbf34-a80b-4c81-b6e9-593600675fb2", jwt.ExpirationKey: validUntil.Unix(), @@ -64,7 +22,6 @@ func CreateTestDeziIDToken(issuedAt time.Time, validUntil time.Time) ([]byte, er "json_schema": "https://max.proeftuin.Dezi-online.rdobeheer.nl/json_schema.json", "loa_authn": "http://eidas.europa.eu/LoA/high", "loa_Dezi": "http://eidas.europa.eu/LoA/high", - "jwks": json.RawMessage(jwksJSON), "relations": []map[string]interface{}{ { "entity_name": "Zorgaanbieder", @@ -80,12 +37,15 @@ func CreateTestDeziIDToken(issuedAt time.Time, validUntil time.Time) ([]byte, er } } - // Create headers with kid headers := jws.NewHeaders() - if err := headers.Set(jwk.KeyIDKey, kid); err != nil { - return nil, err + for k, v := range map[string]any{ + "alg": "RS256", + "kid": "1", + "jku": "https://example.com/jwks.json", + } { + if err := headers.Set(k, v); err != nil { + return nil, err + } } - - // Sign with the key ID in the header return jwt.Sign(token, jwt.WithKey(jwa.RS256, key, jws.WithProtectedHeaders(headers))) } diff --git a/vcr/pe/presentation_definition_test.go b/vcr/pe/presentation_definition_test.go index c35310c926..e24cd25985 100644 --- a/vcr/pe/presentation_definition_test.go +++ b/vcr/pe/presentation_definition_test.go @@ -108,7 +108,7 @@ func TestParsePresentationDefinition(t *testing.T) { func TestDeziIDTokenCredential(t *testing.T) { iat := time.Unix(1701933627, 0) exp := time.Unix(1701933697, 0) - token, err := credential.CreateTestDeziIDToken(iat, exp) + token, err := credential.CreateTestDeziIDToken(iat, exp, nil) require.NoError(t, err) // Create DeziIDTokenCredential using the helper function diff --git a/vcr/verifier/verifier_test.go b/vcr/verifier/verifier_test.go index b986b3adf0..78844ac8b4 100644 --- a/vcr/verifier/verifier_test.go +++ b/vcr/verifier/verifier_test.go @@ -874,7 +874,7 @@ func newMockContext(t *testing.T) mockContext { func createDeziCredential(t *testing.T, holderDID string) *vc.VerifiableCredential { exp := time.Unix(1701933697, 0) iat := time.Unix(1701933627, 0) - idToken, err := credential.CreateTestDeziIDToken(iat, exp) + idToken, err := credential.CreateTestDeziIDToken(iat, exp, nil) require.NoError(t, err) credentialMap := map[string]any{ From 1f18ef361ba142c5d684e1a04c9b57ed5965373f Mon Sep 17 00:00:00 2001 From: Rein Krul Date: Mon, 2 Mar 2026 10:56:44 +0100 Subject: [PATCH 07/11] Support both 2024 and v0.7 version --- ...line+koppelvlak+1_+platformleverancier.pdf | Bin 0 -> 387512 bytes vcr/credential/dezi.go | 205 ++++++++++++++++-- vcr/credential/dezi_test.go | 199 ++++++++++++++++- vcr/credential/resolver.go | 2 +- 4 files changed, 378 insertions(+), 28 deletions(-) create mode 100644 vcr/credential/Koppelvlakspecificatie+DEZI-Online+koppelvlak+1_+platformleverancier.pdf diff --git a/vcr/credential/Koppelvlakspecificatie+DEZI-Online+koppelvlak+1_+platformleverancier.pdf b/vcr/credential/Koppelvlakspecificatie+DEZI-Online+koppelvlak+1_+platformleverancier.pdf new file mode 100644 index 0000000000000000000000000000000000000000..aa7c8bb645a2c85146a0a7c61ee194a4ba2828b4 GIT binary patch literal 387512 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≤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 L7m@Wa&x>>-Gevli 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