fixed verificatoin
This commit is contained in:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user