Mqtt connection gives 403 for aws IOT Pre-Signed URL
Asked Answered
G

1

7

I have aws lambda function that generates IOT websocket URL as below.

const v4 = require('aws-signature-v4');
const crypto = require('crypto');

const WSSURL = v4.createPresignedURL(
        'GET',
        process.env.IOT_ENDPOINT_HOST.toLowerCase(),
        '/mqtt',
        'iotdevicegateway',
        crypto.createHash('sha256').update('', 'utf8').digest('hex'),
        {
            'key': process.env.IOT_ACCESS_KEY,
            'secret': process.env.IOT_SECRET_KEY,
            'protocol': 'wss',
            'region': process.env.IOT_AWS_REGION,
        }
    );

and I have mqttjs at client side that use this url and try to connect web socket as following.

var options = {
        will    : {
            topic  : LAST_WILL_TOPIC,
            payload: getMessageString(wss_userId, wss_email, wss_userType, {})
        },
        clientId: wss_userType + '||' + wss_userId
    };

    wssClient = mqtt.connect(WSSURL, options); 

This code was working perfect before few months but now connection doesn't initiate and gives following error

failed: Error during WebSocket handshake: Unexpected response code: 403
Gules answered 25/7, 2018 at 9:58 Comment(1)
Form last few days error started again and reason for that was AWS has changed IOT endpoint URL. kindly double check that part also if this error is happening again.Gules
C
4

Our team have run into this issue recently too.

The problem in the aws-signature-v4 module with version > 1.2.0.

Here is a code:

`options.sessionToken = options.sessionToken || process.env.AWS_SESSION_TOKEN;` 

AWS lambda by default has a AWS_SESSION_TOKEN in process.env

You should use STS to assume role and use sessionToken from it

import v4 from 'aws-signature-v4' // ^1.3.0
import STS from 'aws-sdk/clients/sts'

const { IOT_ENDPOINT_HOST, IOT_ROLE_ARN } = process.env
const sts = new STS()

const createSocketUrl = async () => {
  const data = await sts
    .assumeRole({
      RoleArn: IOT_ROLE_ARN,
      RoleSessionName: `some-role-session-name`,
      DurationSeconds: 3600
    })
    .promise()

  return v4.createPresignedURL(
    'GET',
    IOT_ENDPOINT_HOST,
    '/mqtt',
    'iotdevicegateway',
    '',
    {
      key: data.Credentials.AccessKeyId,
      secret: data.Credentials.SecretAccessKey,
      sessionToken: data.Credentials.SessionToken,
      protocol: 'wss'
    }
  )
}

or if you don't want to use STS

import v4 from 'aws-signature-v4' // ^1.3.0

const { IOT_ENDPOINT_HOST, IOT_ACCESS_KEY_ID, IOT_SECRET_ACCESS_KEY } = process.env

const createSocketUrl = () => {
  const { AWS_SESSION_TOKEN } = process.env

  delete process.env['AWS_SESSION_TOKEN']

  const url = v4.createPresignedURL(
    'GET',
    IOT_ENDPOINT_HOST,
    '/mqtt',
    'iotdevicegateway',
    '',
    {
      key: IOT_ACCESS_KEY_ID,
      secret: IOT_SECRET_ACCESS_KEY,
      protocol: 'wss'
    }
  )
  process.env['AWS_SESSION_TOKEN'] = AWS_SESSION_TOKEN

  return url
}
Constituency answered 15/11, 2018 at 13:28 Comment(1)
I tried without using STS. but still it throws the error. I downgraded to 1.1.0 but still the same error is there. Any other reason as to why it throws 403?Emelina

© 2022 - 2024 — McMap. All rights reserved.