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
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.
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)
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())
Related
We recently deployed a Bot to Azure with type User assigned managed identity, so I only have app id, but not app password. I am trying to send messages via the Direct Line service from a python app. Following are my code:
from botframework.connector.auth import MicrosoftAppCredentials
from botframework.connector.client import ConnectorClient
credentials = MicrosoftAppCredentials("YOUR_CLIENT_ID", "")
base_url="https://directline.botframework.com/"
client = ConnectorClient(credentials, base_url=base_url)
connectorClient = ConnectorClient(credentials, base_url=base_url)
client.conversations.send_to_conversation(conversation_id, message, service_url=base_url)
The python package I installed is botframework-connector==4.14.0
I got an error about access_token. Can anyone help what I am missing?
Thanks
You can make use of the request library to send direct message to azure bot via direct line like this :
import requests
# Replace YOUR_DIRECT_LINE_SECRET with your bot's Direct Line secret
direct_line_secret = 'YOUR_DIRECT_LINE_SECRET'
# Set the headers for the request
headers = {
'Authorization': 'Bearer ' + direct_line_secret,
'Content-Type': 'application/json'
}
# Set the parameters for the request
data = {
'type': 'message',
'from': {
'id': 'YOUR_USER_ID'
},
'text': 'Hello, world!'
}
# Send the request to the Direct Line API
response = requests.post('https://directline.botframework.com/v3/directline/conversations/YOUR_CONVERSATION_ID/activities', json=data, headers=headers)
# Print the response status code
print(response.status_code)
If you want a user-assigned managed identity to authenticate the BotFrameworkConnectorClient without specifying an app password.
Follow this :
import os
from botframework.connector import ConnectorClient
from botframework.connector.auth import AzureActiveDirectoryAuthentication
# Replace YOUR_CLIENT_ID and YOUR_TENANT_ID with your own values
CLIENT_ID = 'YOUR_CLIENT_ID'
TENANT_ID = 'YOUR_TENANT_ID'
# Get the access token for the Bot Framework API
auth = AzureActiveDirectoryAuthentication(CLIENT_ID, TENANT_ID)
access_token = auth.get_access_token()
# Create a ConnectorClient
client = ConnectorClient(auth, base_url='https://directline.botframework.com/v3/directline')
# Set the parameters for the request
conversation_id = 'YOUR_CONVERSATION_ID'
activity = {
'type': 'message',
'from': {
'id': 'YOUR_USER_ID'
},
'text': 'Hello, world!'
}
# Send the message
response = client.conversations.send_to_conversation(conversation_id, activity)
# Print the response
print(response)
or you could use ManagedIdentityCredential
import os
from azure.identity import ManagedIdentityCredential
from botframework.connector import ConnectorClient
# Set the resource and tenant ID
RESOURCE = "https://directline.botframework.com"
TENANT_ID = "YOUR_TENANT_ID"
# Create a ManagedIdentityCredential
credential = ManagedIdentityCredential(resource=RESOURCE, tenant_id=TENANT_ID)
# Create a ConnectorClient
client = ConnectorClient(credential, base_url='https://directline.botframework.com/v3/directline')
# Set the parameters for the request
conversation_id = 'YOUR_CONVERSATION_ID'
activity = {
'type': 'message',
'from': {
'id': 'YOUR_USER_ID'
},
'text': 'Hello, world!'
}
# Send the message
response = client.conversations.send_to_conversation(conversation_id, activity)
# Print the response
print(response
)
I have a Spotify project needing authorization codes through their API. I built an API to redirect the user to Spotify's login and then turn back to my API along with the user's code.
The API:
import boto3
import requests
import base64
from fastapi import APIRouter
from fastapi.responses import RedirectResponse
from mangum import Mangum
from client import client_id, client_secret
app = APIRouter()
lambda_handler = Mangum(app,lifespan='off')
url = 'https://XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.lambda-url.me-south-1.on.aws'
#app.get("/")
async def root():
return RedirectResponse("/login/")
#app.get("/home/")
async def main(code: str):
encoded = base64.b64encode(
(client_id + ":" + client_secret).encode("ascii")
).decode("ascii")
base = "https://accounts.spotify.com/api/token"
payload = {
"grant_type": "authorization_code",
"code": code,
"redirect_uri": f"{url}/home/",
}
headers = {
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": "Basic " + encoded,
}
tokens = requests.post(base, data=payload, headers=headers).json()
refresh_token = tokens["refresh_token"]
access_token = tokens["access_token"]
email_base_url = "https://api.spotify.com/v1/me"
email_headers = {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": f"Bearer {access_token}",
}
email = requests.get(email_base_url, headers=email_headers).json()["email"]
dynamo = boto3.resource("dynamodb")
tokens = dynamo.Table("tokens")
item = {"email": email, "token": access_token, "refresh_token": refresh_token}
tokens.put_item(Item=item)
return {"message": "success"}
#app.get("/login/")
async def login():
base = "https://accounts.spotify.com/authorize?"
base += "response_type=code"
base += f"&client_id={client_id}"
base += "&scope=user-read-recently-played user-read-email"
base += f"&redirect_uri={url}/home/"
return RedirectResponse(base)
The API works as intended when I run it on localhost or use an ngrok tunnel. However, when I upload it to AWS Lambda and generate a function URL, the browser returns the error ERR_TOO_MANY_REDIRECTS for any of the three endpoints.
I've seen questions about this topic, but they all included CloudFront. I would be happy to provide information about the Lambda function as necessary.
Does it makes a different if you call baseurl/ vs baseurl/login ?
If someone is already logged in and hits the baseurl you redirecting him.
/ -> /login -> spotify -> /home
You can try sort / -> /login into one as its still just a redirection.
Update 1:
Do not use the same variable base in login and main.
Log the urls you are redirecting. This will help you understand what is going on.
Urlencode the url before passing it as url param. base += f"&redirect_uri= {urlencode(url)}/home/"
I am trying to replicate what this C# code for a REST API post with requests in python.
public static string SendData(string request, bool bIsJson,string apiKey)
{
var sMediaTYpe = bIsJson ? "application/json" : "application/xml";
var sResponse = System.String.Empty;
using (var c = new HttpClient(new HttpClientHandler() {UseDefaultCredentials = true}))
{
c.DefaultRequestHeaders.Add("ApiKey", apiKey);
var task = c.PostAsync(ConfigurationManager.AppSettings["GatewayRestUrl"],
new StringContent(request, Encoding.UTF8, sMediaTYpe));
Task.WaitAll(task);
var responseTask = task.Result.Content.ReadAsStringAsync();
Task.WaitAll(responseTask);
sResponse = responseTask.Result;
}
return sResponse;
}
From what I understand, this sends the windows login credentials and an API key that I have. I cannot figure out how to replicate this in python
This is what I have so far, but I am getting 401 responses
import requests
import getpass
restURL = 'url'
method = "post"
headers = {'ApiKey': 'key'}
username = getpass.getuser()
response = requests.request(method, restURL, headers=headers, auth=(username,getpass.getpass()), verify=False, json=params)
Thank you to #wkl. Seems it was a duplicate.
If anyone in the future finds this question I was able to use the requests_negotiate_sspi package to perform the windows credential auth.
import requests
from requests_negotiate_sspi import HttpNegotiateAuth
response = requests.request(method, restURL, headers=headers, auth=HttpNegotiateAuth(), verify=False, json=params)
I'm trying to launch a workflow template from a python script in Ansible AWX, my script looks like this
#!/usr/bin/env python
import requests
import json
from requests.auth import HTTPBasicAuth
import base64
import os
def main():
URL = 'https://myawx/api/v2/'
USER = 'username'
PASSWORD = 'password'
session = object
session = requests.Session()
session.auth = (USER, PASSWORD)
session.verify = False
myvars={
"test": "test"
}
AWX_JOB_TEMPLATES_API="https://myawx.com/api/v2/workflow_job_templates/10/launch/"
response = session.post(url=AWX_JOB_TEMPLATES_API,json=myvars, verify=False)
print(response)
if __name__ == '__main__':
main()
This launches the workflow fine, but it doesn't include the extra vars from the net_vars variable. My workflow_job_template has "Prompt on launch" enabled for the EXTRA VARIABLES field from the frontend of AWX.
Is it possible to launch a workflow with extra vars?
If you want to use extra vars via REST API, myvars needs to have extra_vars key.
For example, here's like.
#!/usr/bin/env python
import requests
def main():
url = "http://awx_server/api/v2/job_templates/14/launch/"
username = "admin"
passwd = "secret"
headers = {
"Content-Type": "application/json",
}
data = {}
data['extra_vars'] = {
"extra_string": "test message"
}
req = requests.post(url,
headers=headers,
auth=(username, passwd),
json=data,
verify=False)
print(req.status_code)
print(req.text)
if __name__ == "__main__":
main()
When the above sample, set test message to extra_string variable.
The extra_string is to be given as extra vars via REST API.
Question in short: how to get Facebook API access_token with permission
I want to read the reach for specific ad settings from the Facebook API using Python. In order to do so, I need a facebook access token with extended permissions. I use the following function to get a new access token, but the token I get does not have the proper permission levels. So: how to get an access_token with custom permissions, like you can do manually here?
Python example below (problem is actually language independent):
import requests
import json
from facebookads.adobjects.adaccount import AdAccount
from facebookads.api import FacebookAdsApi
from facebookads.adobjects.adset import AdSet
app_id = 'xxxx'
app_secret = 'xxxx'
account_id = 'xxxx'
def get_fb_token(app_id, app_secret):
payload = {'grant_type': 'client_credentials',
'client_id': app_id,
'client_secret': app_secret,
}
file = requests.post('https://graph.facebook.com/oauth/access_token?', params = payload)
string = file.content.decode('utf-8')
json_obj = json.loads(string)
return json_obj['access_token']
access_token = get_fb_token(app_id, app_secret)
account = AdAccount(account_id)
# initiate API
FacebookAdsApi.init(app_id, app_secret, access_token)
# Request reach
targeting_spec = {
'geo_locations': {
'countries': ['nl']
},
}
fb_params = {
'currency': 'EUR',
'optimize_for': AdSet.OptimizationGoal.offsite_conversions,
'targeting_spec': targeting_spec,
}
reach_estimate = account.get_reach_estimate(params=fb_params)
And the error message I get:
(#278) Reading advertisements requires an access token with the extended permission ads_read
Help is highly appreciated!
Try this:
payload = {
'grant_type': 'client_credentials',
'client_id': app_id,
'client_secret': app_secret,
'scope': 'ads_read'
}
file = requests.post('https://graph.facebook.com/oauth/access_token?', params = payload)
Also, you need to redirect to the correct endpoint (https://www.facebook.com/v2.xy/dialog/oauth), not POST to it. You cannot get a User token without user interaction.