fixed verificatoin

This commit is contained in:
Stephan D
2026-02-12 20:26:10 +01:00
parent 57914c0754
commit fcbfa323c8
2 changed files with 77 additions and 0 deletions

View File

@@ -3,6 +3,7 @@ package verificationimp
import (
"context"
"errors"
"strings"
"sync"
"testing"
"time"
@@ -101,6 +102,27 @@ func (m *memoryTokenRepository) Insert(_ context.Context, obj storable.Storable,
if _, exists := m.data[*id]; exists {
return merrors.DataConflict("token already exists")
}
for _, existing := range m.data {
if existing.VerifyTokenHash == tok.VerifyTokenHash {
return merrors.DataConflict("duplicate verifyTokenHash")
}
if existing.AccountRef != tok.AccountRef {
continue
}
if existing.Purpose != tok.Purpose {
continue
}
if existing.Target != tok.Target {
continue
}
switch {
case existing.IdempotencyKey == nil && tok.IdempotencyKey == nil:
return merrors.DataConflict("duplicate verification context idempotency")
case existing.IdempotencyKey != nil && tok.IdempotencyKey != nil && *existing.IdempotencyKey == *tok.IdempotencyKey:
return merrors.DataConflict("duplicate verification context idempotency")
}
}
m.data[*id] = cloneToken(tok)
m.order = append(m.order, *id)
return nil
@@ -633,6 +655,49 @@ func TestCreate_InvalidatesPreviousToken(t *testing.T) {
assert.Equal(t, accountRef, tok.AccountRef)
}
func TestCreate_AccountActivationResendWithoutIdempotency_ReissuesToken(t *testing.T) {
db := newTestVerificationDB(t)
ctx := context.Background()
accountRef := bson.NewObjectID()
// First issue during signup.
firstRaw, err := db.Create(ctx, req(accountRef, model.PurposeAccountActivation, "", time.Hour))
require.NoError(t, err)
// Second issue during resend should rotate token instead of failing with duplicate key.
secondRaw, err := db.Create(ctx, req(accountRef, model.PurposeAccountActivation, "", time.Hour))
require.NoError(t, err)
assert.NotEqual(t, firstRaw, secondRaw)
// Old token becomes unusable after reissue.
_, err = db.Consume(ctx, bson.NilObjectID, model.PurposeAccountActivation, firstRaw)
require.Error(t, err)
assert.True(t, errors.Is(err, verification.ErrTokenAlreadyUsed))
// New token is valid.
tok, err := db.Consume(ctx, bson.NilObjectID, model.PurposeAccountActivation, secondRaw)
require.NoError(t, err)
assert.Equal(t, accountRef, tok.AccountRef)
assert.Equal(t, model.PurposeAccountActivation, tok.Purpose)
// Non-idempotent requests should still persist unique internal keys,
// preventing uniqueness collisions on (accountRef, purpose, target, idempotencyKey).
repo := db.Repository.(*memoryTokenRepository)
repo.mu.Lock()
defer repo.mu.Unlock()
keys := map[string]struct{}{}
for _, stored := range repo.data {
if stored.AccountRef != accountRef || stored.Purpose != model.PurposeAccountActivation {
continue
}
require.NotNil(t, stored.IdempotencyKey)
assert.True(t, strings.HasPrefix(*stored.IdempotencyKey, "auto:"))
keys[*stored.IdempotencyKey] = struct{}{}
}
assert.Len(t, keys, 2)
}
func TestCreate_InvalidatesMultiplePreviousTokens(t *testing.T) {
db := newTestVerificationDB(t)
ctx := context.Background()