Wrong user record fetched after moving away from react-moralis

Hi there. So I have been working on a DApp for the past 1.5 years. At the time of the start of the project it was recommended to use react-moralis with your own self hosted parse-server setup. I have been running that on my DApp for a bit now but want to move to using only API calls using Moralis V2.

I followed this guide to add the parse-server integration into my already running express api https://docs.moralis.io/authentication-api/evm/integrations/parse-server-nodejs

And was able to get it all running okay. I was able to connect it to my front end by just using axios request to /request-message and /sign-message. The issue that I am facing though is that when I try to authenticate with a wallet that already had an account when react-moralis was in use, it creates a new user record instead of returning the already existing one.

After some debugging I noticed that the user record is layed out slightly differently using this new method of authentication. Right now I am thinking there is two options to go

  1. Migrate all users records to the new schema (this would like to be avoided)
  2. Find out where the user record schema is setup and change it to match my old schema

This is one of my old user records using Moralis v2 self hosted parse server + react-moralis

{
  "_id": "xxxxxxxxxx",
  "username": "xxxxxxxxxxxxxx",
  "_wperm": [
    "xxxxxxxxxx"
  ],
  "_rperm": [
    "xxxxxxxxxx"
  ],
  "_auth_data_moralisEth": {
    "id": "0x04f94d9f63e62e8b682fd84f554765f58b7fa0ac",
    "signature": "0xxxxxxxxxxxxxxxxxxxxxxxxx",
    "data": "iraz.app wants you to sign in with your Ethereum account:\n0x04f94D9F63E62E8B682Fd84F554765F58B7fa0ac\n\nAuthentication Signature for iRaz\n\nURI: https://iraz.app\nVersion: 1\nChain ID: 1\nNonce: xxxxxxxxxxx\nIssued At: 2023-07-01T16:47:09.596Z\nExpiration Time: 2023-07-08T16:47:09.060Z",
    "chainId": 1,
    "nonce": "0xxxxxxxxxxxxxxxxxxx",
    "address": "0x04f94D9F63E62E8B682Fd84F554765F58B7fa0ac",
    "version": "1",
    "domain": "iraz.app",
    "expirationTime": "2023-07-08T16:47:09.060Z",
    "notBefore": null,
    "resources": null,
    "statement": "Authentication Signature for iRaz",
    "uri": "https://iraz.app",
    "moralisProfileId": "0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  },
  "_acl": {
    "xxxxxxxxxx": {
      "w": true,
      "r": true
    }
  },
  "_created_at": "2023-07-01T16:47:14.701Z",
  "_updated_at": "2023-07-01T16:47:16.007Z",
  "accounts": [
    "0x04f94d9f63e62e8b682fd84f554765f58b7fa0ac"
  ],
  "ethAddress": "0x04f94d9f63e62e8b682fd84f554765f58b7fa0ac",
}

And this is how the schema of the new user layout looks after integrating the parse-server directly into my express api. This way uses the api request to auth the user instead of react-moralis

{
  "_id": "AZUbfJ9RwL",
  "username": "FCw3Ntb8hZWUZgQL0cXS5i32p",
  "_wperm": [
    "AZUbfJ9RwL"
  ],
  "_rperm": [
    "AZUbfJ9RwL"
  ],
  "_auth_data_moralis": {
    "id": "THIS MATCHES THE moralisProfileId NOW - NO LONGER ETH ADDRESS",
    "authId": "xxxxxxxxx",
    "message": "iraz.app wants you to sign in with your Ethereum account:\n0x04f94D9F63E62E8B682Fd84F554765F58B7fa0ac\n\nAuthentication Signature for iRaz\n\nURI: https://iraz.app\nVersion: 1\nChain ID: 11155111\nNonce: xxxxxxxxxxxxx\nIssued At: 2023-07-05T22:36:41.116Z\nExpiration Time: 2023-07-12T22:36:41.680Z",
    "signature": "0xxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "nonce": "xxxxxxxxxxxxx",
    "address": "0x04f94D9F63E62E8B682Fd84F554765F58B7fa0ac",
    "version": "1",
    "domain": "iraz.app",
    "expirationTime": "2023-07-12T22:36:41.680Z",
    "notBefore": null,
    "resources": null,
    "statement": "Authentication Signature for iRaz",
    "uri": "https://iraz.app"
  },
  "_acl": {
    "xxxxxxxxx": {
      "w": true,
      "r": true
    }
  },
  "_created_at": "2023-07-05T22:36:51.120Z",
  "_updated_at": "2023-07-05T22:36:52.221Z",
  "moralisProfileId": "0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}

Does anyone know what is happening here? Any suggestions would be appreciated!

This is the code for my MoralisAuthEthAdapter on the new version

function validateAuthData(authData: any) {
  const { message, signature, network, id, authId } = authData;
  console.log("auth data", authData)
  return Moralis.Auth.verify({
    message,
    signature,
    network,
  })
    .then((result:any) => {
      const data = result.toJSON();
      console.log("auth verify result: ",data)

      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(() => {
      // @ts-ignore (see note at top of file)
      throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Moralis auth failed, invalid data');
    });
}

And the code for the verify-signature endpoint

export async function verifyMessage({ network, signature, message }: VerifyMessage) {
  const storedData = authRequests.get(message);

  if (!storedData) {
    throw new Error('Invalid message');
  }

  const { id: storedId, profileId: storedProfileId } = storedData;
  console.log("stored data: ", storedData)

  const authData = {
    id: storedProfileId,
    authId: storedId,
    message,
    signature,
    network,
  };

  // Authenticate
  const user = await serverRequest.post<ParseUser>({
    endpoint: `/users`,
    params: {
      authData: {
        moralis: authData,
      },
    },
    useMasterKey: true,
  });

  console.log("user", user)

  // Update user moralisProfile column
  await serverRequest.put({
    endpoint: `/users/${user.objectId}`,
    params: {
      moralisProfileId: storedProfileId,
    },
    useMasterKey: true,
  });

  // Get authenticated user
  const updatedUser = await serverRequest.get({
    endpoint: `/users/${user.objectId}`,
    useMasterKey: true,
  });

  console.log("updated user: ", updatedUser)

  return updatedUser;
}

Hi @Calmix

If you are referring to the _auth_data_moralis object difference it is because in self host server we are using the Auth API to generate message and verify signature. The data under _auth_data_moralis is the response from the auth API.

Are you sure that this is what causing the duplicate users records?

I think the root of the issue is that for the _moralis_auth_data the id in that schema on the new setup is now the moralisProfileId. on the old setup the _moralis_auth_data.id was the users ethAddress.

I’m not 100% sure if that is the issue but it’s the only other thing I can think of.

Also just to note - I was using a self hosted server on the old setup too. Both were using a self hosted parse server but the new one is getting rid of the use of react-moralis package on the front end. So all auth will only be from direct API calls now instead of using a front end library.

So I am still a little unsure why the records look different. Even the accounts and ethAddress keys are missing from the new setup.

It seems like it might be due to the mismatch in the signature, as it is different between server and API. In that case, you will need to delete the duplicate users manually.

You can also try to automate the duplicate deletion using the beforeLogin cloud trigger, so there cannot be any duplicate for all users.

Thanks for the insight John and pushing me in the right direction. I ended up figuring it out, I was looking for the signature/moralis profile id wrong. Got it figured out and now have Coinbase Wallet and WalletConnect integrated :slight_smile:

1 Like