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 <cursoragent@cursor.com>
This commit is contained in:
@@ -110,6 +110,12 @@ type WalletAuthResponse struct {
|
|||||||
InstitutionName string `json:"institution_name,omitempty"`
|
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
|
// GenerateNonce generates a random nonce for wallet authentication
|
||||||
func (w *WalletAuth) GenerateNonce(ctx context.Context, address string) (*NonceResponse, error) {
|
func (w *WalletAuth) GenerateNonce(ctx context.Context, address string) (*NonceResponse, error) {
|
||||||
// Validate address format
|
// Validate address format
|
||||||
@@ -184,7 +190,7 @@ func (w *WalletAuth) AuthenticateWallet(ctx context.Context, req *WalletAuthRequ
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify signature
|
// 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))
|
messageHash := accounts.TextHash([]byte(message))
|
||||||
|
|
||||||
sigBytes, err := decodeWalletSignature(req.Signature)
|
sigBytes, err := decodeWalletSignature(req.Signature)
|
||||||
|
|||||||
@@ -5,9 +5,42 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/stretchr/testify/require"
|
"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) {
|
func TestDecodeWalletSignatureRejectsMalformedValues(t *testing.T) {
|
||||||
_, err := decodeWalletSignature("deadbeef")
|
_, err := decodeWalletSignature("deadbeef")
|
||||||
require.ErrorContains(t, err, "signature must start with 0x")
|
require.ErrorContains(t, err, "signature must start with 0x")
|
||||||
|
|||||||
@@ -166,6 +166,7 @@ function setStoredWalletSession(session: WalletAccessSession | null) {
|
|||||||
window.dispatchEvent(new Event(ACCESS_SESSION_EVENT))
|
window.dispatchEvent(new Event(ACCESS_SESSION_EVENT))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep in sync with walletAuthSignMessage() in backend/auth/wallet_auth.go.
|
||||||
function buildWalletMessage(nonce: string) {
|
function buildWalletMessage(nonce: string) {
|
||||||
return `Sign this message to authenticate with DBIS Explorer.\n\nNonce: ${nonce}`
|
return `Sign this message to authenticate with DBIS Explorer.\n\nNonce: ${nonce}`
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user