How can I pass a required input from application to Amazon Lex Bot? - aws-lambda

I have created a bot with Amazon lex and it's validation & fulfillment with Python and MongoDb.
Bot is working as expected.
Now I am working to integrate my Bot with an ipad application.
Currently my bot asks user about his account id and then bot validate that id in DB and according responses.
Now after integration instead of asking the account id from user, that id should be passed from ipad application to the bot and then bot should responds according.
My question is about this. How can we pass account id from ipad app to bot and then how can my bot or lambda function can get that?
Please suggest if anyone has done similar functionality.

You will want to use requestAttributes or sessionAttributes to pass information like an account ID to your bot with the initial input.
Your bot can then retrieve these from event.requestAttributes or event.sessionAttributes
References: Lex-Lambda Input Event and Response Format
sessionAttributes – Application-specific session attributes that the client sends in the request. If you want Amazon Lex to include them in the response to the client, your Lambda function should send these back to Amazon Lex in the response. For more information, see Setting Session Attributes
requestAttributes – Request-specific attributes that the client sends in the request. Use request attributes to pass information that doesn't need to persist for the entire session. If there are no request attributes, the value will be null. For more information, see Setting Request Attributes
Additional Info
You will want to handle the passing of userInput to your Lex bot yourself in order to include requestAttributes data. To do this, you will need to use PostContent (text or audio input) or PostText (text input only) to send data to your Lex bot.
Your Lex bot will interpret the input and pass along the requestAttributes to your Lambda function, where you can handle the logic based on the Account ID.
Sending user input data as JSON object via PostText:
POST /bot/botName/alias/botAlias/user/userId/text HTTP/1.1
Content-type: application/json
{
"inputText": "Hello Bot",
"requestAttributes": {
"accountID" : "xxxxxxxx"
},
"sessionAttributes": {
"name" : "John Smith"
}
}
To see what Lex will pass to your Lambda Function and how to retrieve the requestAttributes there, see this question where I've answered that in more depth:
AWS Lex Python Codehook references

Related

AWS Cognito Pre-Token Generation not adding custom claims to ID Token (with ALB setup + Auth Code flow)

I'm adding custom claims to Cognito's ID token using the "Pre Token Generation" trigger.
Problem
The lambda is triggered, but the issued ID Token doesn't include the claims I added. Am I missing something?
My setup
Using OAuth 2.0 with authorization code flow
My client app sits behind a load balancer (alb). The alb interacts with Cognito to get the Access + ID Tokens in the form of a ALBSessionCookie. Very similar to [0]
To get the ID Token, the client calls a custom endpoint to my backend with the ALBSessionCookie. The backend uses that cookie to return a decoded ID Token to the user. This is the ID Token that I expect should have the custom claims included.
[0] https://www.exampleloadbalancer.com/auth_detail.html
Lambda function (pre-token generation trigger)
Format taken from https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-token-generation.html#aws-lambda-triggers-pre-token-generation-example-1
exports.handler = (event, context, callback) => {
event.response = {
"claimsOverrideDetails": {
"claimsToAddOrOverride": {
"my-custom-claims-namespace": JSON.stringify({
"custom-claim-1": "hello",
"custom-claim-2": "hello",
})
}
}
}
callback(null, event)
}
If I can't make this work with ALB, what are some workarounds? Some ideas:
Call Cognito directly for an ID Token (somehow), hoping that will trigger the lambda to issue a JWT with the custom claims
Call Cognito via AmplifyJS
I have a feeling this is expected behavior, though seems like a limitation. Looking here:
https://www.exampleloadbalancer.com/auth_detail.html
We can see that the following steps occur:
ALB receives JWT (ID token, Access Token)
ALB to send access token
ALB receives user info(claims)
I believe the ALB is then not sending the contents of the Decoded ID token (That were manipulated by the Lambda trigger) back to the backend but instead sends the 'user info(claims)' (returned from the UserInfo endpoint) which are not effected by the Cognito trigger.
Yeah the ALB doesn't work that way, the ID Token that Lambda trigger customizes is the one you get when a user Authenticates. There are a couple of options.
Custom User Attributes
The least invasive IMO if instead of adding these attributes in the Lambda trigger, you could have them as custom attributes in Cognito, these I do believe will be in this token. You can sync these attributes at each successful Authorization. That may meet your requirements.
API GW
You could put an API GW either between your LB and your APP or infront of your LB. The API GW does give you a layer in which you can do all this stuff and more with customizing headers, tokens etc. For example you could have a Lambda Authorizer which reads this access token, and returns a context which you can reference in your integration requests back to your backend. It's a bit more involved and will add at least some latency to your app, although you can safely have a large TTL on your auth response because your LB is already doing Auth and you only want some extra attributes. You could also do a re-design and put this all in API GW and get all the bells and whistles it has but you might not need them.
But yeah probably easiest to use the first option if possible as that won't require you to do a redesign and you will just need to change your attribute names to custom:....

Restrict users to create data only for themselves

I have a microservices architecture deployed on kubernetes with istio gateway, AWS cognito used for Authentication. Now when the user logs in to the APP, he can create/view data using the API's. The mobile FE application sends the "userId" of the logged in user for which data needs to be created or fetched. JWT token from cognito is sent with each request which has the userId in claims.
What I want to achieve is restrict my API's such that the data should be created only for the used whose token is sent or the data should be fetched only for the user in the JWT token. For example consider the below request
POST /api/data/submit
{
"name": "John doe",
"address": "131 parklane street",
"userId": 1000,
...
}
In the above request, any user(Say id:90) can create the data for user id 1000.
The solution currently implemented in the project is using Spring Aspects. The drawback is that it works only for API's where userId is sent as PathVariable though it can be modified to check request objects using reflection but that wouldn't be efficient.
The solution I have in mind is using Bean Validation. I create a custom annotation which can be applied at field and parameter level with a custom validator and validate that the value should match the userId present in the token.
I tried searching for best approach but mostly found solutions for ROLE based authorization though what I want to achieve is different.
Any guidance would be helpful.
In one of my project we are doing the same. In such calls, we are not sending userId field. Services are extracting the same from token instead of depending upon POST data field.

How to send direct messages to a user as app in app channel

How is it possible to send message in slack directly to the user, by user.id as application.
this application has scope: bot,channels:write,emoji:read,users:read,users:read.email
I find how to send message only as DM or by webhooks, but there is no scope for that. Any one has idea?
If I understand your question correctly, you want to send direct messages to users in the app channel instead of the standard slackbot channel.
In order to do that you need to
Your app needs the bot scope and a bot user
Open a direct message channel from your app with the user with conversations.open. You get back a direct message ID.
Send a message with chat.postMessage to the the direct message channel ID
Make sure to use your bot access token (not the user access token) from your Slack app.
The bot scope gives you all permissions needed to open and send DMs to users from your bot channel. No other scopes are required.
You can also use the new conversations methods, which work for all kind of channel types to do the same.
See also this question on the same topic.
There is an alternative way to solve this, which can be more suitable if your app uses a bot to operate with Slack API.
You need to call chat.postMessage API method and specify channel argument equal to the user ID (e.g. U0G9QF9C6) you want to message and as_user argument is true.
Important detail - ensure you are using bot access token (learn here how to obtain it).
Example:
curl -X POST "https://slack.com/api/chat.postMessage" -H "accept: application/json" -d token=BOT_ACCESS_TOKEN -d channel=U0G9QF3C6 -d text=Hello -d as_user=true
In this way, your message will be always sent on behalf (name and icon) of your bot and will be display like a direct message in the app channel (YourAppChannel in the Slack sidebar).
Compared to the approach of #ErikKalkoken you have no need to create a channel in advance and as a result, keep track of its ID (it may be good or bad depending on your needs).
For those who is still searching for detailed answer:
First of all you need to make call to this endpoint.
You need to make call with bot token and provide into users param value of user you want to send message.
Also you need set prevent_creation and return_im to true.
Example:
Authorization: Bearer {your_bot_token}
{
"users": "U12345679",
"prevent_creation": true,
"return_im": true
}
After that you will have your channel id to which you want to send message.
Example response:
{
"ok": true,
"no_op": true,
"already_open": true,
"channel": {
"id": "D123456789", <-- this is your id
...
"unread_count": 0,
"unread_count_display": 0,
"is_open": true,
"priority": 0
}
}
and then with same bot token and user id send message with help of this one
Blockquote I keep getting {"ok":false,"error":"not_in_channel"} do i need to manually add the bot to a channel?? –
Abhijeet Bajracharya
Feb 4 '20 at 8:04
you need to get scope that allow to send messages like this
There is no need to use the conversation.info, you can post message (DM), by using the users.list endpoint and fetch the user id, which then you can use in chat.postMessage

Amazon Lex Facebook Messenger Webhook and getting Page Scoped ID

I'm currently doing some proof of concept stuff with Amazon Lex, Lambda, and the Facebook Messenger Webhook integration for chatbots.
I see through Facebook's Webhook that when messages are sent to Lex, they include a user ID (which I believe is the Page Scoped ID).
Through Lambda initialization and validation with Amazon Lex, I don't see this page scoped ID passed anywhere in the event object inside of Lambda, which leads me to believe Lex if formatting the event and stripping out any body content that is passed from Facebook.
Is there a way to read the body content of the incoming request?
When accessing Lex through Facebook, Lex will pass Facebook data to your Lambda Function inside of event.requestAttributes. This is the structure:
"requestAttributes": {
"x-amz-lex:facebook-page-id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"x-amz-lex:channel-id": "XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"x-amz-lex:webhook-endpoint-url": "https://channels.lex.us-east-1.amazonaws.com/facebook/webhook/XXX-XXXX-XXXXXXXXX",
"x-amz-lex:accept-content-types": "PlainText",
"x-amz-lex:user-id": "XXXXXXXXXXXXXXX",
"x-amz-lex:channel-name": "FacebookLexBotAppName",
"x-amz-lex:channel-type": "Facebook"
},
To get the Page Access Token = event.requestAttributes['x-amz-lex:facebook-page-id']
To get the PSID (Page Scoped ID) = event.requestAttributes['x-amz-lex:user-id']

How to secure event grid subscription webhook

What is the best practice to validate that webhook has been sent to my subscription endpoint by azure event grid rather than by other, possibly malicious, service or person.
When you configure webhook URL, you can put a secret token into a query parameter. Then, in your code you can validate this parameter.
For example, for Azure Function webhook, you would use code parameter:
https://myfunctionapp.azurewebsites.net/api/EventGridWebHook1?code=your_functionapp_code

Resources