Python Lambda function access cognito "sub" uuid - python

I have a Lambda function written in python that is triggered by the API Gateway with proxy integration and a Cognito user pool authorizer. I am trying to access the "sub" UUID that Cognito gives every user, but nothing I try works. I have just about exhausted google of all related search results and nothing that I have found seems valid. They either return a null or they crash. My function (with every attempted UUID access line commented out) is below:
import json
def lambda_handler(event, context):
#UniqueUser = context.identity.cognito_identity_id
#UniqueUser = context.authorizer.claims.sub
#UniqueUser = event.requestContext.authorizer.claims.sub
#UniqueUser = event.request.userAttributes.sub
#UniqueUser = event.queryStringParameters.sub
try:
# Something that gets sub from cognito
except:
UniqueUser = "not valid code"
if not UniqueUser:
UniqueUser = "UniqueUser is null"
return {
'statusCode': 200,
'headers': {
'Access-Control-Allow-Origin': '*'
},
'body': json.dumps('Hello from Lambda ' + UniqueUser + "!")
};
Does anyone know any possible sources of this behavior or solution to this problem?

If you need to access the UUID of a Cognito user who is calling a Lambda function through the API Gateway with proxy integration, use the code below.
UniqueUser = event['requestContext']['authorizer']['claims']['sub']

Related

Is there a way to decode the string returned from AWS lambda to meta's whatsapp business api?

I am trying to setup a webhook in AWS Lambda (using API Gateway) for Meta's WhatsApp Business API. They have the following guidelines:
Whenever your endpoint receives a verification request, it must:
Verify that the hub.verify_token value matches the string you set in the Verify Token field when you configure Webhooks in your App Dashboard (you haven't set up this token string yet). Respond with the hub.challenge value."
I have setup all the query strings it needs in the API gateway. Here is what my lambda handler looks like:
def lambda_handler(event, context):
response = {
"status": 400,
"body" : "failed"
}
print(str(event))
print("Received context" + str(context))
if(len(event) != 0 and (event['hub.verify_token'] == "some value")):
response['status'] = 200
response['body'] = event['hub.challenge']
return event['hub.challenge']
#print(response)
#returnResponse = re.findall('[0-9]+', event['hub.challenge'])
#return returnResponse[0]
else:
return(response)
the event looks like:
{
"hub.mode" : "some value",
"hub.verify_token": "some value",
"hub.challenge": "1158201444"
}
The response in AWS console looks like "1158201444" but on meta's end, the response looks like "\"1158201444\"" and the webhook confirmation fails in Meta's dashboard.
How can remove the extra characters and decode the string? I have already tried regex and still getting the extra characters (\"\").
So what worked for me is was that I passed hub.challenge as a integer format instead of string. I just converted it to an integer before returning it.

How to update a security group using lambda function?

I'm trying to update AWS security group with one inbound rules using lambda function Python 3.7. For example: I would like to add my public IP with 8443 Port in existing security group. I have followed below link to achieve.
Modifying ec2 security group using lambda function
When I used this code on Lambda function with Python 3.7, it's not working.
import boto3
import hashlib
import json
import copy
import urllib2
# ID of the security group we want to update
SECURITY_GROUP_ID = "sg-XXXX"
# Description of the security rule we want to replace
SECURITY_RULE_DESCR = "My Home IP"
def lambda_handler(event, context):
new_ip_address = list(event.values())[0]
result = update_security_group(new_ip_address)
return result
def update_security_group(new_ip_address):
client = boto3.client('ec2')
response = client.describe_security_groups(GroupIds=[SECURITY_GROUP_ID])
group = response['SecurityGroups'][0]
for permission in group['IpPermissions']:
new_permission = copy.deepcopy(permission)
ip_ranges = new_permission['IpRanges']
for ip_range in ip_ranges:
if ip_range['Description'] == 'My Home IP':
ip_range['CidrIp'] = "%s/32" % new_ip_address
client.revoke_security_group_ingress(GroupId=group['GroupId'], IpPermissions=
[permission])
client.authorize_security_group_ingress(GroupId=group['GroupId'], IpPermissions=
[new_permission])
return ""
I got response as "", when I tested this code on lambda function.
The revoke_security_group_ingress() and authorize_security_group_ingress() calls in boto3 includes a Return field in the response:
Returns true if the request succeeds; otherwise, returns an error.
However, your code does not seem to be storing a response or examining its contents.

How to validate Slack API request?

I've a slack app that is sending to a service written in typescript that is forwarding the message to my python script where I'm trying to validate the request. However, for some reason, the validation always fails.
The typescript relevant code:
const rp = require('request-promise');
var qs = require('querystring')
export const handler = async (event: any, context: Context, callback: Callback): Promise<any> => {
const options = {
method: method,
uri: some_url,
body: qs.parse(event.body),
headers: {
signature: event.headers['X-Slack-Signature'],
timestamp: event.headers['X-Slack-Request-Timestamp']
},
json: true
};
return rp(options);
The python code (based on this article) :
def authenticate_message(self, request: Request) -> bool:
slack_signing_secret = bytes(SLACK_SIGNING_SECRET, 'utf-8')
slack_signature = request.headers['signature']
slack_timestamp = request.headers['timestamp']
request_body = json.loads(request.body)['payload']
basestring = f"v0:{slack_timestamp}:{request_body}".encode('utf-8')
my_signature = 'v0=' + hmac.new(slack_signing_secret, basestring, hashlib.sha256).hexdigest()
return hmac.compare_digest(my_signature, slack_signature))
I'm pretty sure the issue is the way I'm taking the body but tried several options and still no luck.
Any ideas?
Thanks,
Nir.
I had the same issue. My solution was to parse the payload to replace '/' by %2F and ':' by %3A. It's not explicit in the Slack doc but if you see the example, that's how it's shown:
'v0:1531420618:token=xyzz0WbapA4vBCDEFasx0q6G&team_id=T1DC2JH3J&team_domain=testteamnow&channel_id=G8PSS9T3V&channel_name=foobar&user_id=U2CERLKJA&user_name=roadrunner&command=%2Fwebhook-collect&text=&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT1DC2JH3J%2F397700885554%2F96rGlfmibIGlgcZRskXaIFfN&trigger_id=398738663015.47445629121.803a0bc887a14d10d2c447fce8b6703c'
You see command and response_url are parsed.
I managed to get this working in Python. I see you ask in Typescript, but I hope this python script helps:
#app.route('/slack-validation', methods=['GET', 'POST'])
def slack_secutiry():
headers = request.headers
timestamp = request.headers['X-Slack-Request-Timestamp']
slack_payload = request.form
dict_slack = slack_payload.to_dict()
### This is the key that solved the issue for me, where urllib.parse.quote(val, safe='')] ###
payload= "&".join(['='.join([key, urllib.parse.quote(val, safe='')]) for key, val in dict_slack.items()])
### compose the message:
sig_basestring = 'v0:' + timestamp + ':' + payload
sig_basestring = sig_basestring.encode('utf-8')
### secret
signing_secret = slack_signing_secret.encode('utf-8') # I had an env variable declared with slack_signing_secret
my_signature = 'v0=' + hmac.new(signing_secret, sig_basestring, hashlib.sha256).hexdigest()
print('my signature: ')
print(my_signature)
return '', 200
It might be useful for you to check how the request validation feature is implemented in the Bolt framework:
https://github.com/slackapi/bolt-python/blob/4e0709f0578080833f9aeab984a778be81a30178/slack_bolt/middleware/request_verification/request_verification.py
Note that it is implemented as a middleware, enabled by default when you instantiate the app (see attribute request_verification_enabled).
You can inspect this behaviour and/or change it if you want to validate the requests manually:
app = App(
token=SLACK_BOT_TOKEN,
signing_secret=SLACK_SIGNING_SECRET,
request_verification_enabled=False
)
The following solution solves the problem of verification of signing secret of slack
#!/usr/bin/env python3
import hashlib
import hmac
import base64
def verify_slack_request(event: dict, slack_signing_secret: str) -> bool:
"""Verify slack requests.
Borrowed from https://janikarhunen.fi/verify-slack-requests-in-aws-lambda-and-python.html
- Removed optional args
- Checks isBase64Encoded
:param event: standard event handler
:param slack_signing_secret: slack secret for the slash command
:return: True if verification worked
"""
slack_signature = event['headers']['x-slack-signature']
slack_time = event['headers']['x-slack-request-timestamp']
body = event['body']
if event['isBase64Encoded']:
body = base64.b64decode(body).decode("utf-8")
""" Form the basestring as stated in the Slack API docs. We need to make a bytestring"""
base_string = f'v0:{slack_time}:{body}'.encode('utf-8')
""" Make the Signing Secret a bytestring too. """
slack_signing_secret = bytes(slack_signing_secret, 'utf-8')
""" Create a new HMAC 'signature', and return the string presentation."""
my_signature = 'v0=' + hmac.new(
slack_signing_secret, base_string, hashlib.sha256
).hexdigest()
''' Compare the the Slack provided signature to ours.
If they are equal, the request should be verified successfully.
Log the unsuccessful requests for further analysis
(along with another relevant info about the request).'''
result = hmac.compare_digest(my_signature, slack_signature)
if not result:
logger.error('Verification failed. my_signature: ')
logger.error(f'{my_signature} != {slack_signature}')
return result
if __name__ == '__main__':
# add correct params here
print(verify_slack_request({}, None))
Borrowed From:
https://gist.github.com/nitrocode/288bb104893698011720d108e9841b1f
Credits: https://gist.github.com/nitrocode

Python Lambda giving botocore.errorfactory.InvalidLambdaResponseException when triggered on postconfirmation

I have setup AWS Lambda function that is triggered on an AWS Cognito. The trigger on a successful email confirmation. The Lambda function is in Python3.6.
I am referring to the AWS documentation for Cognito postConfirmation trigger.
https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-post-confirmation.html
"response": {}
So far I have tried returning None, {}, '{}'(empty json string) or valid dictionary like {'status':200, 'message':'the message string'} but it is giving error.
botocore.errorfactory.InvalidLambdaResponseException: An error occurred (InvalidLambdaResponseException) when calling the ConfirmSignUp operation: Unrecognizable lambda output
What should be a valid response for the post confirmation function?
here is the part of code.
from DBConnect import user
import json
def lambda_handler(event, context):
ua = event['request']['userAttributes']
print("create user ua = ", ua)
if ('name' in ua):
name = ua['name']
else:
name = "guest"
newUser = user.create(
name = name,
uid = ua['sub'],
owner = ua['sub'],
phoneNumber = ua['phone_number'],
email = ua['email']
)
print(newUser)
return '{}' # <--- I am using literals here only.
You need to return the event object:
return event
This is not obvious in the examples they provide in the documentation. You may want to check and ensure the event object does contain a response key (it should).

python aws put_bucket_policy() MalformedPolicy

How does one use put_bucket_policy()? It throws a MalformedPolicy error even when I try to pass in an existing valid policy:
import boto3
client = boto3.client('s3')
dict_policy = client.get_bucket_policy(Bucket = 'my_bucket')
str_policy = str(dict_policy)
response = client.put_bucket_policy(Bucket = 'my_bucket', Policy = str_policy)
* error message: *
botocore.exceptions.ClientError: An error occurred (MalformedPolicy) when calling the PutBucketPolicy operation: This policy contains invalid Json
That's because applying str to a dict doesn't turn it into a valid json, use json.dumps instead:
import boto3
import json
client = boto3.client('s3')
dict_policy = client.get_bucket_policy(Bucket = 'my_bucket')
str_policy = json.dumps(dict_policy)
response = client.put_bucket_policy(Bucket = 'my_bucket', Policy = str_policy)
Current boto3 API doesn't have a function to APPEND the bucket policy, whether add another items/elements/attributes. You need load and manipulate the JSON yourself. E.g. write script load the policy into a dict, append the "Statement" element list, then use the policy.put to replace the whole policy. Without the original statement id, user policy will be appended. HOWEVER, there is no way to tell whether later user policy will override rules of the earlier one.
For example
import boto3
s3_conn = boto3.resource('s3')
bucket_policy = s3_conn.BucketPolicy('bucket_name')
policy = s3_conn.get_bucket_policy(Bucket='bucket_name')
user_policy = { "Effect": "Allow",... }
new_policy = policy['Statement'].append(user_policy)
bucket_policy.put(Policy=new_policy)
The user don't need know the old policy in the process.

Categories