[SOLVED] Parse-Server authentication adapter error: "Moralis auth failed, invalid data"

Hello, I’m trying to migrate code from v1 to v2 and ending up with a problem with authentication.

We use the parse-server demo to our backend. After starting the backend and accessing http://localhost:1337 URL, public.js make successfully two POST requests to http://localhost:1337/api/auth/request-message, http://localhost:1337/api/auth/sign-message.

When trying to make a simple request with curl, postman, or insomnia I get this error:

{"error":"Moralis auth failed, invalid data"}

And on the backend after catching the exception from Moralis.Auth.verify function call I receive:

Mon, 16 Jan 2023 18:45:13 GMT express:router errorHandler  : /api/auth/sign-message
ErrorHandler MoralisError [Moralis SDK Core Error]: [C0006] Request failed, Not Found(404): Unknown error (no error info returned from API)
    at RequestController.makeError (/usr/src/app/node_modules/@moralisweb3/common-core/src/controllers/RequestController/RequestController.ts:75:14)
    at RequestController.<anonymous> (/usr/src/app/node_modules/@moralisweb3/common-core/src/controllers/RequestController/RequestController.ts:56:26)
    at step (/usr/src/app/node_modules/@moralisweb3/common-core/lib/controllers/RequestController/RequestController.js:44:23)
    at Object.throw (/usr/src/app/node_modules/@moralisweb3/common-core/lib/controllers/RequestController/RequestController.js:25:53)
    at rejected (/usr/src/app/node_modules/@moralisweb3/common-core/lib/controllers/RequestController/RequestController.js:17:65)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)

The strange thing is when trying to authenticate with the static HTML file on public/ directory (script.js) all work without a problem :face_with_monocle:


The dependencies in package.json:

{
  "dependencies": {
    "@codemirror/language": "^0.20.0",
    "@moralisweb3/common-core": "^2.11.1",
    "@moralisweb3/parse-server": "^2.11.1",
    "cors": "^2.8.5",
    "dotenv": "^16.0.1",
    "envalid": "7.3.1",
    "express": "^4.18.1",
    "express-rate-limit": "^6.5.1",
    "graphql": "^16.6.0",
    "graphql-ws": "^5.10.1",
    "moralis": "^2.11.1",
    "parse-dashboard": "^4.1.4",
    "parse-server": "^5.4.0"
  }
}

The full error from express backend:

ErrorHandler MoralisError [Moralis SDK Core Error]: [C0006] Request failed, Not Found(404): Unknown error (no error info returned from API)
    at RequestController.makeError (/usr/src/app/node_modules/@moralisweb3/common-core/src/controllers/RequestController/RequestController.ts:75:14)
    at RequestController.<anonymous> (/usr/src/app/node_modules/@moralisweb3/common-core/src/controllers/RequestController/RequestController.ts:56:26)
    at step (/usr/src/app/node_modules/@moralisweb3/common-core/lib/controllers/RequestController/RequestController.js:44:23)
    at Object.throw (/usr/src/app/node_modules/@moralisweb3/common-core/lib/controllers/RequestController/RequestController.js:25:53)
    at rejected (/usr/src/app/node_modules/@moralisweb3/common-core/lib/controllers/RequestController/RequestController.js:17:65)
    at processTicksAndRejections (node:internal/process/task_queues:96:5) {
  isMoralisError: true,
  code: 'C0006',
  details: {
    status: 404,
    response: {
      status: 404,
      statusText: 'Not Found',
      headers: [AxiosHeaders],
      config: [Object],
      request: [ClientRequest],
      data: [Object]
    }
  },
  [cause]: AxiosError: Request failed with status code 404
      at settle (/usr/src/app/node_modules/axios/lib/core/settle.js:19:12)
      at IncomingMessage.handleStreamEnd (/usr/src/app/node_modules/axios/lib/adapters/http.js:512:11)
      at IncomingMessage.emit (node:events:525:35)
      at IncomingMessage.emit (node:domain:489:12)
      at endReadableNT (node:internal/streams/readable:1358:12)
      at processTicksAndRejections (node:internal/process/task_queues:83:21) {
    code: 'ERR_BAD_REQUEST',
    config: {
      transitional: [Object],
      adapter: [Array],
      transformRequest: [Array],
      transformResponse: [Array],
      timeout: 20000,
      xsrfCookieName: 'XSRF-TOKEN',
      xsrfHeaderName: 'X-XSRF-TOKEN',
      maxContentLength: Infinity,
      maxBodyLength: Infinity,
      env: [Object],
      validateStatus: [Function: validateStatus],
      headers: [AxiosHeaders],
      url: 'http://localhost:1337/server//users',
      method: 'post',
      data: '{"authData":{"moralis":{"id":"0x3393b5d1e9ecb00d866a3ef6e468c2915db175bfb5b54281109b4156eb8296bb","authId":"HPUTPW83vKpaMYMuk","message":"mydomain.com wants you to sign in with your Ethereum account:\\n0x<MY_WALLET_ADDRESS>\\n\\nPlease sign this message to confirm your identity.\\n\\nURI: https://mydomain.com\\nVersion: 1\\nChain ID: 1\\nNonce: cEYoTrTZEFGzgdCfE\\nIssued At: 2023-01-16T18:43:10.825Z\\nExpiration Time: 2024-01-01T00:00:00.000Z","signature":"0x8aa047453a79e4eb46323b2a933f551c6b43659072829279ffe78f366d9b12b75a9f42cceb19441de21ea266aaeec843b9501561322329472e7933d08e6a8cae1c","network":"evm"}}}'
    },
    request: ClientRequest {
      _events: [Object: null prototype],
      _eventsCount: 7,
      _maxListeners: undefined,
      outputData: [],
      outputSize: 0,
      writable: true,
      destroyed: false,
      _last: true,
      chunkedEncoding: false,
      shouldKeepAlive: false,
      maxRequestsOnConnectionReached: false,
      _defaultKeepAlive: true,
      useChunkedEncodingByDefault: true,
      sendDate: false,
      _removedConnection: false,
      _removedContLen: false,
      _removedTE: false,
      strictContentLength: false,
      _contentLength: '632',
      _hasBody: true,
      _trailer: '',
      finished: true,
      _headerSent: true,
      _closed: false,
      socket: [Socket],
      _header: 'POST /server//users HTTP/1.1\r\n' +
        'Accept: application/json, text/plain, */*\r\n' +
        'Content-Type: application/json\r\n' +
        'X-Parse-Master-Key: replace_me\r\n' +
        'X-Parse-Application-Id: 65ac0f481e82acec09d6fa74b66265bb2c727f95074e44bd1e10afbe3ec1dddc\r\n' +
        'User-Agent: axios/1.2.2\r\n' +
        'Content-Length: 632\r\n' +
        'Accept-Encoding: gzip, compress, deflate, br\r\n' +
        'Host: localhost:1337\r\n' +
        'Connection: close\r\n' +
        '\r\n',
      _keepAliveTimeout: 0,
      _onPendingData: [Function: nop],
      agent: [Agent],
      socketPath: undefined,
      method: 'POST',
      maxHeaderSize: undefined,
      insecureHTTPParser: undefined,
      path: '/server//users',
      _ended: true,
      res: [IncomingMessage],
      aborted: false,
      timeoutCb: null,
      upgradeOrConnect: false,
      parser: null,
      maxHeadersCount: null,
      reusedSocket: false,
      host: 'localhost',
      protocol: 'http:',
      _redirectable: [Writable],
      [Symbol(kCapture)]: false,
      [Symbol(kBytesWritten)]: 0,
      [Symbol(kEndCalled)]: true,
      [Symbol(kNeedDrain)]: false,
      [Symbol(corked)]: 0,
      [Symbol(kOutHeaders)]: [Object: null prototype],
      [Symbol(kUniqueHeaders)]: null
    },
    response: {
      status: 404,
      statusText: 'Not Found',
      headers: [AxiosHeaders],
      config: [Object],
      request: [ClientRequest],
      data: [Object]
    }
  }
}

I noticed that http://localhost:1337/server//users URL have a extra / between host and endpoint, but already try to change code, but problem persist

What is the message that gets signed with MetaMask?

The raw message was:

mydomain.com wants you to sign in with your Ethereum account:
0x<MY_WALLET_ADDRESS>

Please sign this message to confirm your identity.

URI: https://mydomain.com
Version: 1
Chain ID: 1
Nonce: qvIAXQAKWgsVfdmSG
Issued At: 2023-01-16T19:06:04.722Z
Expiration Time: 2024-01-01T00:00:00.000Z

When using curl or postman I use the JSON escape sanitization:

mydomain.xyz wants you to sign in with your Ethereum account:\n0x<MY_WALLET_ADDRESS>\n\nPlease sign this message to confirm your identity.\n\nURI: https://mydomain.xyz\nVersion: 1\nChain ID: 1\nNonce: qvIAXQAKWgsVfdmSG\nIssued At: 2023-01-16T19:06:04.722Z\nExpiration Time: 2024-01-01T00:00:00.000Z

it looks like the expected message, it is not an issue with Expiration Time being in the past

Fix the Expiration Time error days ago, the error came from src/auth/MoralisAuthAdapter.ts, I need to deep troubleshoot/debug the validateAuthData function:

// Note: do not import Parse dependency. see https://github.com/parse-community/parse-server/issues/6467
/* global Parse */
import Moralis from 'moralis';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function validateAuthData(authData: any) {
  const { message, signature, network, id, authId } = authData;
  return Moralis.Auth.verify({
    message,
    signature,
    network,
  })
  .then((result) => {
    const data = result.toJSON();
    
    if (id === data.profileId && authId === data.id) {
        if (authData.chainId) {
          authData.chainId = result.result.chain.decimal;
        }
        authData.nonce = data.nonce;
        authData.address = result.result.address.checksum;
        authData.version = data.version;
        authData.domain = data.domain;
        authData.expirationTime = data.expirationTime;
        authData.notBefore = data.notBefore;
        authData.resources = data.resources;
        authData.statement = data.statement;
        authData.uri = data.uri;
        return;
      }

      // @ts-ignore (see note at top of file)
      throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Moralis auth failed, invalid data');
    })
    .catch(() => {
      // <------ ERROR HERE, because Moralis.Auth.verify call fail

      // @ts-ignore (see note at top of file)
      throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Moralis auth failed, invalid data');
    });
}

function validateAppId() {
  return Promise.resolve();
}

export default {
  validateAuthData,
  validateAppId,
};

same code was working before without issues and you started to get errors today?

can you add logging for this data?

but this is only when Moralis.Auth.verify success. it fails and an exception will be thrown, const data = result.toJSON(); line will never be executed.

{
  id: 'IAVntKr2puebcYECW',
  domain: 'mydomain.com',
  chainId: 1,
  address: '0x<MY_WALLET_ADDRESS>',
  statement: 'Please sign this message to confirm your identity.',
  uri: 'https://mydomain.com',
  expirationTime: '2024-01-01T00:00:00.000Z',
  version: '1',
  nonce: 'W8h1YQok2pdcXvNcl',
  profileId: '0x3393b5d1e9ecb00d866a3ef6e468c2915db175bfb5b54281109b4156eb8296bb'
}

sometimes it works and sometimes it doesn’t work?

did you try to increase the time interval for when the message can be signed?

it works using the static files present at public/ dir (see demo code).

With manual HTTP requests (using curl or postman) it fails, but maybe was that time between request-message and sign-message.

You are talking about this timeout? const TIMEOUT = 15; (on src/auth/authService.ts file), 15 seconds between request and sign?

yes, increase that to 60 to see if it works

1 Like

It works, thank you soo much @cryptokid :ok_hand: :rocket:

1 Like