Integrating Authentication API with AWS Lambda

I wonder if we can use Authentication API along with AWS Lamda / Serveless Framework setting. Since I am not proficient to server-side, I want something easier way to use Moralis APIs.

it may be possible, for authentication you have to call the api to get a message to sign, then you have to send that message to front end for the user to sign it and after you get the signed message from the user you can validate the signature by making another api call

Ok, I try it anyway.
But I wonder if Moralis stores user data and authentication information in your database. When a user signs up or logs in with Moralis, their information is stored in Moralis’ cloud database? Otherwise should I connect to Mongo db with Lambda?

I got this question because I noticed that setting own parse server needs Mongo db connected.

It depends on your application needs. You can make it work with your own database or without a database and only check the user authentication once. You may need a database somewhere to store a session token

1 Like

It seems AWS Lambda received a call with the code below.
But the issue I face now is that I am getting an alert that says Status code 500 {"error":"Cannot read property 'Web3Provider' of undefined"} from my front-end.
Could you lead me to right direction? Do you have any documentations that I can refer to make this server setting work?

authenticateUser.js

“use strict”;
const ethers = require(‘ethers’);

exports.handler = async (event) => {
console.log(“Event received:”, event);
const body = JSON.parse(event.body);
const { address, signedMessage } = body;

if (!address || !signedMessage) {
return {
statusCode: 400,
body: JSON.stringify({ error: “Missing address or signed message.” }),
};
}

try {
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
const recoveredAddress = await signer.getAddress(signedMessage);

if (address.toLowerCase() === recoveredAddress.toLowerCase()) {
  // You can create a session or JWT token here and return it in the response
  return {
    statusCode: 200,
    body: JSON.stringify({ success: true }),
  };
} else {
  return {
    statusCode: 401,
    body: JSON.stringify({ error: "Invalid signature." }),
  };
}

}
catch (error) {
return {
statusCode: 500,
body: JSON.stringify({ error: error.message }),
};
}
};

Log in AWS Lambda

2023-03-27T12:30:34.664Z e66db2a1-7873-429a-9bba-1a499a2be302 INFO Event received: {
resource: ‘/authenticateUser’,
path: ‘/authenticateUser’,
httpMethod: ‘POST’,
headers: {
‘Accept-Encoding’: ‘gzip, deflate’,
‘CloudFront-Forwarded-Proto’: ‘https’,
‘CloudFront-Is-Desktop-Viewer’: ‘true’,
‘CloudFront-Is-Mobile-Viewer’: ‘false’,
‘CloudFront-Is-SmartTV-Viewer’: ‘false’,
‘CloudFront-Is-Tablet-Viewer’: ‘false’,
‘CloudFront-Viewer-ASN’: ‘16509’,
‘CloudFront-Viewer-Country’: ‘US’,
‘content-type’: ‘application/json’,
Host: ‘5xkgxbww4k.execute-api.us-east-1.amazonaws.com’,
traceparent: ‘00-a786a5c39fb7c194b79e391f936b3376-d2563e38edbb33fb-01’,
‘User-Agent’: ‘Amazon CloudFront’,
Via: ‘1.1 3c7c59dd8a259f28206268185f3ecaa2.cloudfront.net (CloudFront)’,
‘X-Amz-Cf-Id’: ‘0_Lkdhyya9DuoTEz1jl_BLvZdjb0Drcf2CgS4-M-1iY1_jxlsD0_eg==’,
‘X-Amzn-Trace-Id’: ‘Root=1-64218c69-5fb81b184735351950117fda’,
‘x-bubble-depth’: ‘1’,
‘X-Forwarded-For’: ‘34.220.209.63, 130.176.100.142’,
‘X-Forwarded-Port’: ‘443’,
‘X-Forwarded-Proto’: ‘https’
},
multiValueHeaders: {
‘Accept-Encoding’: [ ‘gzip, deflate’ ],
‘CloudFront-Forwarded-Proto’: [ ‘https’ ],
‘CloudFront-Is-Desktop-Viewer’: [ ‘true’ ],
‘CloudFront-Is-Mobile-Viewer’: [ ‘false’ ],
‘CloudFront-Is-SmartTV-Viewer’: [ ‘false’ ],
‘CloudFront-Is-Tablet-Viewer’: [ ‘false’ ],
‘CloudFront-Viewer-ASN’: [ ‘16509’ ],
‘CloudFront-Viewer-Country’: [ ‘US’ ],
‘content-type’: [ ‘application/json’ ],
Host: [ ‘5xkgxbww4k.execute-api.us-east-1.amazonaws.com’ ],
traceparent: [ ‘00-a786a5c39fb7c194b79e391f936b3376-d2563e38edbb33fb-01’ ],
‘User-Agent’: [ ‘Amazon CloudFront’ ],
Via: [
‘1.1 3c7c59dd8a259f28206268185f3ecaa2.cloudfront.net (CloudFront)’
],
‘X-Amz-Cf-Id’: [ ‘0_Lkdhyya9DuoTEz1jl_BLvZdjb0Drcf2CgS4-M-1iY1_jxlsD0_eg==’ ],
‘X-Amzn-Trace-Id’: [ ‘Root=1-64218c69-5fb81b184735351950117fda’ ],
‘x-bubble-depth’: [ ‘1’ ],
‘X-Forwarded-For’: [ ‘34.220.209.63, 130.176.100.142’ ],
‘X-Forwarded-Port’: [ ‘443’ ],
‘X-Forwarded-Proto’: [ ‘https’ ]
},
queryStringParameters: null,
multiValueQueryStringParameters: null,
pathParameters: null,
stageVariables: null,
requestContext: {
resourceId: ‘8p2jrh’,
resourcePath: ‘/authenticateUser’,
httpMethod: ‘POST’,
extendedRequestId: ‘CcLggFPeoAMFXAw=’,
requestTime: ‘27/Mar/2023:12:30:33 +0000’,
path: ‘/dev/authenticateUser’,
accountId: ‘461270241695’,
protocol: ‘HTTP/1.1’,
stage: ‘dev’,
domainPrefix: ‘5xkgxbww4k’,
requestTimeEpoch: 1679920233340,
requestId: ‘0afa95ad-9081-4536-a5ff-f4bb51e84ddf’,
identity: {
cognitoIdentityPoolId: null,
accountId: null,
cognitoIdentityId: null,
caller: null,
sourceIp: ‘34.220.209.63’,
principalOrgId: null,
accessKey: null,
cognitoAuthenticationType: null,
cognitoAuthenticationProvider: null,
userArn: null,
userAgent: ‘Amazon CloudFront’,
user: null
},
domainName: ‘5xkgxbww4k.execute-api.us-east-1.amazonaws.com’,
apiId: ‘5xkgxbww4k’
},
body: ‘{“address”:“0x8cDE5Aec2Ea9189C7c90509e377D4925F19D91E2”,“signedMessage”:“test”}’,
isBase64Encoded: false
}

it looks like ethers.providers is undefined

Hey, thanks.
This is what I have in package.json, what else am I missing?

{
  "name": "aws-node-express-api",
  "version": "1.0.0",
  "description": "",
  "dependencies": {
    "body-parser": "^1.20.2",
    "ethers": "^6.2.2",
    "express": "^4.18.2",
    "moralis": "^2.17.0",
    "serverless-http": "^3.1.1"
  },
  "devDependencies": {
    "serverless-offline": "^12.0.4"
  }
}

maybe the syntax is different for that version of ethers,

https://stackoverflow.com/questions/60785630/how-to-connect-ethers-js-with-metamask

=>

Using ethers.js to interact with Metamask

v6.0.0 (2023-02-02) and above

const provider = new ethers.BrowserProvider(window.ethereum);
// Prompt user for account connections
await provider.send("eth_requestAccounts", []);
const signer = provider.getSigner();
console.log("Account:", await signer.getAddress());
v5.7.2 and below

const provider = new ethers.providers.Web3Provider(window.ethereum, "any");
// Prompt user for account connections
await provider.send("eth_requestAccounts", []);
const signer = provider.getSigner();
console.log("Account:", await signer.getAddress());
1 Like

I am working on this quite a while, but still drifting around…

I get Status code 500 {"error":"Request failed with status code 400"} from the frone-end. I see this logs in Lambda’s Cloud watch: Error encountered: AxiosError: Request failed with status code 400

Can you identify my problem? Sorry I’m still new in server-side configs. Do I have to have Cloud function? Do I need some codes other than this?

"use strict";
const Moralis = require("moralis").default;
const axios = require("axios");

const startMoralis = async () => {
  await Moralis.start({
    apiKey: process.env.MORALIS_API_KEY,
  });
};

exports.handler = async (event) => {
  console.log("Event received:", event);
  const body = JSON.parse(event.body);
  const { address, message, signature } = body;

  if (!address || !message || !signature) {
    return {
      statusCode: 400,
      body: JSON.stringify({ error: "Missing address, message or signature." }),
    };
  }

  try {
    // Initialize Moralis
    console.log("Initializing Moralis...");
    await startMoralis();
    console.log("Moralis initialized.");

    // Call the Moralis Authentication API
    console.log("Calling Moralis Authentication API...");
    const response = await axios.post("https://xxxx.grandmoralis.com:2053/server/functions/authenticateUser", {
      address,
      message,
      signature,
    });
    console.log("Moralis Authentication API response:", response.data);

    if (response.data.success) {
      return {
        statusCode: 200,
        body: JSON.stringify({ success: true }),
      };
    } else {
      return {
        statusCode: 401,
        body: JSON.stringify({ error: "Invalid signature." }),
      };
    }
  } catch (error) {
    console.error("Error encountered:", error);
    return {
      statusCode: 500,
      body: JSON.stringify({ error: error.message }),
    };
  }
};

what is the line that returns an error?

I think this part caused the error:

axios.post("https://xxxx.grandmoralis.com:2053/server/functions/authenticateUser", {
  address,
  message,
  signature,
});
1 Like

you are using a moralis managed server for authentication?

@rio. I have a simpler solution if your using next. if you want a serverless solution you could just write your auth method as a next APi call in your server side code a communicate with you DB from here. (this is if your using next js of course). doing a custom auth is really simple even the moralis source code for the old authenticate call is only a few lines. you just need to take in the usrs FE sig and use a library like ethereum-js to call a recovery method. Considering that the initial problem is simple, to not over complicate things i would suggest that if your using nextJs, just writeyou api call there. something like this would work

export async function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseData>
  next: NextFunction
) {
    const { signature, nonce, publicAddress, } = req.body;
    const userExists = await doesUserExist(publicAddress)

    if (!nonce || !signature) {
        return next(new ErrorResponse(
            "User already exists. please use a unique address", 400, 3
        ));
    }
    try {
        const msg = getMessage(nonce)
        const recoveredAddress = ethers.utils.recoverAddress(
            hashMessage(msg), signature
        );

        if (recoveredAddress.toLowerCase() === publicAddress.toLowerCase()) {
            const nonce: number = Math.floor(Math.random() * 10000);
            if (!userExists) {
                const user: User | null = await User.create({
                    publicAddress, nonce
                })
                 sendToken(user!, 200, res);
            } else {
                const user: User | null = await User.findOne({ publicAddress })
                 sendToken(user!, 200, res);
            }
		} else {
			res.status(401).send({
				error: 'Signature verification failed',
			});
		}
    } catch(err: unknown) {
        next(err);
    }
}

this is of course if a solution like this satisfies your needs an reqirements. if not then just keep searching yoyull find a solution eventyualy. but look into to this if it can be adapted to your specifc needs and setup

Of course depending on what DB your working with you will need to adapt my sol above for appending/checking users etc

1 Like

I think so. I am trying to migrate to moralis v2 from moralis v1(hosted).
Therefore, I use the same App key. Using the same App key might cause issues?

ok, you have to do it in your self hosted server, you can also try to understand the above answer

Hi @error_undefined, Thank you so much for the reply!
I really appreciate your detailed code sample. Unfortunately I don’t use Next.js, but my tech stack is something like this:
Front: Bubble(no-code, but extensive Javascript capability)
Back-end: AWS Lambda w/ Serverless Framework

API Connector in Bubble and API Gateway in AWS communicates. With that, I could successfully implement this: https://docs.moralis.io/web3-data-api/evm/integrations/aws-lambda-nodejs

What I keep failing is a call to ethers.js functions.
I have asked chatGTP how I can convert your code sample so that I can apply that to my environment did not work out yet.

Do I need to have self hosted server? I want to do the same with Serverless configuration.
Are you saying that Serverless is impossible? Why you have this introduced at the beginning of the doc if it is not doable when it comes to Authentication API?

@cryptokid Could you make this instruction applicable to non-game developers? Again I want to use AWS Lambda as my server.

what are the functionalities that use your moralis server now?

do you really need authentication with a wallet that requires to sign a message and validate it in your application?

Yes, I do need the authentication. Currently my app is
running with Moralis version 1 with the Metamask login implemented.

1 Like