From 888838cfb08dd854cf9f9e3d381c770ff6f1c804 Mon Sep 17 00:00:00 2001 From: Bryan Frimin Date: Wed, 18 Mar 2026 19:43:33 +0100 Subject: [PATCH] Fix race condition in magic link token verification and typo in auth error message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Hold SELECT FOR UPDATE lock within transaction by using tx directly instead of separate WithConn, ensuring mutual exclusion when multiple requests race to verify the same token - Fix "resouce" → "resource" typo in authentication error messages Signed-off-by: Bryan Frimin --- pkg/iam/auth_service.go | 19 +++++-------------- .../api/authn/identity_presence_middleware.go | 2 +- .../gqlutils/directives/session/session.go | 2 +- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/pkg/iam/auth_service.go b/pkg/iam/auth_service.go index 6bac9601b..1c0cf5e35 100644 --- a/pkg/iam/auth_service.go +++ b/pkg/iam/auth_service.go @@ -628,21 +628,12 @@ func (s AuthService) OpenSessionWithMagicLink(ctx context.Context, tokenString s hashedValue := HashToken(tokenString) token := &coredata.Token{} - if err := s.pg.WithConn( - ctx, - func(conn pg.Conn) error { - if err := token.LoadByHashedValueForUpdate(ctx, conn, hashedValue); err != nil { - if errors.Is(err, coredata.ErrResourceNotFound) { - return NewInvalidTokenError() - } + if err := token.LoadByHashedValueForUpdate(ctx, tx, hashedValue); err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + return NewInvalidTokenError() + } - return fmt.Errorf("cannot load token by hashed value: %w", err) - } - - return nil - }, - ); err != nil { - return fmt.Errorf("cannot load token: %w", err) + return fmt.Errorf("cannot load token by hashed value: %w", err) } err := identity.LoadByEmail(ctx, tx, payload.Data.Email) diff --git a/pkg/server/api/authn/identity_presence_middleware.go b/pkg/server/api/authn/identity_presence_middleware.go index 7a6d79eda..ffd52a28d 100644 --- a/pkg/server/api/authn/identity_presence_middleware.go +++ b/pkg/server/api/authn/identity_presence_middleware.go @@ -37,7 +37,7 @@ func NewIdentityPresenceMiddleware() func(next http.Handler) http.Handler { Errors: gqlerror.List{ gqlutils.Unauthenticatedf( r.Context(), - "authentication is required to access this resouce", + "authentication is required to access this resource", ), }, }, diff --git a/pkg/server/gqlutils/directives/session/session.go b/pkg/server/gqlutils/directives/session/session.go index 2e5670ef6..70ec777f2 100644 --- a/pkg/server/gqlutils/directives/session/session.go +++ b/pkg/server/gqlutils/directives/session/session.go @@ -92,7 +92,7 @@ func Directive(ctx context.Context, obj any, next graphql.Resolver, required Ses if identity == nil { return nil, gqlutils.Unauthenticatedf( ctx, - "authentication is required to access this resouce", + "authentication is required to access this resource", ) } case SessionRequirementNone: