diff --git a/backend/v3/storage/database/dialect/postgres/migration/013_user_table/up.sql b/backend/v3/storage/database/dialect/postgres/migration/013_user_table/up.sql index 184fdeb78..440af9bb5 100644 --- a/backend/v3/storage/database/dialect/postgres/migration/013_user_table/up.sql +++ b/backend/v3/storage/database/dialect/postgres/migration/013_user_table/up.sql @@ -185,7 +185,7 @@ CREATE TYPE zitadel.passkey_type AS ENUM ( CREATE TABLE zitadel.user_passkeys( instance_id TEXT NOT NULL , token_id TEXT NOT NULL - , key_id BYTEA NOT NULL + , key_id BYTEA , user_id TEXT NOT NULL @@ -194,12 +194,12 @@ CREATE TABLE zitadel.user_passkeys( , verified_at TIMESTAMPTZ , type zitadel.passkey_type NOT NULL - , name TEXT NOT NULL CHECK (name <> '') + , name TEXT NOT NULL DEFAULT '' , sign_count INT NOT NULL DEFAULT 0 CHECK (sign_count >= 0) , challenge BYTEA NOT NULL - , public_key BYTEA NOT NULL - , attestation_type TEXT NOT NULL CHECK (attestation_type <> '') - , authenticator_attestation_guid BYTEA NOT NULL + , public_key BYTEA + , attestation_type TEXT NOT NULL + , authenticator_attestation_guid BYTEA , relying_party_id TEXT NOT NULL CHECK (relying_party_id <> '') , PRIMARY KEY (instance_id, token_id) diff --git a/internal/query/projection/assert.go b/internal/query/projection/assert.go index 96ed19543..7705b867b 100644 --- a/internal/query/projection/assert.go +++ b/internal/query/projection/assert.go @@ -1,6 +1,8 @@ package projection import ( + "fmt" + "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/zerrors" ) @@ -8,7 +10,7 @@ import ( func assertEvent[T eventstore.Event](event eventstore.Event) (T, error) { e, ok := event.(T) if !ok { - return e, zerrors.ThrowInvalidArgumentf(nil, "HANDL-1m9fS", "reduce.wrong.event.type %T", event) + return e, zerrors.CreateZitadelError(zerrors.KindInvalidArgument, nil, "HANDL-1m9fS", fmt.Sprintf("reduce.wrong.event.type %T", event), 1) } return e, nil } diff --git a/internal/query/projection/user_relational.go b/internal/query/projection/user_relational.go index 5448a496a..12d8c02a6 100644 --- a/internal/query/projection/user_relational.go +++ b/internal/query/projection/user_relational.go @@ -1124,6 +1124,7 @@ func (p *relationalTablesProjection) reducePasskeyVerified(event eventstore.Even repo.SetPasskeySignCount(e.SignCount), repo.SetPasskeyName(e.WebAuthNTokenName), repo.SetPasskeyUpdatedAt(e.CreatedAt()), + repo.SetPasskeyVerifiedAt(e.CreatedAt()), ), repo.SetUpdatedAt(e.CreatedAt()), ) diff --git a/internal/query/projection/user_relational_test.go b/internal/query/projection/user_relational_test.go index 1328e852d..ebd33edd9 100644 --- a/internal/query/projection/user_relational_test.go +++ b/internal/query/projection/user_relational_test.go @@ -181,6 +181,66 @@ func TestUserRelationalProjection_Reducers(t *testing.T) { assert.Zero(t, gotUser.Human.RecoveryCodes.LastSuccessfullyCheckedAt) assert.Equal(t, uint8(1), gotUser.Human.RecoveryCodes.FailedAttempts) }) + + t.Run("reduce user.HumanWebAuthNVerifiedEvent event", func(t *testing.T) { + // Given + + // create user + existingUserID := gofakeit.UUID() + existingUserAgg := createUser(t, tx, userRepo, instanceID, orgID, existingUserID) + now := time.Now() + + // add passkey to the user + pkey := &domain.Passkey{ + ID: "pkey-id", + Challenge: []byte("some challenge"), + RelyingPartyID: "rpID", + CreatedAt: now, + UpdatedAt: now, + Type: domain.PasskeyTypePasswordless, + } + + _, err = userRepo.Update(t.Context(), tx, + userRepo.PrimaryKeyCondition(instanceID, existingUserID), + userRepo.Human().AddPasskey(pkey), + ) + require.NoError(t, err) + + pkeyVerifiedEvt := user.NewHumanPasswordlessVerifiedEvent( + t.Context(), + &existingUserAgg.Aggregate, + pkey.ID, + "some-token-name", + "attestation-type", + []byte("key-id"), + []byte("public-key-id"), + []byte("authenticator-attestation-guid"), + 33, + "", + ) + + // Test + eventReduced := callReduce(t, rawTx, handler, pkeyVerifiedEvt) + require.True(t, eventReduced) + + // Verify + gotUser, err := userRepo.Get(t.Context(), tx, database.WithCondition( + database.And( + userRepo.IDCondition(existingUserID), + userRepo.InstanceIDCondition(instanceID), + ), + )) + require.NoError(t, err) + require.Len(t, gotUser.Human.Passkeys, 1) + gotPKey := gotUser.Human.Passkeys[0] + assert.Equal(t, pkeyVerifiedEvt.KeyID, gotPKey.KeyID) + assert.Equal(t, pkeyVerifiedEvt.PublicKey, gotPKey.PublicKey) + assert.Equal(t, pkeyVerifiedEvt.AttestationType, gotPKey.AttestationType) + assert.Equal(t, pkeyVerifiedEvt.AAGUID, gotPKey.AuthenticatorAttestationGUID) + assert.Equal(t, pkeyVerifiedEvt.SignCount, gotPKey.SignCount) + assert.Equal(t, pkeyVerifiedEvt.WebAuthNTokenName, gotPKey.Name) + assert.NotZero(t, gotPKey.VerifiedAt) + }) } func createUser(t *testing.T,