From 567b4647c084f1ca30b482c53e051dfad1e265dc Mon Sep 17 00:00:00 2001 From: defiQUG Date: Fri, 22 May 2026 22:01:11 -0700 Subject: [PATCH] Fix wallet connect signature mismatch on mobile and desktop. Align backend EIP-191 auth message with the DBIS Explorer text the frontend and legacy SPA already sign, instead of the stale SolaceScan string. Co-authored-by: Cursor --- backend/auth/wallet_auth.go | 8 ++++++- backend/auth/wallet_auth_test.go | 33 +++++++++++++++++++++++++++++ frontend/src/services/api/access.ts | 1 + 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/backend/auth/wallet_auth.go b/backend/auth/wallet_auth.go index 15048c1..ed7b059 100644 --- a/backend/auth/wallet_auth.go +++ b/backend/auth/wallet_auth.go @@ -110,6 +110,12 @@ type WalletAuthResponse struct { InstitutionName string `json:"institution_name,omitempty"` } +// walletAuthSignMessage returns the EIP-191 plaintext users sign during wallet login. +// Must stay in sync with frontend buildWalletMessage() in access.ts and explorer-spa.js. +func walletAuthSignMessage(nonce string) string { + return fmt.Sprintf("Sign this message to authenticate with DBIS Explorer.\n\nNonce: %s", nonce) +} + // GenerateNonce generates a random nonce for wallet authentication func (w *WalletAuth) GenerateNonce(ctx context.Context, address string) (*NonceResponse, error) { // Validate address format @@ -184,7 +190,7 @@ func (w *WalletAuth) AuthenticateWallet(ctx context.Context, req *WalletAuthRequ } // Verify signature - message := fmt.Sprintf("Sign this message to authenticate with SolaceScan.\n\nNonce: %s", req.Nonce) + message := walletAuthSignMessage(req.Nonce) messageHash := accounts.TextHash([]byte(message)) sigBytes, err := decodeWalletSignature(req.Signature) diff --git a/backend/auth/wallet_auth_test.go b/backend/auth/wallet_auth_test.go index 56e09ab..062c2b9 100644 --- a/backend/auth/wallet_auth_test.go +++ b/backend/auth/wallet_auth_test.go @@ -5,9 +5,42 @@ import ( "testing" "time" + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/require" ) +func TestWalletAuthSignMessageMatchesFrontend(t *testing.T) { + nonce := "abc123def456" + require.Equal( + t, + "Sign this message to authenticate with DBIS Explorer.\n\nNonce: abc123def456", + walletAuthSignMessage(nonce), + ) +} + +func TestAuthenticateWalletRecoversSignerFromFrontendMessage(t *testing.T) { + privateKey, err := crypto.GenerateKey() + require.NoError(t, err) + address := crypto.PubkeyToAddress(privateKey.PublicKey).Hex() + nonce := "test-nonce-001" + + message := walletAuthSignMessage(nonce) + messageHash := accounts.TextHash([]byte(message)) + signature, err := crypto.Sign(messageHash, privateKey) + require.NoError(t, err) + signature[64] += 27 + + sigBytes := make([]byte, len(signature)) + copy(sigBytes, signature) + if sigBytes[64] >= 27 { + sigBytes[64] -= 27 + } + pubKey, err := crypto.SigToPub(messageHash, sigBytes) + require.NoError(t, err) + require.Equal(t, address, crypto.PubkeyToAddress(*pubKey).Hex()) +} + func TestDecodeWalletSignatureRejectsMalformedValues(t *testing.T) { _, err := decodeWalletSignature("deadbeef") require.ErrorContains(t, err, "signature must start with 0x") diff --git a/frontend/src/services/api/access.ts b/frontend/src/services/api/access.ts index a65ef91..23717b7 100644 --- a/frontend/src/services/api/access.ts +++ b/frontend/src/services/api/access.ts @@ -166,6 +166,7 @@ function setStoredWalletSession(session: WalletAccessSession | null) { window.dispatchEvent(new Event(ACCESS_SESSION_EVENT)) } +// Keep in sync with walletAuthSignMessage() in backend/auth/wallet_auth.go. function buildWalletMessage(nonce: string) { return `Sign this message to authenticate with DBIS Explorer.\n\nNonce: ${nonce}` }