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
- Migrate all users records to the new schema (this would like to be avoided)
- 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;
}