How to send to Amazon's Alexa Event Gateway? - node.js

I am trying to test sending an event to the Amazon's Event Gateway for my Alexa Smart Home skill using Postman but I keep receiving an 'invalid access token exception.' I have read the Amazon's documentation on this but apparently I am missing something.
When I enable my skill, my Smart Home Lambda receives the AcceptGrant.
{
"directive": {
"header": {
"namespace": "Alexa.Authorization",
"name": "AcceptGrant",
"messageId": "b2862179-bc56-4bb2-ac05-ce55c7a3e977",
"payloadVersion": "3"
},
"payload": {
"grant": {
"type": "OAuth2.AuthorizationCode",
"code": "ANSVjPzpTDBsdfoRSyrs"
},
"grantee": {
"type": "BearerToken",
"token": "Atza|IwEB..."
}
}
}
}
My lambda sends a POST to 'https://api.amazon.com/auth/o2/token' to receive the Access and Refresh tokens. It then stores those tokens. Next, my Lamdba responds with the following:
{
"event": {
"header": {
"namespace": "Alexa.Authorization",
"name": "AcceptGrant.Response",
"messageId": "b2862179-bc56-4bb2-ac05-ce55c7a3e977",
"payloadVersion": "3"
},
"payload": {}
}
}
I then get a message web page that I have successfully linked my skill - all is good.
Next, I try to send an event to Amazon's Alexa event gateway using the Postman app. I put the Access token (I also tried the Refresh token) in the header as a 'BearerToken' type and the in the 'scope' of the 'endpoint' object.
POST https://api.amazonalexa.com/v3/events?Content-Type=application/json&charset=UTF-8
with a header that specifies a Bearer Token (Access token received earlier) and a body that contains the following:
{
"event": {
"header": {
"messageId": "abc-123-def-456",
"namespace": "Alexa",
"name": "ChangeReport",
"payloadVersion": "3"
},
"endpoint": {
"scope": {
"type": "BearerToken",
"token": "<access token>"
},
"endpointId": "MySmartSwitch-001"
},
"payload": {
"change": {
"cause": {
"type": "RULE_TRIGGER"
},
"properties": [
{
"namespace": "Alexa.ModeController",
"name": "mode",
"value": "Backup",
"timeOfSample": "2020-01-02T09:30:00ZZ",
"uncertaintyInMilliseconds": 50
}
]
}
}
},
"context": {
"properties": [
{
"namespace": "Alexa.PowerController",
"name": "powerState",
"value": "ON",
"timeOfSample": "2020-01-02T09:30:00Z",
"uncertaintyInMilliseconds": 60000
},
{
"namespace": "Alexa.EndpointHealth",
"name": "connectivity",
"value": {
"value": "OK"
},
"timeOfSample": "2020-01-02T09:30:00Z",
"uncertaintyInMilliseconds": 0
}
]
}
}
The response received is '401 Unauthorized'
{
"header": {
"namespace": "System",
"name": "Exception",
"messageId": "95bd23c3-76e6-472b-9c6d-74d436e1eb61"
},
"payload": {
"code": "INVALID_ACCESS_TOKEN_EXCEPTION",
"description": "Access token is not valid."
}
}

I figured out the issue. I was mistakenly sending parameters: Content-Type=application/json and charset=UTF-8 as well including them in the header - my bad. You just need to include them in the header.

Related

Multiple authorization using swagger OpenAPI 3.0 with Express and JSON format

The project has two authorization systems, basic auth and bearer. I need for each request after clicking on the "try it out" and "execute" buttons to attach to the request the Authorization headers, in which there will be a Basic line and a jwt header, in which there will be a bearer token. The problem is that I can attach these headers individually, but not together. There is a feeling that both authorizations want to write to the Authorization header and one of them overwrites the other, even though I explicitly indicated the header names in the schema.
My schemas:
{
"securitySchemes": {
"Bearer": {
"in": "header",
"name": "jwt",
"type": "http",
"scheme": "bearer"
},
"basicAuth": {
"type": "http",
"scheme": "basic"
}
}
}
and how I use it:
{
"/channel/base-list": {
"get": {
"tags": [
"CMS Channel"
],
"security": [
{
"Bearer": [],
"basicAuth": []
}
],
"summary": "Get _id and title of all channels",
"produces": [
"application/json"
],
"parameters": [
{
"in": "query",
"name": "count",
"required": false,
"schema": {
"type": "Integer"
},
"default": 25,
"example": 10
},
{
"in": "query",
"name": "search",
"required": false,
"schema": {
"type": "String"
},
"description": "Channel name"
}
],
"responses": {
"200": {
"description": "A list of channels",
"content": {
"application/json": {
"schema": {
"$ref": "#/definitions/get-channel-base-list"
}
}
}
}
}
}
}
}
I use swagger-ui-express for node.JS and OpenAPI 3.0
A request can contain only one Authorization header, and the Authorization header can only contain a single set of credentials (i.e. either Basic or Bearer, but not both). Your use case is not supported by the HTTP protocol.

Malformed Response: Failed to parse Dialogflow response into AppResponse because of empty speech response

I am using firebase function for the webhook fulfillment in Dialogflow. I am getting webhook successful as a fulfillment status but it is not working. I am using version 1. When I test it on Google Assistant simulator, it says "App is not responding".
firebase function
const functions = require('firebase-functions');
exports.webhook = functions.https.onRequest((request, response) => {
response.send({
"google":{
"richResponse":{
"items":[
{
"simpleResponse":{
"textToSpeech":"Hey! Good to see you."
}
},
{
"mediaResponse":{
"mediaType":"AUDIO",
"mediaObjects":[
{
"name":"Exercises",
"description":"ex",
"largeImage":{
"url":"http://res.freestockphotos.biz/pictures/17/17903-balloons-pv.jpg",
"accessibilityText":"..."
},
"contentUrl":"https://theislam360.me:8080/hbd.mp3"
}
]
}
}
],
"suggestions":[
{
"title":"chips"
}
]
}
}
}
)
});`
When I copy paste the response from {google... to the end in the custom payload manually via GUI, It works. While for webhook, it is not working.
RAW API RESPONSE
{
"id": "eaf627ed-26b5-4965-b0b0-bc77144e144b",
"timestamp": "2019-04-15T11:54:18.948Z",
"lang": "en",
"result": {
"source": "agent",
"resolvedQuery": "play hbd",
"action": "",
"actionIncomplete": false,
"parameters": {
"any": "hbd"
},
"contexts": [],
"metadata": {
"isFallbackIntent": "false",
"webhookResponseTime": 34,
"intentName": "play",
"intentId": "e60071cd-ce31-4ef9-ae9b-cc370c3362b3",
"webhookUsed": "true",
"webhookForSlotFillingUsed": "false"
},
"fulfillment": {
"messages": []
},
"score": 1
},
"status": {
"code": 200,
"errorType": "success"
},
"sessionId": "e91bd62f-766b-b19d-d37b-2917ac20caa6"
}
FULFILLMENT REQUEST
{
"id": "eaf627ed-26b5-4965-b0b0-bc77144e144b",
"timestamp": "2019-04-15T11:54:18.948Z",
"lang": "en",
"result": {
"source": "agent",
"resolvedQuery": "play hbd",
"speech": "",
"action": "",
"actionIncomplete": false,
"parameters": {
"any": "hbd"
},
"contexts": [],
"metadata": {
"intentId": "e60071cd-ce31-4ef9-ae9b-cc370c3362b3",
"webhookUsed": "true",
"webhookForSlotFillingUsed": "false",
"isFallbackIntent": "false",
"intentName": "play"
},
"fulfillment": {
"speech": "",
"messages": []
},
"score": 1
},
"status": {
"code": 200,
"errorType": "success"
},
"sessionId": "e91bd62f-766b-b19d-d37b-2917ac20caa6"
}
FULFILLMENT RESPONSE
{
"google": {
"richResponse": {
"items": [
{
"simpleResponse": {
"textToSpeech": "Hey! Good to see you."
}
},
{
"mediaResponse": {
"mediaType": "AUDIO",
"mediaObjects": [
{
"name": "Exercises",
"description": "ex",
"largeImage": {
"url": "http://res.freestockphotos.biz/pictures/17/17903-balloons-pv.jpg",
"accessibilityText": "..."
},
"contentUrl": "https://theislam360.me:8080/hbd.mp3"
}
]
}
}
],
"suggestions": [
{
"title": "chips"
}
]
}
}
}
FULFILLMENT STATUS
Webhook execution successful
Firebase Logs
Google Assistant Simulator Logs
You're not using the correct JSON in the response. By putting it in the GUI in the "custom payload" section, it is creating a larger JSON response for you. The google object needs to be under the data object for Dialogflow v1 or payload for Dialogflow v2. (And if you haven't switched to v2 - you should do so immediately, since v1 will be switched off in about a month.)
So what you're returning should look more like
{
"payload": {
"google": {
...
}
}
}

Missing Subscription Key field in Swagger API connector (trough Azure API Management) in Logic App

I have created a REST API with a Swagger/OPEN API specification which I will like to consume trough a Azure API Management tenant in a Logic App.
When I download the specification it looks like this:
{
"swagger": "2.0",
"info": {
"title": "Leasing",
"version": "1.0"
},
"host": "ENDPOINT.azure-api.net",
"basePath": "/leasing",
"schemes": [
"http",
"https"
],
"securityDefinitions": {
"apiKeyHeader": {
"type": "apiKey",
"name": "Ocp-Apim-Subscription-Key",
"in": "header"
},
"apiKeyQuery": {
"type": "apiKey",
"name": "subscription-key",
"in": "query"
}
},
"security": [
{
"apiKeyHeader": []
},
{
"apiKeyQuery": []
}
],
"paths": {
"/{Brand}/groups": {
"get": {
"description": "Get a list of leasing groups on a brand",
"operationId": "GetGroups",
"parameters": [
{
"name": "Brand",
"in": "path",
"description": "Selection of possible brands",
"required": true,
"type": "string",
"enum": [
"Volkswagen",
"Audi",
"Seat",
"Skoda",
"VolkswagenErhverv",
"Porsche",
"Ducati"
]
}
],
"responses": {
"200": {
"description": "Returns a list of leasing groups",
"schema": {
"$ref": "#/definitions/GroupArray"
}
},
"400": {
"description": "If the brand is not valid",
"schema": {
"$ref": "#/definitions/Error"
}
}
},
"produces": [
"application/json"
]
}
}
},
"definitions": {
"Group": {
"type": "object",
"properties": {
"id": {
"format": "int32",
"type": "integer"
},
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"leasingModelCount": {
"format": "int32",
"type": "integer"
},
"lowestMonthlyFee": {
"format": "int32",
"type": "integer"
}
}
},
"Error": {
"type": "object",
"properties": {
"code": {
"enum": [
"NotValidBrand",
"NotValidGroupId"
],
"type": "string",
"x-ms-enum": {
"name": "ErrorCode",
"modelAsString": true
}
},
"message": {
"type": "string"
}
}
},
"GroupArray": {
"type": "array",
"items": {
"$ref": "#/definitions/Group"
}
}
}
}
When I add this in a Logic App with the connector HTTP + Swagger I only get to define the {Brand} query input but not the various ways of using the Subscriptions key (header or query) as defined in SecurityDefiniations.
The whole securityDefinitions and security section are automatically generated in the Azure API Management service, but not recognized in Logic App.
See image of missing subscription key field:
What am I doing wrong?
Update
I have tried the following:
Usage of the 'Authentication' field (but this field is limited to certain types of auths flows https://learn.microsoft.com/en-us/azure/connectors/connectors-native-http#authentication)
Change the Logic App 'Http + Swagger'-action in code to add the header parameter, but this action converts the action to a simple 'Http' action and therfore loosing the automatic schema generation from Swagger.
I think you need to specify this in the Authentication-field in a JSON format. Something like:
{
"apiKeyHeader" : "your Ocp-Apim-Subscription-Key",
"apiKeyQuery" : "your subscription key"
}

OpenAM /json/authenticate ReST api behaves differently on different servers

I am calling the OpenAM /json/authenticate api for a realm. The request details are as below. The issue is on one server the response received contains the tokenId directly and on other it gives the authId with callbacks. Both servers are exactly same with same configuration. Any help in fixing the output to get the tokenId directly would be a great help. There is limited help available on OpenAM. This has given me a couple of sleepless nights.
Method: POST
URL: http://localhost:8080/openam/json/authenticate?realm=/MYREALM
Headers:
1. Content-Type:application/json
2. X-OpenAM-Password:Wsdwe#9900
3. X-OpenAM-Username:sdf.er#dummy.com
Output on one server
{
"tokenId": "AQIC5wM2LY4SfcwB_yB8SZ5JOfJSzlbBCIb0645ky5NrwUY.*AAJTSQACMDMAAlNLADASMjExOTAwMjczNDI5Mjg0Mjc3MgACUzEAAjAy*",
"successUrl": "/openam/console"
}
Output on other server
{
"authId": "eyAidHlwIjogIkpXVCIsGHJhbGciOiAiSFMyNTYiIH0.eyAib3RrIjogImo1Y2gybnFqaGwybGhnZ250cWIzazNkMHFpIiwgInJlYWxtIjogIm89aXRzdXBwb3J0MjQ3ZGF0YXN0b3JlLG91PXNlcnZpY2VzLGRjPW9wZW5hbSxkYz1mb3JnAWEvY2ssZGM9b3JnIiwgInNlc3Npb25JZCI6ICJBUUlDNXdNMkxZNFNmY3h0UUDSFGlielFPYkJuUVUxTTNudUlab3JFdEcxX2liNC4qQUFKVFNRQUNNREVBQWxOTEFCTTRPRGcxTkRRME16STFNRFl3TXpnNE5EUTRBQUpUTVFBQSoiIH0.rWE7A_tjcsyiXpQdAXUsS6OiHi97HhXQEkYwY919ovE",
"template": "",
"stage": "IDMDataStore1",
"header": "Access Your Account",
"callbacks": [
{
"type": "NameCallback",
"output": [
{
"name": "prompt",
"value": "Email ID:"
}
],
"input": [
{
"name": "IDToken1",
"value": "sdf.er#dummy.com"
}
]
},
{
"type": "PasswordCallback",
"output": [
{
"name": "prompt",
"value": "Password:"
}
],
"input": [
{
"name": "IDToken2",
"value": ""
}
]
},
{
"type": "TextOutputCallback",
"output": [
{
"name": "message",
"value": "\n "
},
{
"name": "messageType",
"value": "0"
}
]
}
]
}
NOTE: I am using OpenAM 13.0.0

Can AWS Cognito adminCreateUser send a temporary password?

The Amazon Cognito adminCreateUser documentation on TemporaryPassword states:
This parameter is not required. If you do not specify a value, Amazon
Cognito generates one for you.
How does the user ever get it? At first I thought it would get emailed to the user, but that doesn't seem to be the case. Then I thought maybe it would come back in the response. Nope.
Here's the code I'm calling in my node JS Lambda function:
adminCreateUser(
{
"UserPoolId": "us-east-1_XXXXXXXX",
"Username": "roger__mailinator.com",
"DesiredDeliveryMediums": [
"EMAIL"
],
"ForceAliasCreation": false,
"MessageAction": "SUPPRESS",
"UserAttributes": [
{
"Name": "given_name",
"Value": "Rodger"
},
{
"Name": "family_name",
"Value": "Ribbit"
},
{
"Name": "name",
"Value": "Rodger Ribbit"
},
{
"Name": "email",
"Value": "roger#mailinator.com"
},
{
"Name": "custom:title",
"Value": "Animation Designer"
},
{
"Name": "custom:company",
"Value": "76"
}
]
}, function(error, data) {
if (error) {
console.log("Error adding user to cognito: " + error, error.stack);
//...
} else {
console.log("Received back from cognito: " + JSON.stringify(data));
//...
}
});
and here's the response I get:
Received back from cognito:
{
"User": {
"Username": "roger__mailinator.com",
"Attributes": [
{
"Name": "custom:title",
"Value": "Animation Designer"
},
{
"Name": "sub",
"Value": "1cd612a0-0da0-4e7b-84c7-30570fab80a9"
},
{
"Name": "name",
"Value": "Rodger Ribbit"
},
{
"Name": "given_name",
"Value": "Rodger"
},
{
"Name": "family_name",
"Value": "Ribbit"
},
{
"Name": "email",
"Value": "roger#mailinator.com"
},
{
"Name": "custom:company",
"Value": "76"
}
],
"UserCreateDate": "2017-03-30T18:31:34.283Z",
"UserLastModifiedDate": "2017-03-30T18:31:34.283Z",
"Enabled": true,
"UserStatus": "FORCE_CHANGE_PASSWORD"
}
}
Where does the password go? Are we supposed to guess it? :-)
I think it's because you are passing "MessageAction": "SUPPRESS". That would suppress the sending of the email.
The temporary password is emailed to the user and he needs to reset it upon the first login.
The answer is that the user email needs to be verified before they'll receive it. So you need to modify the user attribute on email to be verified. See: https://stackoverflow.com/a/43033722/491553

Resources