Moralis Auth.Verify not working with Ledger

Hello,
In my dApp, the auth system is built with Next.js, Moralis & Supabase. All regular MetaMask users who use the dApp have no issues authenticating. However recently, some users who have their Ledger wallet connected to their Metamask are attempting to log in to the dApp and they are not able to do that because of the following error.

Whenever the code

await Moralis.Auth.verify({
    network,
    signature,
    message,
});

is run, the following error takes place:

ERROR	TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'ClientRequest'
    |     property 'socket' -> object with constructor 'TLSSocket'
    --- property '_httpMessage' closes the circle
    at JSON.stringify (<anonymous>)
    at sendData (/var/task/node_modules/next/dist/server/api-utils/node.js:288:47)
    at ServerResponse.apiRes.send (/var/task/node_modules/next/dist/server/api-utils/node.js:162:31)
    at handler (/var/task/.next/server/pages/api/auth/verifyMessage.js:73:25)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async Object.apiResolver (/var/task/node_modules/next/dist/server/api-utils/node.js:179:9)
    at async NextNodeServer.runApi (/var/task/node_modules/next/dist/server/next-server.js:381:9)
    at async Object.fn (/var/task/node_modules/next/dist/server/base-server.js:500:37)
    at async Router.execute (/var/task/node_modules/next/dist/server/router.js:213:36)
    at async NextNodeServer.run (/var/task/node_modules/next/dist/server/base-server.js:619:29)

To reiterate again, this issue ONLY takes place with Ledger wallets that are imported into MetaMask.

I’d appreciate some insight into this.

Thank you so much.

That error usually happens when an object is sent as parameter instead of a value.

You can add some logging to compare the parameters for when it works with MetaMask vs when it doesn’t work.

I did. I even have a record of a sample login attempt and it doesn’t contain any objects.

Here is a sample of the input parameters that failed.
Please note I have removed the user’s wallet address just in case it will open any vulnerabilities.

network: evm
signature: 0x919ed73d8fdc3a8329c1a3a17b1336e9e74aa6c704649ae09318f817072bbf645a02474c78d9e2f73071a1fb5ba44618699d111743266d5eca05a57c1c4f9a0f00
message: dreamworldnfts.com wants you to sign in with your Ethereum account:
<WALLET_ADDRESS_HERE>

Please sign this message to confirm your identity.

URI: https://dreamworldnfts.com
Version: 1
Chain ID: 1
Nonce: 5PvQyXIBsLECfKvyh
Issued At: 2022-12-12T22:18:13.387Z
Expiration Time: 2023-01-01T00:00:00.000Z

Did you try to compare it with a the parameters for when it works?

Does the signature have the same length?

Yes everything seems to be the same whether it’s from MetaMask directly or from an imported physical wallet such as Ledger.

Can you write me on discord with a complete example for the parameters?

Did you set the timeout long enough? The default may be 15 seconds

I don’t believe the timeout would be the issue here since Moralis.Auth.Verify throws a JSON error. Sure I can send it to you privately on Discord if you give me your Discord username.

I have the same username in discord as in forum

it looks like it is a difference on how the signature is generated with Ledger vs metamask

Authenticating seems fine on my end with Ledger > MetaMask extension. Tested with two demos - Next.js MetaMask and Parse Server React client. Do you have a public app URL to test?

Can you give more info about your project e.g. your package.json and full code involving Moralis’s Auth API.

The website is live at https://dreamworldnfts.com/staking.

The site is built in Next.js, TypeScript, Wagmi & Supabase.
This function generates the message on the backend:

export async function requestMessage(
    {
        address,
        chain,
        network
    }: MessageType
) {
    const result = await Moralis.Auth.requestMessage({
        address,
        chain,
        network,
        domain: 'dreamworldnfts.com',
        statement: 'Please sign this message to confirm your identity.',
        uri: 'https://dreamworldnfts.com',
        expirationTime: '2023-01-01T00:00:00.000Z',
        timeout: 15,
    });
    const { message } = result.toJSON();

    return message;
}

It is then signed on the front end with wagmi and then another request is made to verify the signature:

const { data: message } = await api.requestMessageRequest(account, chain.id);
const signature = await signMessageAsync({ message });
const { data: session } = await api.verifyMessageRequest(message, signature);

And this is the function that verifies the message:

export async function verifyMessage({ network, signature, message }: VerifyEvmOptions) {
    const result = await Moralis.Auth.verify({
        network,
        signature,
        message,
    });
    // ...
}

Here are the relevant packages used:

{
  ...
  "dependencies": {
    ...
    "ethers": "^5.6.9",
    "moralis": "^2.6.4",
    "next": "12.2.3",
    "react": "18.2.0",
    "wagmi": "^0.7.7",
    ...
  },
  "devDependencies": {
    "@moralisweb3/auth": "^2.6.4",
    "typescript": "4.7.4"
    ...
  }
}

I was able to authenticate initially with that site but then got errors if I tried it again (cleared local storage and cookies and authenticated again). Reauthenticating repeatedly is not an issue on my end with those latest demos.

https://dreamworldnfts.com/api/auth/verifyMessage 500

But then it worked again with the same Ledger account so the issue doesn’t seem consistent. If you are able to, ask your Ledger users to do similar tests and if they were able to initially authenticate at least once.

Those versions of moralis, wagmi and ethers didn’t affect this in the Next.js MetaMask demo.

1 Like

That’s quite strange. I looked up your login attempts and yes as you said you were able to authenticate and also fail authentication with the same wallet address. Could you confirm that the wallet address you used ends in '31d6Dc?
If that is your address then I think @cryptokid was right about the timeout parameter from Moralis.Auth.requestMessage. The only thing that would vary from each login attempt would be how long it took to get a signature back, or at least that’s the only thing I can think of right now…
I’ll play around with that and will report if that’s what’s causing the issue.

Yes that’s correct. It could be the timeout - it’s probably 2 seconds longer to accept the request on the device but maybe with server delays it’s not quite enough - you can try increasing that.

But check if the Ledger users were actually able to authenticate at any point.

Hello, I had some Ledger users try it out with the timeout being set to 60 instead of 15, and still, none of them could log in. Seems to me that it might be a bug in the Moralis SDK.

You can try using a newer version of moralis in your production build.

did you find a solution to this? I’m having the same issue

I tried updating Moralis and now even I cannot connect my MetaMask. I am getting the circular JSON error from the Moralis.Auth.Verify function consistently.

Here is a demo link: https://dreamworld-site-4vd9snpi5-ctrla-byte.vercel.app/staking.

Here is the dump for the backend logs when it fails:

2023-01-02T22:44:50.870Z	6ff3836c-32e0-4b05-a4d8-d14a3b8cd50a	ERROR	TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'ClientRequest'
    |     property 'socket' -> object with constructor 'TLSSocket'
    --- property '_httpMessage' closes the circle
    at JSON.stringify (<anonymous>)
    at sendData (/var/task/node_modules/next/dist/server/api-utils/node.js:288:47)
    at ServerResponse.apiRes.send (/var/task/node_modules/next/dist/server/api-utils/node.js:162:31)
    at handler (/var/task/.next/server/pages/api/auth/verifyMessage.js:71:25)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async Object.apiResolver (/var/task/node_modules/next/dist/server/api-utils/node.js:179:9)
    at async NextNodeServer.runApi (/var/task/node_modules/next/dist/server/next-server.js:381:9)
    at async Object.fn (/var/task/node_modules/next/dist/server/base-server.js:500:37)
    at async Router.execute (/var/task/node_modules/next/dist/server/router.js:213:36)
    at async NextNodeServer.run (/var/task/node_modules/next/dist/server/base-server.js:619:29)

2023-01-02T22:44:50.870Z	6ff3836c-32e0-4b05-a4d8-d14a3b8cd50a	ERROR	TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'ClientRequest'
    |     property 'socket' -> object with constructor 'TLSSocket'
    --- property '_httpMessage' closes the circle
    at JSON.stringify (<anonymous>)
    at sendData (/var/task/node_modules/next/dist/server/api-utils/node.js:288:47)
    at ServerResponse.apiRes.send (/var/task/node_modules/next/dist/server/api-utils/node.js:162:31)
    at handler (/var/task/.next/server/pages/api/auth/verifyMessage.js:71:25)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async Object.apiResolver (/var/task/node_modules/next/dist/server/api-utils/node.js:179:9)
    at async NextNodeServer.runApi (/var/task/node_modules/next/dist/server/next-server.js:381:9)
    at async Object.fn (/var/task/node_modules/next/dist/server/base-server.js:500:37)
    at async Router.execute (/var/task/node_modules/next/dist/server/router.js:213:36)
    at async NextNodeServer.run (/var/task/node_modules/next/dist/server/base-server.js:619:29)
RequestId: 6ff3836c-32e0-4b05-a4d8-d14a3b8cd50a Error: Runtime exited with error: exit status 1

Is the Auth API down? I just realized even my previous deployments (that worked fine) have suddenly stopped working… All giving the same Circular JSON error.

Hey @arvin,

can you give more detail on what are the parameters that you used in Moralis.Auth.Verify?