How to connect a python websocket to an AppSync subscription, with IAM Auth?

Viewed 342

I want python to open an AWS AppSync Subscription and receive the updates.

The official blog is hand-wavy over how to authenticate a websocket with IAM.
https://aws.amazon.com/blogs/mobile/appsync-websockets-python/

It defers to this document about signing http requests, which does not mention websockets, and thus provides no clues what to include, or exlude, or vary, to start a websocket. I fear it is going to take me quite some time to digest, adapt, and test before I get anything happening.

IAM_header = {
    "accept": "application/json, text/javascript",
    "content-encoding": "amz-1.0",
    "content-type": "application/json; charset=UTF-8",
    "host": <HOST>,
    "x-amz-date": <ISO_UTC_TIMESTAMP>,,
    "X-Amz-Security-Token": <IAM SECURITY TOKEN>,
    "Authorization": <IAM SIGNATURE>
}

I don't suppose anyone has does this before and can share some insights, or code?

IAM_SIGNATURE
In the context of creating a websocket what are the "request_parameters" that we are signing?

So far I have fed details into requests_aws4auth and stuffed the headers from that into the websocket. That effort received this sort of reply:

message

<< {"payload":{"errors":[{"errorType":"BadRequestException","message":"The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.\n\nThe Canonical String for this request should have been\n'POST\n/graphql/connect\n\nhost:iutysrbt2qxx7ekx2xi.appsync-api.ap-southeast-2.amazonaws.com\nx-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934852b855\nx-amz-date:20200730T044539Z\nx-amz-security-token:FwoGZpgV9hfLzGbDiPLCK1HMsXYJapqB299GReKYAALLKPOgifkFMi2VZsiycpLbxp7ZDGGO4NfSgEaULYm9GMMkGIj1Ck/0YcckBNawWpisa6NxdMs=\n\nhost;x-amz-content-sha256;x-amz-date;x-amz-security-token\n44136fa355b3678a1b4fc21fe77e8310c060f61caaff8a'\n\nThe String-to-Sign should have been\n'AWS4-HMAC-SHA256\n20200730T044539Z\n20200730/ap-southeast-2/appsync/aws4_request\n29a5ecebf83602958c569f1ae18b5fa8353dd04833a833885'\n"}]},"type":"connection_error"}

So I need to work out the "Canonical String", the "String-to-Sign", and how to stuff them into the Authorization header.

1 Answers

graphql-python/gql supports AWS AppSync since version 3.0.0rc0.

It supports queries, mutation and even subscriptions on the realtime endpoint.

It supports IAM, api key and JWT authentication methods.

The documentation is available here

Here is an example of a subscription using the IAM authentication:

import asyncio
import os
import sys

from gql import Client, gql
from gql.transport.appsync_websockets import AppSyncWebsocketsTransport

# Uncomment the following lines to enable debug output
# import logging
# logging.basicConfig(level=logging.DEBUG)


async def main():

    # Should look like:
    # https://XXXXXXXXXXXXXXXXXXXXXXXXXX.appsync-api.REGION.amazonaws.com/graphql
    url = os.environ.get("AWS_GRAPHQL_API_ENDPOINT")

    if url is None:
        print("Missing environment variables")
        sys.exit()

    transport = AppSyncWebsocketsTransport(url=url)

    async with Client(transport=transport) as session:

        subscription = gql(
            """
subscription onCreateMessage {
  onCreateMessage {
    message
  }
}
"""
        )

        print("Waiting for messages...")

        async for result in session.subscribe(subscription):
            print(result)


asyncio.run(main())
Related