In Identity identity-validate function, https requests don't work

  • site name: nifty-mcclintock-5552c0

Hello, I am trying to write a netlify lambda function to manage the signup process of my users.

I have created an identity-validate function that gets properly triggered when a user signs up.
My goal in this function is to simply post an incoming webhook to our Mattermost server (slack clone) to receive a message in a channel whenever a user signs up.
So I use the standard node module https to POST a request to our site (we use HTTPS as protocol:


const handler = async (event) => {
  try {
    body = event.body
    console.log(body)
    payload = JSON.parse(body)

    user_email = payload["user"]["email"]

    provider = payload["user"]["app_metadata"]["provider"]
    full_name = payload["user"]["user_metadata"]["full_name"]
    text = "email = " + user_email + " provider=" + provider + " full_name=" + full_name
    console.log(text)

    const https = require('https')
    const data = JSON.stringify({
      text: text
    })

    const options = {
      hostname: 'REDACTED',
      port: 443,
      path: '/hooks/REDACTED',
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Content-Length': data.length
      }
    }
    const req = https.request(options, res => {
      console.log('statusCode: ' + res["statusCode"])

      res.on('data', d => {
        console.log("out=" + d)
      })
    })

    req.on('error', error => {
      console.error("ERROR")
      console.error(error)
    })


    req.write(data)
    req.end()

    console.log("fin3")

    return {
      statusCode: 200,
      body: JSON.stringify({ message: "success" }),
    }
  } catch (error) {
    console.log(error)
    return { statusCode: 500, body: error.toString() }
  }
}

module.exports = { handler }

When I serve the function with netlify functions:serve, and test it, the HTTPS request is successfully posted :

19:57 $ netlify functions:serve
◈ Ignored general context env var: LC_ALL (defined in process)
◈ Loaded function hello-world.
◈ Loaded function identity-validate.
◈ Functions server is listening on 9999
Request from ::1: POST /.netlify/functions/identity-validate
{      "event": "validate",      "instance_id": "78c271e5-85ae-4184-a920-9d7505ad62aa",      "user": {        "id": "803a0f87-88dc-49e8-bf5e-f2599adb7323",        "aud": "",        "role": "",        "email": "fgarzon+1@gmail.com",        "app_metadata": {          "provider": "email"        },        "user_metadata": {          "full_name": "Frederic Garzon"        },        "created_at": "2021-11-09T20:36:30.741779Z",        "updated_at": "2021-11-09T20:36:30.745095Z"      }    }
email = fgarzon+1@gmail.com provider=email full_name=Frederic Garzon
fin3
Response with status 200 in 29 ms.
statusCode: 200
out=ok

However, This code does not generate any HTTPS request when run in production : No error, no exception, none of the logs that you see in the code within the request are written, here is the log.

7:23:10 PM: 07625f6e INFO   {"event":"validate","instance_id":"78c271e5-85ae-4184-a920-9d7505ad62aa","user":{"id":"13a74cc8-0e26-43ef-a7b6-c054fa1399a8","aud":"","role":"","email":"fgarzon+1@gmail.com","app_metadata":{"provider":"email"},"user_metadata":{"full_name":"rrrr"},"created_at":"2021-11-17T18:23:09.843998Z","updated_at":"2021-11-17T18:23:09.846527Z"}}
7:23:10 PM: 07625f6e INFO   email = fgarzon+1@gmail.com provider=email full_name=rrrr
7:23:10 PM: 07625f6e INFO   fin3
7:23:10 PM: 07625f6e Duration: 36.35 ms	Memory Usage: 57 MB	Init Duration: 159.90 m

Is there a limitation in the AWS lambda function that restrict HTTPS requests ? Or do I do something wrong ? (I am a total newbie with node and javascript…)

Thank you in advance for your help

I believe this would be way easier with node-fetch or axios. But if you were to use https, you might try something like:

const https = require('https')
exports.handler = async event => {
  return new Promise((resolve, reject) => {
    const user = JSON.parse(event.body).user
    const data = JSON.stringify({
      text: `email = ${user.email} provider=${user.app_metadata.provider} full_name=${user.user_metadata.full_name}`
    })
    const request = https.request({
      hostname: 'REDACTED',
      port: 443,
      path: '/hooks/REDACTED',
      method: 'POST',
      headers: {
        'content-type': 'application/json',
        'content-length': data.length
      }
    }, response => {
      response.setEncoding('utf8')
      let responseBody = ''
      response.on('data', chunk => {
        responseBody += chunk
      })
      response.on('end', () => {
        resolve(JSON.parse(responseBody))
      })
    })
    request.on('error', error => {
      reject(error)
    })
    request.write(data)
    request.end()
  }).then(() => {
    return {
      body: JSON.stringify({
        message:'success'
      }),
      statusCode: 200
    }
  }).catch(error => {
    return {
      body: error.toString(),
      statusCode: 500
    }
  })
}

Thank you for your answer. I tried the solution, and we are going into the right direction : The HTTPS request is sent successfully to my mattermost server, but the identity-validate function is called three times when a user signs up, and from the browser perspective, it fails with the message “Failed to
handle signup webhook”. The browser network console shows a 422 error on the POST to https://nifty-mcclintock-5552c0.netlify.app/.netlify/identity/signup
The netlify function logs on Netlify App shows 3 successful calls to the function, and the logs that shows 3 successful post to the mattermost server…

My lack of knowledge about nodejs does not help… Why using a Promise (which his a concept completely foreign to me) makes the https request work, but induces this weird behaviour that leads to 3 successful instead of one… ?
Any suggestion is welcome…

Your case looks oddly similar to:

Even they are getting 3 attempts and ending up with a 422. I’ll be curious to know if it’s affecting multiple users.

AWS Lambda functions need to be carefully written to strictly be async. This is because, unlike browsers where the front-end will eventually get a response (as long as the tab is open), AWS terminates the function as soon as it hits the return statement. So, if you don’t wait for something to complete before firing off the final return statement, that task would never complete. This is where Promises come into play.

The way a Promise (prefixed with a return keyword) would work (in this context) is: The function would wait for a piece of code to finish executing before moving on to the next line, and in your case, firing the final return statement.The Promise is considered executed when it returns resolve() or reject() (in case of success/failure respectively). So, here, we’re calling resolve() after the https connection is closed and thus, we’re making sure that the request is fully done before moving on to send the data back to the client.

About why it makes a call thrice, I’ve absolutely no idea. You’re right, it should not, yet it does. The only thing you could try right now is that, does this also happen when using node-fetch or axios? You probably not want to add an extra dependency to your project, but I guess it’s worth a try, right?

Hi all,

I found a solution via google search “aws lambda nodejs https request not working” here : node.js - Why is this HTTP request not working on AWS Lambda? - Stack Overflow

const https = require('https')
const response = {
  statusCode: 200,
  body: JSON.stringify({
    message:'success'
  })
};
exports.handler = async event => {
  return httprequest(event).then((data) => {
    return response;
  }).catch(error => {
    console.error(`ERROR sending message to mattermost : ${error}`)
    return response
  })
};

the httprequest function returns the Promise. The response of the main handler must be returned via the resolve (or reject) function.

Thank you for your help !