How to call an AppSync mutation with Cognito authentication using python?

Question:

Is it possible to calling an AppSync mutation with Cognito authentication using Python? How?

I am trying to use boto3, but I don’t found a way to execute graphql operations.

https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/appsync.html

Asked By: Andrey

||

Answers:

You can turn your API auth mode to be “API KEY” and call an AppSync mutation with http.

For example.

import requests
import json

APPSYNC_API_KEY = 'da2-xxxxxxxxxxxxx'
APPSYNC_API_ENDPOINT_URL = 'https://xxxxxxxxxxxxx.appsync-api.us-west-2.amazonaws.com/graphql'

headers = {
    'Content-Type': "application/graphql",
    'x-api-key': APPSYNC_API_KEY,
    'cache-control': "no-cache",
}

def execute_gql(query):
    payload_obj = {"query": query}
    payload = json.dumps(payload_obj)
    response = requests.request("POST", APPSYNC_API_ENDPOINT_URL, data=payload, headers=headers)
    return response

Imagine you have a model called Items and you can easily make query like below:

if __name__ == '__main__':
    print(execute_gql("query { listItems { items { id name } } }").json())

Simply replace the string with the mutation operation.

Answered By: yiksanchan

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 (for Cognito for example) authentication methods.

The documentation is available here

Here is an example of a mutation using the API Key authentication:

import asyncio
import os
import sys
from urllib.parse import urlparse

from gql import Client, gql
from gql.transport.aiohttp import AIOHTTPTransport
from gql.transport.appsync_auth import AppSyncApiKeyAuthentication

# 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")
    api_key = os.environ.get("AWS_GRAPHQL_API_KEY")

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

    # Extract host from url
    host = str(urlparse(url).netloc)

    auth = AppSyncApiKeyAuthentication(host=host, api_key=api_key)

    transport = AIOHTTPTransport(url=url, auth=auth)

    async with Client(
        transport=transport, fetch_schema_from_transport=False,
    ) as session:

        query = gql(
            """
mutation createMessage($message: String!) {
  createMessage(input: {message: $message}) {
    id
    message
    createdAt
  }
}"""
        )

        variable_values = {"message": "Hello world!"}

        result = await session.execute(query, variable_values=variable_values)
        print(result)


asyncio.run(main())
Answered By: leszek.hanusz

It appears that idToken from auth response is the only one you need to pass under the Authorization header. Nothing more is required. Auth middleware shouldn’t be passed in the Transport object at all. Here is how it finally worked from my side:

import boto3
from gql import gql, Client as GQLClient
from gql.transport.requests import RequestsHTTPTransport

username = "YOUR_USER_NAME"
password = "YOUR_PASSWORD"
authClientId = "YOUR_COGNITO_AUTH_APP_CLIENT_ID"
regionName = "YOUR_REGION_NAME"
appSyncEndpoint = "YOUR_APPSYNC_ENDPOINT"

def authenticate():
  cgClient = boto3.client("cognito-idp", region_name=regionName)
  response = cgClient.initiate_auth(
      ClientId=authClientId,
      AuthFlow="USER_PASSWORD_AUTH",
      AuthParameters={"USERNAME": username, "PASSWORD": password},
  )
  return response["AuthenticationResult"]["IdToken"]


def make_client(idToken):
    headers = {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        'Authorization': idToken #<<<< Just an idToken
    }
    transport = RequestsHTTPTransport(url=appSyncEndpoint,
                                      headers=headers) #<<<< there is no `auth` parameter here at all
    client = GQLClient(transport=transport,
                    fetch_schema_from_transport=False)
    return client

creds = authenticate()
queryText = """mutation createMessage($message: String!) {
  createMessage(input: {message: $message}) {
    id
    message
    createdAt
  }
}"""
asClient = make_client(creds)
result = asClient.execute(gql(queryText))
print(result)
Answered By: zelibobla