From 0f70bb02fde0d7dec4f8d26c9f0e5b89e2519366 Mon Sep 17 00:00:00 2001 From: Developer Date: Sat, 29 Nov 2025 03:07:50 -0800 Subject: [PATCH] fix: Complete E2E test fixes for account service authentication MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix CreateAccount to decode hex-encoded public key before storage - Fix Login signature verification to hash challenge before verifying - Return 401 instead of 400 for invalid hex format in login credentials - Fix CompleteRecovery to handle direct transition from requested state All 8 E2E tests now pass (100% pass rate): - TestAccountRecoveryFlow, TestCompleteAccountFlow, TestDuplicateUsername, TestInvalidLogin - TestCompleteKeygenFlow, TestExceedParticipantLimit, TestGetNonExistentSession, TestJoinSessionWithInvalidToken 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../account/adapters/input/http/account_handler.go | 14 +++++++++++--- .../account/application/use_cases/login.go | 5 +++-- .../account/domain/services/account_service.go | 7 +++++++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/backend/mpc-system/services/account/adapters/input/http/account_handler.go b/backend/mpc-system/services/account/adapters/input/http/account_handler.go index 59079949..d2bc9354 100644 --- a/backend/mpc-system/services/account/adapters/input/http/account_handler.go +++ b/backend/mpc-system/services/account/adapters/input/http/account_handler.go @@ -135,11 +135,18 @@ func (h *AccountHTTPHandler) CreateAccount(c *gin.Context) { } } + // Decode hex-encoded public key + publicKeyBytes, err := hex.DecodeString(req.PublicKey) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid public key format"}) + return + } + output, err := h.createAccountUC.Execute(c.Request.Context(), ports.CreateAccountInput{ Username: req.Username, Email: req.Email, Phone: req.Phone, - PublicKey: []byte(req.PublicKey), + PublicKey: publicKeyBytes, KeygenSessionID: keygenSessionID, ThresholdN: req.ThresholdN, ThresholdT: req.ThresholdT, @@ -323,15 +330,16 @@ func (h *AccountHTTPHandler) Login(c *gin.Context) { } // Decode hex-encoded challenge and signature + // Return 401 Unauthorized for invalid formats (treated as invalid credentials) challengeBytes, err := hex.DecodeString(req.Challenge) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "invalid challenge format"}) + c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid credentials"}) return } signatureBytes, err := hex.DecodeString(req.Signature) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "invalid signature format"}) + c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid credentials"}) return } diff --git a/backend/mpc-system/services/account/application/use_cases/login.go b/backend/mpc-system/services/account/application/use_cases/login.go index e54cec57..2e53a685 100644 --- a/backend/mpc-system/services/account/application/use_cases/login.go +++ b/backend/mpc-system/services/account/application/use_cases/login.go @@ -78,8 +78,9 @@ func (uc *LoginUseCase) Execute(ctx context.Context, input ports.LoginInput) (*p return nil, ErrSignatureInvalid } - // Verify signature - if !crypto.VerifySignature(pubKey, input.Challenge, input.Signature) { + // Verify signature (hash the challenge first, as SignMessage does) + challengeHash := crypto.HashMessage(input.Challenge) + if !crypto.VerifySignature(pubKey, challengeHash, input.Signature) { return nil, ErrSignatureInvalid } diff --git a/backend/mpc-system/services/account/domain/services/account_service.go b/backend/mpc-system/services/account/domain/services/account_service.go index 7d55c2a0..fe1ac051 100644 --- a/backend/mpc-system/services/account/domain/services/account_service.go +++ b/backend/mpc-system/services/account/domain/services/account_service.go @@ -181,6 +181,13 @@ func (s *AccountDomainService) CompleteRecovery(ctx context.Context, recoverySes return err } + // Start keygen if still in requested state (transitions to in_progress) + if recovery.Status == value_objects.RecoveryStatusRequested { + if err := recovery.StartKeygen(newKeygenSessionID); err != nil { + return err + } + } + // Complete recovery session if err := recovery.Complete(); err != nil { return err