AWS Polly Request from Flutter using sigv4 plugin - amazon-web-services

I am using https://pub.dev/packages/sigv4 dart package to make a request to AWS Polly. I am getting a 403 error stating {"message":"The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details."}. Here's my code,
import 'package:http/http.dart';
import 'package:sigv4/sigv4.dart';
void main() async {
final client = Sigv4Client(
keyId: <accessKEY>,
accessKey: <SecretAccessKEY>,
region: 'us-east-1',
serviceName: 'polly',
);
final request = client.request(
'https://polly.us-east-1.amazonaws.com/v1/speech',
method: 'POST',
body: jsonEncode({"OutputFormat": "mp3",
"VoiceId": "Salli",
"Text": "Hi, this is a request",
"TextType": "text"})
);
var responde = await post(request.url,headers: request.headers, body: request.body);
print(responde.body);
I had no luck using aws_polly_api as well. I am getting the error Unhandled Exception: 403 UnknownError: 403 with aws_polly_api. Here's the code I used:
import 'package:aws_polly_api/polly-2016-06-10.dart';
import 'package:http/http.dart' as http;
void main() async{
var credentials = new AwsClientCredentials(accessKey: <AccessKEY>, secretKey: <SecretAccessKEY);
var outputFormat1 = OutputFormat.mp3;
var text1 = "Hi, This is a request";
var voiceId1 = VoiceId.salli;
var textType1 = TextType.text;
var url = "https://polly.us-east-1.amazonaws.com/v1/speech";
var httpclient = new http.Client();
final service = Polly(region: 'us-east-1', credentials: credentials, client: httpclient, endpointUrl: url);
var resp = service.synthesizeSpeech(outputFormat: outputFormat1, text: text1, voiceId: voiceId1);
resp.then((value) => print(value));
}
Any help on how to get AWS Polly to work in flutter is highly appreciated. Also, if the input being sent is wrong, please help me in correcting it.
I have tried the same request with Postman to SynthesizeSpeech API and it worked...No extra headers were added except "'Content-type': 'application/json'". Just made a request with AWS signature and body in the form of raw json:
{
"OutputFormat": "mp3",
"VoiceId": "Salli",
"Text": "Hi, this is a request",
"TextType": "text"
}
It worked in postman.

Related

Set identity cookie in axios

I have setup a project with core identity and successfully register users via my endpoint but I am unable to get the claims principal to my client browser.
using postman to make a post:
https://localhost:7273/signin?username=test&password=Test123!
I can then do the following get in postman
https://localhost:7273/getuserinfo
To successfully get my userclaims from the cookie and retrieve the userinfo.
However when I do the same requests in my nuxt application with axios no cookie is ever set and my response.headers is undefined.
const res = await this.$axios.$post("https://localhost:7273/signin", {}, { params : {username : "test", password: "Test123!"}},
{withCredentials: true}).then(function(response) {
console.log({ headers: response.headers
});
});
In my backend I get signinResult = succeeded
[Route("Signin")]
[HttpPost]
public async Task<IActionResult> Signin(string username, string password)
{
var signinResult = await _signinManager.PasswordSignInAsync(username, password, true, false);
var principal = HttpContext.User as ClaimsPrincipal;
return Ok();
}
What link am I missing that is making this work in postman but not in my webapp?

add aws signature to the postman script using pm.sendRequest

I would like to use a postman pre-fetch script to refresh my app secret from an api protected by aws signature. I am able to make a basic authentication like this. However I need an aws signature authentication
var url = "https://some.endpoint"
var auth = {
type: 'basic',
basic: [
{ key: "username", value: "postman" },
{ key: "password", value: "secrets" }
]
};
var request = {
url: url,
method: "GET",
auth: auth
}
pm.sendRequest(request, function (err, res) {
const json = res.json() // Get JSON value from the response body
console.log(json)
});
hi just create a normal postman request that work properly and then copy that request to a variable by adding the below line in test script
pm.environment.set("awsrequest", pm.request)
Now you can use the awsrequest variable to send use in pm.sendRequest
pm.sendRequest(pm.environment.get("awsrequest"))

aws-sdk send request with IAM Auth

I would like to make my Lambda function comunicate with a API Gateway...
However what I want is that the API Gateway checks the request with AWS_IAM... therefore the lambda function should in some way "sign" the request with a specific IAM token i suppose...
I was reading this https://docs.aws.amazon.com/general/latest/gr/signing_aws_api_requests.html, but I'm not able to find any example on how to sign the request with a specific IAM User
(I've already created a User in IAM with AmazonAPIGatewayInvokeFullAccess and I have both access key and secret key, so I suppose I really need only the "how to sign the request")
We need to build regular nodejs request options with three additional parameters
service: "execute-api" for Api Gateway
region: "us-east-1" AWS Region.
body: postData , typically we pass body req.write, we also need it in options because it is needed for signing.
and finally the aws4.sign(...) is passed to request.
All .sign method does is adds 4 additional headers X-Amz-Content-Sha256, X-Amz-Security-Token, X-Amz-Date and Authorization
var aws4 = require("aws4");
var https = require("https");
const requestBody = { name: "test" };
var postData = JSON.stringify(requestBody);
var options = {
method: "POST",
hostname: "abcdefgh.execute-api.us-east-1.amazonaws.com",
path: "/qa",
headers: {
"Content-Type": "application/json",
},
service: "execute-api",
region: "us-east-1",
body: postData,
maxRedirects: 20,
};
const signedRequest = aws4.sign(options, {
secretAccessKey: "abcadefghijknlmnopstabcadefghijknlmnopst",
accessKeyId: "ABCDEFGHIJKLMNOPQRST",
sessionToken: "this is optional ==",
});
console.log(signedRequest);
var req = https.request(signedRequest, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function (chunk) {
var body = Buffer.concat(chunks);
console.log(body.toString());
});
res.on("error", function (error) {
console.error(error);
});
});
req.write(postData);
req.end();
since this call is made from lambda whole object with keys can be skipped and simply call aws4.sign(options), it should use from environment variables.

Google Cloud Function with Basic Auth not working properly

React Client Code - Using request promises to send username and password in Header
var password = values.password;
var email = values.email;
request
.head(
"https://us-central1-simplineet-754e8.cloudfunctions.net/CreateUserAuth"
)
.set('Content-Type', 'application/x-www-form-urlencoded')
.auth(email, password, false)
.query(dataobj)
.then(res => {
console.log(res);
if (res.statusCode === 200) {
console.log("statusText",res.body);
} else {
console.log("statusText",res.statusText);
}
})
.catch(err => {});
Backend - Google Cloud Function to Handle Basic Auth Requests from Client
const express = require('express');
const app = express();
const cors = require('cors');
app.use(cors({origin: true}));
exports.CreateUserAuth = functions.https.onRequest((request, response) => {
var corsFn = cors();
corsFn(request, response, function () {
// Request Header
response.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
response.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
response.setHeader('Access-Control-Allow-Credentials', true);
response.setHeader('Access-Control-Allow-Origin', '*');
var auth = require('basic-auth') // basic-auth NPM package to extract username and password from header
var user = auth(request)
var email = user.name; // Getting username from Auth
var password = user.pass; // Getting password from Auth
var username = request.query.username;
response.send('Hello from Firebase!'); // Not getting this response in Client
});
});
Response Getting in Client :
Response {req: Request, xhr: XMLHttpRequest, text: null, statusText: "", statusCode: 200, …}
As per MDN docs, HEAD responses should not have a body:
The HTTP HEAD method requests the headers that are returned if the specified resource would be requested with an HTTP GET method. Such a request can be done before deciding to download a large resource to save bandwidth, for example.
A response to a HEAD method should not have a body. If so, it must be ignored. Even so, entity headers describing the content of the body, like Content-Length may be included in the response. They don't relate to the body of the HEAD response, which should be empty, but to the body of similar request using the GET method would have returned as a response.
My guess is that GCP is handling it as a GET and stripping out the body before returning a response.
However, keep in mind that Google Cloud Functions HTTP trigger docs don't explicitly say that HEAD is a supported method:
You can invoke Cloud Functions with an HTTP request using the POST, PUT, GET, DELETE, and OPTIONS HTTP methods.
It looks like you are making a HEAD request instead of a POST request. Change to request.post() and it should work

Client authentication failed in Postman request for Amazon Alexa Smart Home Skill LWA

I am referring to Amazon documentation for the purpose of Customer Authentication. Currently, I am using LWA.
Steps I followed:
I enabled the Send Alexa Events Permission from the Alexa developer Console in Build > Permission page.
I took the grant code from the request in the cloudwatch logs which was sent when I logged in using Alexa companion app.
Example:-
{
"directive": {
"header": {
"messageId": "Example",
"name": "AcceptGrant",
"namespace": "Alexa.Authorization",
"payloadVersion": "3"
},
"payload": {
"grant": {
"code": "Example2",
"type": "OAuth2.AuthorizationCode"
},
"grantee": {
"token": "Example3",
"type": "BearerToken"
}
}
}
}
Permission Page under build on Alexa Developer console gave me client-Id and client-secret Which I used for making the post request to https://api.amazon.com/auth/o2/token.
Example:-
POST /auth/o2/token HTTP/l.l
Host: api.amazon.com
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
grant_type=authorization_code&code=&client_id=&client_secret=
I passed the code,client_id, and client_secret in the above example and made the post request to this URL https://api.amazon.com/auth/o2/token
I tried using x-www-form-urlencoded;charset=UTF-8 and also JSON for the Content-Type.
I followed the step given in the above documentation and I am stuck on the error ( 401 Unauthorized ):
{
"error_description": "The request has an invalid grant parameter : code",
"error": "invalid_grant"
}
I tried implementing it using Python code and Postman both. Ending up with the Same above error scenario.
Here is a sample code to help you and others who are looking to send events to alexa gateway.
const AWS = require('aws-sdk');
AWS.config.update({region: 'eu-west-1'});
// Create the DynamoDB service object
const ddb = new AWS.DynamoDB({ apiVersion: 'latest' });
const doc = new AWS.DynamoDB.DocumentClient({
convertEmptyValues: true,
service: ddb
});
// Using 'request' for http POST and GET request.
// https://www.npmjs.com/package/requests
// npm install --save requests
const r = require('request');
//Handle Authorization. Call this method from your lambda handler whenever you get Alexa.Authorization message. You will get this message only when you select permission to
//send events in your Smart Home Skill.
//Access to Event gateway allows you to enable Proactive Device Discovery and
//Proactive State Reporting in your skill
//More information on Alexa.Authorization can be found on https://developer.amazon.com/docs/device-apis/alexa-authorization.html
function handleAuthorization(request, context, user) {
//Even when you are using your own authentication, the url below will still
//point to amazon OAuth token url. The token you obtain here has to be stored
//separately for this user. Whenever sending an event to alexa event gateway you will
//require this token.
//URL below is for EU server. Look at following documentation link to identify correct url
//for your system.
//https://developer.amazon.com/docs/smarthome/send-events-to-the-alexa-event-gateway.html
var url = "https://api.amazon.com/auth/o2/token";
var body = {
grant_type : 'authorization_code',
code : request.directive.payload.grant.code,
client_id : 'your client id from permissions page on developer portal where you enable alexa events. This is id different than one you specify in account linking settings',
client_secret : 'client secret from permissions page'
}
//https://developer.amazon.com/docs/smarthome/authenticate-a-customer-permissions.html
r.post({
url: url,
form : body
}, function(error, response, b){
if (error) { return console.log(error); }
var body = JSON.parse(b);
var params = {
TableName: 'Devices',
Item: {
'id' : user,
'auth_token' : body.access_token,
'refresh_token' : body.refresh_token
}
}
log("DEBUG:", "Authorization Body", JSON.stringify(body));
log("DEBUG:", "Authorization Response", JSON.stringify(response));
log("DEBUG:", "Database Params", JSON.stringify(params));
// Call DynamoDB to add the item to the table
var putObjectPromise = doc.put(params).promise();
//Store auth_token and refresh_token in database. We will need these
//while sending events to event gateway.
//Send a success response.
putObjectPromise.then(function(data) {
var response = {
event: {
header: {
messageId: request.directive.header.messageId,
namespace: "Alexa.Authorization",
name: "AcceptGrant.Response",
payloadVersion: "3"
},
"payload": {
}
}
};
context.succeed(response);
}).catch(function(err) {
//TODO - Add a Authorization error response JSON here.
console.log(err);
});
});
}