Repository: zitadel/zitadel (2025-07-08T10:43:46Z)

Issue:
[Bug]: Broken resend code in login v1
### Preflight Checklist

- [x] I could not find a solution in the documentation, the existing issues or discussions
- [x] I have joined the [ZITADEL chat](https://zitadel.com/chat)

### Environment

ZITADEL Cloud

### Version

v3.2.2

### Database

None

### Database Version

_No response_

### Describe the problem caused by this bug

Resend Code returns error `membership not found` (AUTHZ-cdgFK) in the Activate User screen in login v1. 

### To reproduce

1. created a fresh instance 
2. created a user using `users.v2.AddHumanUser` (without verified email)
3. Send invite code using `users.v2.CreateInviteCode` and pass an app name. 
4. Go to login (v1)
5. enter the login name of the newly created user
6. On the Activate user screen, press the "Resend Code" button


### Screenshots

![Image](https://github.com/user-attachments/assets/188668bd-923d-4008-a469-6809f9b2ec97)

### Expected behavior

The code should be resent and no error returned

### Operating System

_No response_

### Relevant Configuration

default qa instance

### Additional Context

Relates to https://github.com/zitadel/zitadel/issues/9860


Discussion:
Same issue in v3.3.0.
Same issue here …
v3.3.0

Please send a workaround, so I can start …
Issue still exists in v3.3.2, does this get backported?



Patch:
diff --git a/internal/command/user_v2_invite.go b/internal/command/user_v2_invite.go
index 430ba8c7d1..10948164e9 100644
--- a/internal/command/user_v2_invite.go
+++ b/internal/command/user_v2_invite.go
@@ -6,6 +6,7 @@ import (
 
 	"github.com/zitadel/logging"
 
+	"github.com/zitadel/zitadel/internal/api/authz"
 	"github.com/zitadel/zitadel/internal/crypto"
 	"github.com/zitadel/zitadel/internal/domain"
 	"github.com/zitadel/zitadel/internal/eventstore"
@@ -50,8 +51,10 @@ func (c *Commands) sendInviteCode(ctx context.Context, invite *CreateUserInvite,
 	if err != nil {
 		return nil, nil, err
 	}
-	if err := c.checkPermission(ctx, domain.PermissionUserWrite, wm.ResourceOwner, wm.AggregateID); err != nil {
-		return nil, nil, err
+	if wm.AggregateID != authz.GetCtxData(ctx).UserID {
+		if err := c.checkPermission(ctx, domain.PermissionUserWrite, wm.ResourceOwner, wm.AggregateID); err != nil {
+			return nil, nil, err
+		}
 	}
 	if !wm.UserState.Exists() {
 		return nil, nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-Wgvn4", "Errors.User.NotFound")


Test patch:
diff --git a/internal/command/user_v2_invite_test.go b/internal/command/user_v2_invite_test.go
index 75bd3157db..53ad1bd944 100644
--- a/internal/command/user_v2_invite_test.go
+++ b/internal/command/user_v2_invite_test.go
@@ -11,6 +11,7 @@ import (
 	"go.uber.org/mock/gomock"
 	"golang.org/x/text/language"
 
+	"github.com/zitadel/zitadel/internal/api/authz"
 	"github.com/zitadel/zitadel/internal/crypto"
 	"github.com/zitadel/zitadel/internal/domain"
 	"github.com/zitadel/zitadel/internal/eventstore"
@@ -205,6 +206,64 @@ func TestCommands_CreateInviteCode(t *testing.T) {
 				returnCode: gu.Ptr("code"),
 			},
 		},
+		{
+			"return ok, with same user requests code",
+			fields{
+				eventstore: expectEventstore(
+					expectFilter(
+						eventFromEventPusher(
+							user.NewHumanAddedEvent(context.Background(),
+								&user.NewAggregate("userID", "org1").Aggregate,
+								"username", "firstName",
+								"lastName",
+								"nickName",
+								"displayName",
+								language.Afrikaans,
+								domain.GenderUnspecified,
+								"email",
+								false,
+							),
+						),
+					),
+					expectPush(
+						eventFromEventPusher(
+							user.NewHumanInviteCodeAddedEvent(authz.SetCtxData(context.Background(), authz.CtxData{UserID: "userID"}),
+								&user.NewAggregate("userID", "org1").Aggregate,
+								&crypto.CryptoValue{
+									CryptoType: crypto.TypeEncryption,
+									Algorithm:  "enc",
+									KeyID:      "id",
+									Crypted:    []byte("code"),
+								},
+								time.Hour,
+								"",
+								true,
+								"",
+								"",
+							),
+						),
+					),
+				),
+				// we do not run checkPermission() because the same user is requesting the code as the user to which the code is intended for
+				checkPermission:             nil,
+				newEncryptedCodeWithDefault: mockEncryptedCodeWithDefault("code", time.Hour),
+				defaultSecretGenerators:     &SecretGenerators{},
+			},
+			args{
+				ctx: authz.SetCtxData(context.Background(), authz.CtxData{UserID: "userID"}),
+				invite: &CreateUserInvite{
+					UserID:     "userID",
+					ReturnCode: true,
+				},
+			},
+			want{
+				details: &domain.ObjectDetails{
+					ResourceOwner: "org1",
+					ID:            "userID",
+				},
+				returnCode: gu.Ptr("code"),
+			},
+		},
 		{
 			"with template and application name ok",
 			fields{
@@ -510,6 +569,77 @@ func TestCommands_ResendInviteCode(t *testing.T) {
 				},
 			},
 		},
+		{
+			"return ok, with same user requests code",
+			fields{
+				eventstore: expectEventstore(
+					expectFilter(
+						eventFromEventPusher(
+							user.NewHumanAddedEvent(context.Background(),
+								&user.NewAggregate("userID", "org1").Aggregate,
+								"username", "firstName",
+								"lastName",
+								"nickName",
+								"displayName",
+								language.Afrikaans,
+								domain.GenderUnspecified,
+								"email",
+								false,
+							),
+						),
+						eventFromEventPusher(
+							user.NewHumanInviteCodeAddedEvent(context.Background(),
+								&user.NewAggregate("userID", "org1").Aggregate,
+								&crypto.CryptoValue{
+									CryptoType: crypto.TypeEncryption,
+									Algorithm:  "enc",
+									KeyID:      "id",
+									Crypted:    []byte("code"),
+								},
+								time.Hour,
+								"",
+								false,
+								"",
+								"authRequestID",
+							),
+						),
+					),
+					expectPush(
+						eventFromEventPusher(
+							user.NewHumanInviteCodeAddedEvent(authz.SetCtxData(context.Background(), authz.CtxData{UserID: "userID"}),
+								&user.NewAggregate("userID", "org1").Aggregate,
+								&crypto.CryptoValue{
+									CryptoType: crypto.TypeEncryption,
+									Algorithm:  "enc",
+									KeyID:      "id",
+									Crypted:    []byte("code"),
+								},
+								time.Hour,
+								"",
+								false,
+								"",
+								"authRequestID",
+							),
+						),
+					),
+				),
+				// we do not run checkPermission() because the same user is requesting the code as the user to which the code is intended for
+				checkPermission:             nil,
+				newEncryptedCodeWithDefault: mockEncryptedCodeWithDefault("code", time.Hour),
+				defaultSecretGenerators:     &SecretGenerators{},
+			},
+			args{
+				// ctx:    context.Background(),
+				ctx:    authz.SetCtxData(context.Background(), authz.CtxData{UserID: "userID"}),
+				userID: "userID",
+			},
+			want{
+				details: &domain.ObjectDetails{
+					ResourceOwner: "org1",
+					ID:            "userID",
+				},
+			},
+		},
 		{
 			"resend with new auth requestID ok",
 			fields{