From 4a9fa1a53d1cb8da28256839db35106e783e42dd Mon Sep 17 00:00:00 2001 From: mishasizov-SK <109598497+mishasizov-SK@users.noreply.github.com> Date: Mon, 3 Feb 2025 16:03:48 +0200 Subject: [PATCH] feat: qr scanned event fix (#1843) * fix: bdd test for events Signed-off-by: Misha Sizov * feat: change the verifier.qr-scanned event Signed-off-by: Misha Sizov --------- Signed-off-by: Misha Sizov --- pkg/restapi/v1/oidc4vp/controller.go | 2 +- pkg/restapi/v1/verifier/controller.go | 72 ++-- pkg/restapi/v1/verifier/controller_test.go | 414 +++++++++++++++++--- pkg/service/oidc4vp/oidc4vp_service.go | 6 +- pkg/service/oidc4vp/oidc4vp_service_test.go | 46 +-- 5 files changed, 408 insertions(+), 132 deletions(-) diff --git a/pkg/restapi/v1/oidc4vp/controller.go b/pkg/restapi/v1/oidc4vp/controller.go index 9e3be4cb1..157883f08 100644 --- a/pkg/restapi/v1/oidc4vp/controller.go +++ b/pkg/restapi/v1/oidc4vp/controller.go @@ -63,7 +63,7 @@ func (c *Controller) PresentAuthorizationResponse(e echo.Context) error { req, err := http.NewRequestWithContext(ctx, http.MethodPost, - c.internalHostURL+oidc4VPCheckEndpoint, + c.internalHostURL+oidc4VPCheckEndpoint, // verifier.Controller.CheckAuthorizationResponse() request.Body, ) if err != nil { diff --git a/pkg/restapi/v1/verifier/controller.go b/pkg/restapi/v1/verifier/controller.go index 8a05eff26..7e130be8a 100644 --- a/pkg/restapi/v1/verifier/controller.go +++ b/pkg/restapi/v1/verifier/controller.go @@ -569,6 +569,11 @@ func (c *Controller) CheckAuthorizationResponse(e echo.Context) error { return oidc4vpErr.WithComponent(resterr.VerifierOIDC4vpSvcComponent) } + c.sendOIDC4VPInteractionEvent( + ctx, oidc4vp.TxID(rawAuthResp.State), spi.VerifierOIDCInteractionQRScanned, func() *oidc4vp.EventPayload { + return &oidc4vp.EventPayload{} + }) + if rawAuthResp.Error != "" { // Error authorization response // Spec: https://openid.net/specs/openid-4-verifiable-presentations-1_0-ID2.html#section-6.4 @@ -594,9 +599,7 @@ func (c *Controller) CheckAuthorizationResponse(e echo.Context) error { if oidc4vpErr != nil { oidc4vpErr = oidc4vpErr.WithComponent(resterr.VerifierOIDC4vpSvcComponent) - if tenantID, authErr := util.GetTenantIDFromRequest(e); authErr == nil { - c.sendFailedEvent(ctx, rawAuthResp.State, tenantID, "", "", oidc4vpErr) - } + c.sendFailedEvent(ctx, rawAuthResp.State, "", "", "", oidc4vpErr) return oidc4vpErr } @@ -1160,47 +1163,30 @@ func (c *Controller) accessProfile(profileID, profileVersion, tenantID string) ( return profile, nil } -func createFailedEventPayload(orgID, profileID, profileVersion string, e error) *oidc4vp.EventPayload { - ep := &oidc4vp.EventPayload{ - OrgID: orgID, - ProfileID: profileID, - ProfileVersion: profileVersion, - } - - var oidc4vpErr *oidc4vperr.Error - - if errors.As(e, &oidc4vpErr) { - ep.Error = oidc4vpErr.Error() - ep.ErrorCode = oidc4vpErr.Code() - ep.ErrorComponent = oidc4vpErr.Component() - } else { - ep.Error = e.Error() - } - - return ep -} - func (c *Controller) sendFailedEvent(ctx context.Context, txnID, orgID, profileID, profileVersion string, e error) { - c.sendOIDCInteractionFailedEvent(ctx, oidc4vp.TxID(txnID), func() *oidc4vp.EventPayload { - return createFailedEventPayload(orgID, profileID, profileVersion, e) - }) + c.sendOIDC4VPInteractionEvent(ctx, oidc4vp.TxID(txnID), spi.VerifierOIDCInteractionFailed, + func() *oidc4vp.EventPayload { + return createFailedEventPayload(orgID, profileID, profileVersion, e) + }) } func (c *Controller) sendFailedTxnEvent(ctx context.Context, orgID string, tx *oidc4vp.Transaction, e error) { - c.sendOIDCInteractionFailedEvent(ctx, tx.ID, func() *oidc4vp.EventPayload { - ep := createFailedEventPayload(orgID, tx.ProfileID, tx.ProfileVersion, e) - ep.PresentationDefinitionID = tx.PresentationDefinition.ID + c.sendOIDC4VPInteractionEvent(ctx, tx.ID, spi.VerifierOIDCInteractionFailed, + func() *oidc4vp.EventPayload { + ep := createFailedEventPayload(orgID, tx.ProfileID, tx.ProfileVersion, e) + ep.PresentationDefinitionID = tx.PresentationDefinition.ID - return ep - }) + return ep + }) } -func (c *Controller) sendOIDCInteractionFailedEvent( +func (c *Controller) sendOIDC4VPInteractionEvent( ctx context.Context, txnID oidc4vp.TxID, + eventType spi.EventType, createPayload func() *oidc4vp.EventPayload, ) { - evt, err := oidc4vp.CreateEvent(spi.VerifierOIDCInteractionFailed, txnID, createPayload()) + evt, err := oidc4vp.CreateEvent(eventType, txnID, createPayload()) if err != nil { logger.Errorc(ctx, "Error creating failure event", log.WithError(err)) @@ -1215,6 +1201,26 @@ func (c *Controller) sendOIDCInteractionFailedEvent( } } +func createFailedEventPayload(orgID, profileID, profileVersion string, e error) *oidc4vp.EventPayload { + ep := &oidc4vp.EventPayload{ + OrgID: orgID, + ProfileID: profileID, + ProfileVersion: profileVersion, + } + + var oidc4vpErr *oidc4vperr.Error + + if errors.As(e, &oidc4vpErr) { + ep.Error = oidc4vpErr.Error() + ep.ErrorCode = oidc4vpErr.Code() + ep.ErrorComponent = oidc4vpErr.Component() + } else { + ep.Error = e.Error() + } + + return ep +} + func findPresentationDefinition( profile *profileapi.Verifier, pdExternalID string, diff --git a/pkg/restapi/v1/verifier/controller_test.go b/pkg/restapi/v1/verifier/controller_test.go index 889658326..08bbe751b 100644 --- a/pkg/restapi/v1/verifier/controller_test.go +++ b/pkg/restapi/v1/verifier/controller_test.go @@ -703,7 +703,13 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { ctx := createContextApplicationForm([]byte(body)) mockEventSvc := NewMockeventService(gomock.NewController(t)) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(0) + mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(1).DoAndReturn( + func(ctx context.Context, topic string, messages ...*spi.Event) error { + assert.Len(t, messages, 1) + assert.Equal(t, spi.VerifierOIDCInteractionQRScanned, messages[0].Type) + + return nil + }) c := NewController(&Config{ VDR: signedClaimsJWTResult.VDR, @@ -752,12 +758,8 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { presentationSubmission, err := json.Marshal(map[string]interface{}{}) assert.NoError(t, err) - mockEventSvc := NewMockeventService(gomock.NewController(t)) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(0) - c := NewController(&Config{ OIDCVPService: svc, - EventSvc: mockEventSvc, EventTopic: spi.VerifierEventTopic, VDR: signedClaimsJWTResult.VDR, DocumentLoader: testutil.DocumentLoader(t), @@ -821,12 +823,8 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { presentationSubmission, err := json.Marshal(map[string]interface{}{}) assert.NoError(t, err) - mockEventSvc := NewMockeventService(gomock.NewController(t)) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(0) - c := NewController(&Config{ OIDCVPService: svc, - EventSvc: mockEventSvc, EventTopic: spi.VerifierEventTopic, VDR: signedClaimsJWTResult.VDR, DocumentLoader: testutil.DocumentLoader(t), @@ -891,12 +889,8 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { Exp: time.Now().Unix() + 1000, }) - mockEventSvc := NewMockeventService(gomock.NewController(t)) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(0) - c := NewController(&Config{ OIDCVPService: svc, - EventSvc: mockEventSvc, EventTopic: spi.VerifierEventTopic, VDR: signedClaimsJWTResult.VDR, DocumentLoader: testutil.DocumentLoader(t), @@ -955,12 +949,8 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { Exp: time.Now().Unix() + 1000, }) - mockEventSvc := NewMockeventService(gomock.NewController(t)) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(0) - c := NewController(&Config{ OIDCVPService: svc, - EventSvc: mockEventSvc, EventTopic: spi.VerifierEventTopic, VDR: signedClaimsJWTResult.VDR, DocumentLoader: testutil.DocumentLoader(t), @@ -997,8 +987,19 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { InteractionDetails: map[string]interface{}{"key1": "value1"}, }).Return(nil) + mockEventSvc := NewMockeventService(gomock.NewController(t)) + mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(1).DoAndReturn( + func(ctx context.Context, topic string, messages ...*spi.Event) error { + assert.Len(t, messages, 1) + assert.Equal(t, spi.VerifierOIDCInteractionQRScanned, messages[0].Type) + + return nil + }) + c := NewController(&Config{ OIDCVPService: svc, + EventSvc: mockEventSvc, + EventTopic: spi.VerifierEventTopic, Tracer: nooptracer.NewTracerProvider().Tracer(""), }) @@ -1019,8 +1020,19 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { ErrorDescription: "unsupported client_id_scheme", }).Return(errors.New("handle wallet notification error")) + mockEventSvc := NewMockeventService(gomock.NewController(t)) + mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(1).DoAndReturn( + func(ctx context.Context, topic string, messages ...*spi.Event) error { + assert.Len(t, messages, 1) + assert.Equal(t, spi.VerifierOIDCInteractionQRScanned, messages[0].Type) + + return nil + }) + c := NewController(&Config{ OIDCVPService: svc, + EventSvc: mockEventSvc, + EventTopic: spi.VerifierEventTopic, Tracer: nooptracer.NewTracerProvider().Tracer(""), }) @@ -1065,14 +1077,12 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { ctx := createContextApplicationForm([]byte(body)) mockEventSvc := NewMockeventService(gomock.NewController(t)) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(1).DoAndReturn( + mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(2).DoAndReturn( func(ctx context.Context, topic string, messages ...*spi.Event) error { assert.Len(t, messages, 1) msg := messages[0] - assert.Equal(t, msg.Type, spi.VerifierOIDCInteractionFailed) - ep := &oidc4ci.EventPayload{} jsonData, err := json.Marshal(msg.Data) @@ -1080,8 +1090,17 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { assert.NoError(t, json.Unmarshal(jsonData, ep)) - assert.Equal(t, "bad_request", ep.ErrorCode) - assert.Equal(t, resterr.VerifierOIDC4vpSvcComponent, resterr.Component(ep.ErrorComponent)) + switch msg.Type { + case spi.VerifierOIDCInteractionFailed: + assert.Empty(t, ep.OrgID) + assert.Equal(t, "bad_request", ep.ErrorCode) + assert.Equal(t, resterr.VerifierOIDC4vpSvcComponent, resterr.Component(ep.ErrorComponent)) + assert.Contains(t, ep.Error, "presentation_submission is missed") + case spi.VerifierOIDCInteractionQRScanned: + assert.Empty(t, ep.OrgID) + default: + assert.Fail(t, "unexpected event type %s", msg.Type) + } return nil }, @@ -1138,7 +1157,34 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { ctx := createContextApplicationForm([]byte(body)) mockEventSvc := NewMockeventService(gomock.NewController(t)) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(1) + mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(2).DoAndReturn( + func(ctx context.Context, topic string, messages ...*spi.Event) error { + assert.Len(t, messages, 1) + + msg := messages[0] + + ep := &oidc4ci.EventPayload{} + + jsonData, err := json.Marshal(msg.Data) + assert.NoError(t, err) + + assert.NoError(t, json.Unmarshal(jsonData, ep)) + + switch msg.Type { + case spi.VerifierOIDCInteractionFailed: + assert.Empty(t, ep.OrgID) + assert.Equal(t, "bad_request", ep.ErrorCode) + assert.Equal(t, resterr.VerifierOIDC4vpSvcComponent, resterr.Component(ep.ErrorComponent)) + assert.Contains(t, ep.Error, "invalid character") + case spi.VerifierOIDCInteractionQRScanned: + assert.Empty(t, ep.OrgID) + default: + assert.Fail(t, "unexpected event type %s", msg.Type) + } + + return nil + }, + ) c := NewController(&Config{ VDR: signedClaimsJWTResult.VDR, @@ -1154,12 +1200,12 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { requireOidc4VpError(t, "bad_request", resterr.VerifierOIDC4vpSvcComponent, "", "presentation_submission", err) }) - t.Run("Nonce different", func(t *testing.T) { + t.Run("ID token expired", func(t *testing.T) { signedClaimsJWTResult := testutil.SignedClaimsJWT(t, &IDTokenClaims{ Nonce: validNonce, Aud: validAud, - Exp: time.Now().Unix() + 1000, + Exp: 0, }, ) @@ -1167,10 +1213,10 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { signedClaimsJWTResult.VerMethodDIDKeyID, signedClaimsJWTResult.Signer, &VPTokenClaims{ - Nonce: "some_invalid", - Aud: validAud, - Exp: time.Now().Unix() + 1000, + Nonce: validNonce, + Aud: "some_invalid", Iss: signedClaimsJWTResult.VerMethodDID, + Exp: time.Now().Unix() + 1000, VP: &verifiable.Presentation{ Context: []string{ "https://www.w3.org/2018/credentials/v1", @@ -1195,7 +1241,34 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { ctx := createContextApplicationForm([]byte(body)) mockEventSvc := NewMockeventService(gomock.NewController(t)) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(1) + mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(2).DoAndReturn( + func(ctx context.Context, topic string, messages ...*spi.Event) error { + assert.Len(t, messages, 1) + + msg := messages[0] + + ep := &oidc4ci.EventPayload{} + + jsonData, err := json.Marshal(msg.Data) + assert.NoError(t, err) + + assert.NoError(t, json.Unmarshal(jsonData, ep)) + + switch msg.Type { + case spi.VerifierOIDCInteractionFailed: + assert.Empty(t, ep.OrgID) + assert.Equal(t, "bad_request", ep.ErrorCode) + assert.Equal(t, resterr.VerifierOIDC4vpSvcComponent, resterr.Component(ep.ErrorComponent)) + assert.Contains(t, ep.Error, "token expired") + case spi.VerifierOIDCInteractionQRScanned: + assert.Empty(t, ep.OrgID) + default: + assert.Fail(t, "unexpected event type %s", msg.Type) + } + + return nil + }, + ) c := NewController(&Config{ VDR: signedClaimsJWTResult.VDR, @@ -1207,10 +1280,10 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { }) err = c.CheckAuthorizationResponse(ctx) - requireOidc4VpError(t, "bad_request", resterr.VerifierOIDC4vpSvcComponent, "", "nonce", err) + requireOidc4VpError(t, "bad_request", resterr.VerifierOIDC4vpSvcComponent, "", "id_token.exp", err) }) - t.Run("Aud different", func(t *testing.T) { + t.Run("Nonce different", func(t *testing.T) { signedClaimsJWTResult := testutil.SignedClaimsJWT(t, &IDTokenClaims{ Nonce: validNonce, @@ -1223,8 +1296,8 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { signedClaimsJWTResult.VerMethodDIDKeyID, signedClaimsJWTResult.Signer, &VPTokenClaims{ - Nonce: validNonce, - Aud: "some_invalid", + Nonce: "some_invalid", + Aud: validAud, Exp: time.Now().Unix() + 1000, Iss: signedClaimsJWTResult.VerMethodDID, VP: &verifiable.Presentation{ @@ -1251,7 +1324,34 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { ctx := createContextApplicationForm([]byte(body)) mockEventSvc := NewMockeventService(gomock.NewController(t)) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(1) + mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(2).DoAndReturn( + func(ctx context.Context, topic string, messages ...*spi.Event) error { + assert.Len(t, messages, 1) + + msg := messages[0] + + ep := &oidc4ci.EventPayload{} + + jsonData, err := json.Marshal(msg.Data) + assert.NoError(t, err) + + assert.NoError(t, json.Unmarshal(jsonData, ep)) + + switch msg.Type { + case spi.VerifierOIDCInteractionFailed: + assert.Empty(t, ep.OrgID) + assert.Equal(t, "bad_request", ep.ErrorCode) + assert.Equal(t, resterr.VerifierOIDC4vpSvcComponent, resterr.Component(ep.ErrorComponent)) + assert.Contains(t, ep.Error, "nonce should be the same for both id_token and vp_token") + case spi.VerifierOIDCInteractionQRScanned: + assert.Empty(t, ep.OrgID) + default: + assert.Fail(t, "unexpected event type %s", msg.Type) + } + + return nil + }, + ) c := NewController(&Config{ VDR: signedClaimsJWTResult.VDR, @@ -1263,16 +1363,15 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { }) err = c.CheckAuthorizationResponse(ctx) - - requireOidc4VpError(t, "bad_request", resterr.VerifierOIDC4vpSvcComponent, "", "aud", err) + requireOidc4VpError(t, "bad_request", resterr.VerifierOIDC4vpSvcComponent, "", "nonce", err) }) - t.Run("ID token expired", func(t *testing.T) { + t.Run("Aud different", func(t *testing.T) { signedClaimsJWTResult := testutil.SignedClaimsJWT(t, &IDTokenClaims{ Nonce: validNonce, Aud: validAud, - Exp: 0, + Exp: time.Now().Unix() + 1000, }, ) @@ -1282,8 +1381,8 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { &VPTokenClaims{ Nonce: validNonce, Aud: "some_invalid", - Iss: signedClaimsJWTResult.VerMethodDID, Exp: time.Now().Unix() + 1000, + Iss: signedClaimsJWTResult.VerMethodDID, VP: &verifiable.Presentation{ Context: []string{ "https://www.w3.org/2018/credentials/v1", @@ -1308,7 +1407,34 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { ctx := createContextApplicationForm([]byte(body)) mockEventSvc := NewMockeventService(gomock.NewController(t)) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(1) + mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(2).DoAndReturn( + func(ctx context.Context, topic string, messages ...*spi.Event) error { + assert.Len(t, messages, 1) + + msg := messages[0] + + ep := &oidc4ci.EventPayload{} + + jsonData, err := json.Marshal(msg.Data) + assert.NoError(t, err) + + assert.NoError(t, json.Unmarshal(jsonData, ep)) + + switch msg.Type { + case spi.VerifierOIDCInteractionFailed: + assert.Empty(t, ep.OrgID) + assert.Equal(t, "bad_request", ep.ErrorCode) + assert.Equal(t, resterr.VerifierOIDC4vpSvcComponent, resterr.Component(ep.ErrorComponent)) + assert.Contains(t, ep.Error, "aud should be the same for both id_token and vp_token") + case spi.VerifierOIDCInteractionQRScanned: + assert.Empty(t, ep.OrgID) + default: + assert.Fail(t, "unexpected event type %s", msg.Type) + } + + return nil + }, + ) c := NewController(&Config{ VDR: signedClaimsJWTResult.VDR, @@ -1320,7 +1446,8 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { }) err = c.CheckAuthorizationResponse(ctx) - requireOidc4VpError(t, "bad_request", resterr.VerifierOIDC4vpSvcComponent, "", "id_token.exp", err) + + requireOidc4VpError(t, "bad_request", resterr.VerifierOIDC4vpSvcComponent, "", "aud", err) }) t.Run("ID token invalid signature", func(t *testing.T) { @@ -1363,7 +1490,34 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { ctx := createContextApplicationForm([]byte(body)) mockEventSvc := NewMockeventService(gomock.NewController(t)) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(1) + mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(2).DoAndReturn( + func(ctx context.Context, topic string, messages ...*spi.Event) error { + assert.Len(t, messages, 1) + + msg := messages[0] + + ep := &oidc4ci.EventPayload{} + + jsonData, err := json.Marshal(msg.Data) + assert.NoError(t, err) + + assert.NoError(t, json.Unmarshal(jsonData, ep)) + + switch msg.Type { + case spi.VerifierOIDCInteractionFailed: + assert.Empty(t, ep.OrgID) + assert.Equal(t, "bad_request", ep.ErrorCode) + assert.Equal(t, resterr.VerifierOIDC4vpSvcComponent, resterr.Component(ep.ErrorComponent)) + assert.Contains(t, ep.Error, "invalid public key id: public key with KID") + case spi.VerifierOIDCInteractionQRScanned: + assert.Empty(t, ep.OrgID) + default: + assert.Fail(t, "unexpected event type %s", msg.Type) + } + + return nil + }, + ) c := NewController(&Config{ // Using different key in controller. @@ -1420,7 +1574,34 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { ctx := createContextApplicationForm([]byte(body)) mockEventSvc := NewMockeventService(gomock.NewController(t)) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(1) + mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(2).DoAndReturn( + func(ctx context.Context, topic string, messages ...*spi.Event) error { + assert.Len(t, messages, 1) + + msg := messages[0] + + ep := &oidc4ci.EventPayload{} + + jsonData, err := json.Marshal(msg.Data) + assert.NoError(t, err) + + assert.NoError(t, json.Unmarshal(jsonData, ep)) + + switch msg.Type { + case spi.VerifierOIDCInteractionFailed: + assert.Empty(t, ep.OrgID) + assert.Equal(t, "bad_request", ep.ErrorCode) + assert.Equal(t, resterr.VerifierOIDC4vpSvcComponent, resterr.Component(ep.ErrorComponent)) + assert.Contains(t, ep.Error, "token expired") + case spi.VerifierOIDCInteractionQRScanned: + assert.Empty(t, ep.OrgID) + default: + assert.Fail(t, "unexpected event type %s", msg.Type) + } + + return nil + }, + ) c := NewController(&Config{ VDR: signedClaimsJWTResult.VDR, @@ -1475,7 +1656,34 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { ctx := createContextApplicationForm([]byte(body)) mockEventSvc := NewMockeventService(gomock.NewController(t)) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(1) + mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(2).DoAndReturn( + func(ctx context.Context, topic string, messages ...*spi.Event) error { + assert.Len(t, messages, 1) + + msg := messages[0] + + ep := &oidc4ci.EventPayload{} + + jsonData, err := json.Marshal(msg.Data) + assert.NoError(t, err) + + assert.NoError(t, json.Unmarshal(jsonData, ep)) + + switch msg.Type { + case spi.VerifierOIDCInteractionFailed: + assert.Empty(t, ep.OrgID) + assert.Equal(t, "bad_request", ep.ErrorCode) + assert.Equal(t, resterr.VerifierOIDC4vpSvcComponent, resterr.Component(ep.ErrorComponent)) + assert.Contains(t, ep.Error, "parse and check proof: invalid public key id: public key with KID") + case spi.VerifierOIDCInteractionQRScanned: + assert.Empty(t, ep.OrgID) + default: + assert.Fail(t, "unexpected event type %s", msg.Type) + } + + return nil + }, + ) c := NewController(&Config{ // Using different key in controller. @@ -1523,7 +1731,34 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { ctx := createContextApplicationForm([]byte(body)) mockEventSvc := NewMockeventService(gomock.NewController(t)) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(1) + mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(2).DoAndReturn( + func(ctx context.Context, topic string, messages ...*spi.Event) error { + assert.Len(t, messages, 1) + + msg := messages[0] + + ep := &oidc4ci.EventPayload{} + + jsonData, err := json.Marshal(msg.Data) + assert.NoError(t, err) + + assert.NoError(t, json.Unmarshal(jsonData, ep)) + + switch msg.Type { + case spi.VerifierOIDCInteractionFailed: + assert.Empty(t, ep.OrgID) + assert.Equal(t, "bad_request", ep.ErrorCode) + assert.Equal(t, resterr.VerifierOIDC4vpSvcComponent, resterr.Component(ep.ErrorComponent)) + assert.Contains(t, ep.Error, "parse presentation: @context is required") + case spi.VerifierOIDCInteractionQRScanned: + assert.Empty(t, ep.OrgID) + default: + assert.Fail(t, "unexpected event type %s", msg.Type) + } + + return nil + }, + ) c := NewController(&Config{ VDR: signedClaimsJWTResult.VDR, @@ -1582,7 +1817,34 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { ctx := createContextApplicationForm([]byte(body)) mockEventSvc := NewMockeventService(gomock.NewController(t)) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(1) + mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(2).DoAndReturn( + func(ctx context.Context, topic string, messages ...*spi.Event) error { + assert.Len(t, messages, 1) + + msg := messages[0] + + ep := &oidc4ci.EventPayload{} + + jsonData, err := json.Marshal(msg.Data) + assert.NoError(t, err) + + assert.NoError(t, json.Unmarshal(jsonData, ep)) + + switch msg.Type { + case spi.VerifierOIDCInteractionFailed: + assert.Empty(t, ep.OrgID) + assert.Equal(t, "bad_request", ep.ErrorCode) + assert.Equal(t, resterr.VerifierOIDC4vpSvcComponent, resterr.Component(ep.ErrorComponent)) + assert.Contains(t, ep.Error, "check linked data proof: proof invalid public key id: public key with KID") + case spi.VerifierOIDCInteractionQRScanned: + assert.Empty(t, ep.OrgID) + default: + assert.Fail(t, "unexpected event type %s", msg.Type) + } + + return nil + }, + ) c := NewController(&Config{ OIDCVPService: svc, @@ -1641,7 +1903,35 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { ctx := createContextApplicationForm([]byte(body)) mockEventSvc := NewMockeventService(gomock.NewController(t)) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(1) + mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(2).DoAndReturn( + func(ctx context.Context, topic string, messages ...*spi.Event) error { + assert.Len(t, messages, 1) + + msg := messages[0] + + ep := &oidc4ci.EventPayload{} + + jsonData, err := json.Marshal(msg.Data) + assert.NoError(t, err) + + assert.NoError(t, json.Unmarshal(jsonData, ep)) + + switch msg.Type { + case spi.VerifierOIDCInteractionFailed: + assert.Empty(t, ep.OrgID) + assert.Equal(t, "bad_request", ep.ErrorCode) + assert.Equal(t, resterr.VerifierOIDC4vpSvcComponent, resterr.Component(ep.ErrorComponent)) + assert.Contains(t, ep.Error, "bad_request[component: verifier.oidc4vp-service; "+ + "incorrect value: vp_token.challenge; http status: 400]: missed field") + case spi.VerifierOIDCInteractionQRScanned: + assert.Empty(t, ep.OrgID) + default: + assert.Fail(t, "unexpected event type %s", msg.Type) + } + + return nil + }, + ) c := NewController(&Config{ OIDCVPService: svc, @@ -1700,7 +1990,35 @@ func TestController_CheckAuthorizationResponse(t *testing.T) { ctx := createContextApplicationForm([]byte(body)) mockEventSvc := NewMockeventService(gomock.NewController(t)) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(1) + mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(2).DoAndReturn( + func(ctx context.Context, topic string, messages ...*spi.Event) error { + assert.Len(t, messages, 1) + + msg := messages[0] + + ep := &oidc4ci.EventPayload{} + + jsonData, err := json.Marshal(msg.Data) + assert.NoError(t, err) + + assert.NoError(t, json.Unmarshal(jsonData, ep)) + + switch msg.Type { + case spi.VerifierOIDCInteractionFailed: + assert.Empty(t, ep.OrgID) + assert.Equal(t, "bad_request", ep.ErrorCode) + assert.Equal(t, resterr.VerifierOIDC4vpSvcComponent, resterr.Component(ep.ErrorComponent)) + assert.Contains(t, ep.Error, "bad_request[component: verifier.oidc4vp-service; "+ + "incorrect value: vp_token.domain; http status: 400]: missed field") + case spi.VerifierOIDCInteractionQRScanned: + assert.Empty(t, ep.OrgID) + default: + assert.Fail(t, "unexpected event type %s", msg.Type) + } + + return nil + }, + ) c := NewController(&Config{ OIDCVPService: svc, diff --git a/pkg/service/oidc4vp/oidc4vp_service.go b/pkg/service/oidc4vp/oidc4vp_service.go index 49962c36a..bed34e7c6 100644 --- a/pkg/service/oidc4vp/oidc4vp_service.go +++ b/pkg/service/oidc4vp/oidc4vp_service.go @@ -188,7 +188,7 @@ func NewService(cfg *Config) *Service { } } -func (s *Service) sendTxEvent( +func (s *Service) sendTxEvent( //nolint:unused ctx context.Context, eventType spi.EventType, tx *Transaction, @@ -469,10 +469,6 @@ func (s *Service) VerifyOIDCVerifiablePresentation( return oidc4vperr.NewBadRequestError(err).WithErrorPrefix("getProfile") } - if err = s.sendTxEvent(ctx, spi.VerifierOIDCInteractionQRScanned, tx, profile); err != nil { - return oidc4vperr.NewBadRequestError(err).WithErrorPrefix("send event") - } - logger.Debugc(ctx, "VerifyOIDCVerifiablePresentation profile fetched", logfields.WithProfileID(profile.ID)) policyChan := make(chan error) diff --git a/pkg/service/oidc4vp/oidc4vp_service_test.go b/pkg/service/oidc4vp/oidc4vp_service_test.go index e9eb51432..9d5f593e1 100644 --- a/pkg/service/oidc4vp/oidc4vp_service_test.go +++ b/pkg/service/oidc4vp/oidc4vp_service_test.go @@ -488,10 +488,6 @@ func TestService_VerifyOIDCVerifiablePresentation(t *testing.T) { assert.False(t, ok) } - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).DoAndReturn( - expectedPublishEventFunc(t, spi.VerifierOIDCInteractionQRScanned, nil, checkFn), - ) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).DoAndReturn( expectedPublishEventFunc(t, spi.VerifierOIDCInteractionSucceeded, nil, checkFn), ) @@ -572,16 +568,6 @@ func TestService_VerifyOIDCVerifiablePresentation(t *testing.T) { txManager2 := NewMockTransactionManager(gomock.NewController(t)) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).DoAndReturn( - expectedPublishEventFunc(t, spi.VerifierOIDCInteractionQRScanned, nil, func(t *testing.T, e *spi.Event) { - ep, ok := e.Data.(map[string]interface{}) - assert.True(t, ok) - - _, ok = ep["interaction_details"] - assert.False(t, ok) - }), - ) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).DoAndReturn( expectedPublishEventFunc(t, spi.VerifierOIDCInteractionSucceeded, nil, func(t *testing.T, e *spi.Event) { ep, ok := e.Data.(map[string]interface{}) @@ -659,10 +645,6 @@ func TestService_VerifyOIDCVerifiablePresentation(t *testing.T) { }) t.Run("Unsupported vp token format", func(t *testing.T) { - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).DoAndReturn( - expectedPublishEventFunc(t, spi.VerifierOIDCInteractionQRScanned, nil), - ) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).DoAndReturn( expectedPublishEventFunc(t, spi.VerifierOIDCInteractionFailed, nil, func(t *testing.T, e *spi.Event) { epData, ok := e.Data.(map[string]interface{}) @@ -753,10 +735,6 @@ func TestService_VerifyOIDCVerifiablePresentation(t *testing.T) { txManager2 := NewMockTransactionManager(gomock.NewController(t)) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).DoAndReturn( - expectedPublishEventFunc(t, spi.VerifierOIDCInteractionQRScanned, nil), - ) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).DoAndReturn( expectedPublishEventFunc(t, spi.VerifierOIDCInteractionFailed, nil, func(t *testing.T, e *spi.Event) { epData, ok := e.Data.(map[string]interface{}) @@ -846,10 +824,6 @@ func TestService_VerifyOIDCVerifiablePresentation(t *testing.T) { }) t.Run("VC subject is not much with vp signer", func(t *testing.T) { - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).DoAndReturn( - expectedPublishEventFunc(t, spi.VerifierOIDCInteractionQRScanned, nil), - ) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).DoAndReturn( expectedPublishEventFunc(t, spi.VerifierOIDCInteractionFailed, nil, func(t *testing.T, e *spi.Event) { epData, ok := e.Data.(map[string]interface{}) @@ -1040,10 +1014,6 @@ func TestService_VerifyOIDCVerifiablePresentation(t *testing.T) { }) t.Run("verification failed", func(t *testing.T) { - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).DoAndReturn( - expectedPublishEventFunc(t, spi.VerifierOIDCInteractionQRScanned, nil), - ) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).DoAndReturn( expectedPublishEventFunc(t, spi.VerifierOIDCInteractionFailed, nil, func(t *testing.T, e *spi.Event) { epData, ok := e.Data.(map[string]interface{}) @@ -1095,10 +1065,6 @@ func TestService_VerifyOIDCVerifiablePresentation(t *testing.T) { }) t.Run("Match failed", func(t *testing.T) { - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).DoAndReturn( - expectedPublishEventFunc(t, spi.VerifierOIDCInteractionQRScanned, nil), - ) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).DoAndReturn( expectedPublishEventFunc(t, spi.VerifierOIDCInteractionFailed, nil, func(t *testing.T, e *spi.Event) { epData, ok := e.Data.(map[string]interface{}) @@ -1153,10 +1119,6 @@ func TestService_VerifyOIDCVerifiablePresentation(t *testing.T) { errTxManager.EXPECT().StoreReceivedClaims(oidc4vp.TxID("txID1"), gomock.Any(), int32(20), int32(10)). Return(errors.New("store error")) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).DoAndReturn( - expectedPublishEventFunc(t, spi.VerifierOIDCInteractionQRScanned, nil), - ) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).DoAndReturn( expectedPublishEventFunc(t, spi.VerifierOIDCInteractionFailed, nil, func(t *testing.T, e *spi.Event) { epData, ok := e.Data.(map[string]interface{}) @@ -1199,10 +1161,6 @@ func TestService_VerifyOIDCVerifiablePresentation(t *testing.T) { errTrustRegistry.EXPECT().ValidatePresentation(gomock.Any(), gomock.Any(), gomock.Any()). AnyTimes().Return(errors.New("validate error")) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).DoAndReturn( - expectedPublishEventFunc(t, spi.VerifierOIDCInteractionQRScanned, nil), - ) - withError := oidc4vp.NewService(&oidc4vp.Config{ EventSvc: mockEventSvc, EventTopic: spi.VerifierEventTopic, @@ -1242,14 +1200,12 @@ func TestService_VerifyOIDCVerifiablePresentation(t *testing.T) { errExpected := errors.New("injected publish error") mockEventSvc := NewMockeventService(gomock.NewController(t)) - mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(2). + mockEventSvc.EXPECT().Publish(gomock.Any(), spi.VerifierEventTopic, gomock.Any()).Times(1). DoAndReturn( func(ctx context.Context, topic string, messages ...*spi.Event) error { assert.Len(t, messages, 1) switch messages[0].Type { //nolint:exhaustive - case spi.VerifierOIDCInteractionQRScanned: - return nil case spi.VerifierOIDCInteractionSucceeded: return errExpected default: