Search code examples
iotaws-sdk-jsaws-iot-core

connect from web to iot core using a custom authorizer


I'm trying to use a custom authorizer to authenticate a web client. I have succesfully created a dedicated lambda and a custom authorizer. If I launch aws iot describe-authorizer --authorizer-name <authorizer-name> I can see

{
    "authorizerDescription": {
        "authorizerName": "<authorizer-name>",
        "authorizerArn": "...",
        "authorizerFunctionArn": "...",
        "tokenKeyName": "<token-key-name>",
        "tokenSigningPublicKeys": {
            "<public-key-name>": "-----BEGIN PUBLIC KEY-----\n<public-key-content>\n-----END PUBLIC KEY-----"
        },
        "status": "ACTIVE",
        "creationDate": "...",
        "lastModifiedDate": "...",
        "signingDisabled": false,
        "enableCachingForHttp": false
    }
}

Moreover I can test it succesfully:

$ aws iot test-invoke-authorizer --authorizer-name '<authorizer-name>' --token '<public-key-name>' --token-signature '<private-key-content>'
{
    "isAuthenticated": true,
    "principalId": "...",
    "policyDocuments": [ "..." ],
    "refreshAfterInSeconds": 600,
    "disconnectAfterInSeconds": 3600
}
$

But I cannot connect using the browser.

I'm using aws-iot-device-sdk and according the SDK documentation I should set customAuthHeaders and/or customAuthQueryString (my understanding is that the latter should be used in web environment due to a limitation of the browsers) with the headers / queryparams X-Amz-CustomAuthorizer-Name, X-Amz-CustomAuthorizer-Signature and TestAuthorizerToken but no matter what combination I set for these values the iot endpoint always close the connection (I see a 1000 / 1005 code for the closed connection)

What I've written so far is


const CUSTOM_AUTHORIZER_NAME = '<authorizer-name>';
const CUSTOM_AUTHORIZER_SIGNATURE = '<private-key-content>';
const TOKEN_KEY_NAME = 'TestAuthorizerToken';
const TEST_AUTHORIZER_TOKEN = '<public-key-name>';


function f(k: string, v?: string, p: string = '&'): string {
    if (!v)
        return '';
    return `${p}${encodeURIComponent(k)}=${encodeURIComponent(v)}`;
}

const client = new device({
            region: '...',
            clientId: '...',
            protocol: 'wss-custom-auth' as any,
            host: '...',
            debug: true,
            // customAuthHeaders: {
            //     'X-Amz-CustomAuthorizer-Name': CUSTOM_AUTHORIZER_NAME,
            //     'X-Amz-CustomAuthorizer-Signature': CUSTOM_AUTHORIZER_SIGNATURE,
            //     [TOKEN_KEY_NAME]: TEST_AUTHORIZER_TOKEN
            // },
            customAuthQueryString: `${f('X-Amz-CustomAuthorizer-Name', CUSTOM_AUTHORIZER_NAME, '?')}${f('X-Amz-CustomAuthorizer-Signature', CUSTOM_AUTHORIZER_SIGNATURE)}${f(TOKEN_KEY_NAME, TEST_AUTHORIZER_TOKEN)}`,
        } as any);

As you can see I started having also doubts about the headers names!

After running my code I see that the client tries to do a GET to the host with the querystring that I wrote. I also see that IoT core responds with a 101 Switching Protocols, and then that my client send the CONNECT command to IoT via websocket and then another packet from my browser to the backend system. Then the connection is closed by IoT. Looking at cloudwatch I cannot see any interaction with the lambda, it's like the request is blocked.

my doubts are:

  • first of all, is it possible to connect via mqtt+wss using only a custom auth, without cognito/certificates? keep in mind that I am able to use a cognito identity pool without errors, but I need to remove it.
  • is it correct that I just need to set up the customAuthQueryString parameter? my understanding is that this should be used on the web.
  • what are the values I should set up for the various headers/queryparams? X-Amz-CustomAuthorizer-Name is self explanatory, but I'm not sure about X-Amz-CustomAuthorizer-Signature (it's correct to fill it with the content of my private key?). moreover I'm not sure about the TestAuthorizerToken. Is it the correct key to set up?

I've also tried to run the custom_authorizer_connect of the sdk v2 but it's still not working, and I run out of ideas.


Solution

  • turns out the problem was in the permissions set on the backend systems.