I tried to automate my postman test. In first test I should get access token to execute the API call.
So I write pre test below. But I am getting 'There was an error in evaluating the Pre-request Script:Error: No data, empty input at 1:1 ^'. I tried commenting last section but it's not worked.
How should I automate this ?
const tokenUrl = 'https://xxxxxxxxxxxx/token';
const clientId = 'xxxxxxxxxxxxxxxx';
const clientSecret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
const getTokenRequest = {
method: 'POST',
url: tokenUrl,
body: {
mode: 'formdata',
formdata: [
{ key: 'grant_type', value: 'Client Credentials' },
{ key: 'client_id', value: clientId },
{ key: 'client_secret', value: clientSecret }
]
}
};
pm.sendRequest(getTokenRequest, (err, response) => {
const jsonResponse = response.json();
const newAccessToken = jsonResponse.access_token;
pm.variables.set('access_token', newAccessToken);
});
Related
This has me stumped.
I have a cognito pool with a resource server, one custom scope and one app client (clientid and secret).
I have my (pre-request) script set up in Postman like this
const clientId = pm.environment.get("cognitoClientId");
const clientSecret = pm.environment.get("cognitoClientSecret");
const grantType = "client_credentials";
const tokenUrl = pm.environment.get("cognitoAuthTokenURL");
const postRequest = {
url: tokenUrl,
method: 'POST',
header: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: {
mode: 'urlencoded',
urlencoded: [
{
key: "grant_type",
value: grantType
},
{
key: "client_id",
value: clientId
},
{
key: "client_secret",
value: clientSecret
},
{
key: "scope",
value: "myResourceServer/myResource.list"
}
]
}
};
pm.sendRequest(
postRequest
,(error, response) => {
console.log("Callback fired");
console.log(error);
console.log(response);
}
);
It fails on the response body with the following error:
{"error":"invalid_grant"}
From the AWS Documentation I get
Refresh token has been revoked
Which makes no sense as I am not requesting a refresh token
What am I doing wrong here, or where should I be looking?
I am following this guide for signing HTTP requests to an Amazon OpenSearch Service using Node.js (version 3 of the AWS SDK for JavaScript).
When I copy the exact sample code and export my AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY of my authorised user, the PUT index/type/id request to add an item is successful:
201 Created
Response body: {"_index":"products","_type":"_doc","_id":"2","_version":1,"result":"created","_shards":{"total":2,"successful":2,"failed":0},"_seq_no":0,"_primary_term":1}
However, when I change the request to instead GET /_search endpoint, I get:
403 Forbidden
Response body: {"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."}
The user is fully authorised against the index:
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::**:user/aws-elasticbeanstalk-ec2-user"
},
"Action": "es:*",
"Resource": "arn:aws:es:ap-southeast-2:**:domain/mydomain/*"
},
How do I rectify my signature?
Here is my modified code from the above link:
const { HttpRequest } = require('#aws-sdk/protocol-http')
const { defaultProvider } = require('#aws-sdk/credential-provider-node')
const { SignatureV4 } = require('#aws-sdk/signature-v4')
const { NodeHttpHandler } = require('#aws-sdk/node-http-handler')
const { Sha256 } = require('#aws-crypto/sha256-browser')
const region = ''
const domain = ''
const index = 'products'
const type = '_search'
const createBody = (query) => ({
query: {
multi_match: {
query,
type: 'phrase',
fields: [
'tags',
'name',
'category',
'maker'
]
}
},
highlight: {
pre_tags: [''],
post_tags: [''],
fields: {
tags: {},
name: {},
category: {},
maker: {}
}
}
})
searchIndex('sh').then(() => process.exit())
async function searchIndex (query) {
const request = new HttpRequest({
body: JSON.stringify(createBody(query)),
headers: {
'Content-Type': 'application/json',
host: domain
},
hostname: domain,
method: 'GET',
path: index + '/' + type
})
const signer = new SignatureV4({
credentials: defaultProvider(),
region: region,
service: 'es',
sha256: Sha256
})
const signedRequest = await signer.sign(request)
const client = new NodeHttpHandler()
const { response } = await client.handle(signedRequest)
console.log(response.statusCode + ' ' + response.body.statusMessage)
let responseBody = ''
return new Promise((resolve) => {
response.body.on('data', (chunk) => {
responseBody += chunk
})
response.body.on('end', () => {
console.log('Response body: ' + responseBody)
resolve(responseBody)
})
}, (error) => {
console.log('Error: ' + error)
})
}
i also had this issue, using the same tutorial
reading the docs on request body searches, i found it states the following:
Note The _search API accepts HTTP GET and POST for request body
searches, but not all HTTP clients support adding a request body to a
GET request. POST is the more universal choice.
changing my method to POST solved the issue for me
I am working on a custom auth solution, I backend is on NODE (lambda) and calling that lambda by API gateway (it's a post-call).
For every thin is working fine if I use no-Proxy APIs, but in my case in need to pass custom additional headers. And when I tried with prox it is not responding. (it look like Cognito async call takes time or response is not resolving)
I am not sure I am missing some configuration or something wrong with code (code is working fine individual lambda and with API without Proxy).
here is my Lambda code.
// const AWS = require("aws-sdk");
// var crypto = require("crypto-js");
var CryptoJS = require("crypto-js");
var Crypto = require("crypto");
const { CognitoIdentityServiceProvider } = require("aws-sdk");
const hashSecret = (clientSecret, username, clientId) =>
Crypto.createHmac("SHA256", clientSecret)
.update(username + clientId)
.digest("base64");
async function isUserValid() {
const USER_POOL_ID = "poolid";
const CLIENT_ID = "clidntID";
const CLIENT_SECRET = "secreteCode";
const Password = "userName";
const Username = "password";
const cognito = new CognitoIdentityServiceProvider({
region: "ap-southeast-2",
});
try {
const payload = {
UserPoolId: USER_POOL_ID,
AuthFlow: "ADMIN_NO_SRP_AUTH",
ClientId: CLIENT_ID,
AuthParameters: {
USERNAME: "username",
PASSWORD: "password",
SECRET_HASH: hashSecret(CLIENT_SECRET, Username, CLIENT_ID),
},
};
const response = await cognito.adminInitiateAuth(payload).promise();
// // console.log("respone :", response)
// console.log("before response node js lambda ::: ")
return response;
} catch (e) {
console.log("error : ", e.message);
}
}
async function test() {
return "test ";
}
exports.handler = async (event) => {
// TODO implement
console.log("event : ", event);
'
const response = {
statusCode: 200,
headers: {
"Content-Type" : "application/json",
"Access-Control-Allow-Origin" : "*",
"X-Content-Type-Option" : "nosniff",
"Content-Security-Policy" : "default-src self",
"X-frame-options" : "DENY",
"Cache-Control" : "max-age=86400",
"X-XSS-protection" : 0,
"X-rate-limit": 600,
"Strict-Transport-Security": "max-age=31536000; includeSubDomains",
},
// body: await test() // (this response is working fine
body: await isUserValid(),
};
return response;
};
Errors when tested from API gateway with proxy setting on :
{
"message": "Internal server error"
}
Response Headers
{"x-amzn-ErrorType":"InternalServerErrorException"}
I tried multiple options but nothing is working for me.
The 'body' property must be a JSON string.
await cognito.adminInitiateAuth(payload).promise() is returning an object. You need to JSON.stringify() that response. Be sure to take care of the error case , too.
Hello i use Electron JS for a desktop app which is related to a cloud plateform from which in needto get a list of Patients.
As far as now i can get it but with a static AccessToken. I really struggled to get it dynamic, please help.
Here is my code :
This is my configuration file where i specify Cognito Parameters :
export default {
s3: {
REGION: 'YOUR_S3_UPLOADS_BUCKET_REGION',
BUCKET: 'YOUR_S3_UPLOADS_BUCKET_NAME',
},
apiGateway: {
REGION: 'YOUR_API_GATEWAY_REGION',
URL: 'YOUR_API_GATEWAY_URL',
},
cognito: {
REGION: 'eu-west-1',
USER_POOL_ID: 'eu-west-1_P0Jcr7nig',
APP_CLIENT_ID: '4m1utu56hjm835dshts9jg63ou',
IDENTITY_POOL_ID: 'YOUR_IDENTITY_POOL_ID',
authenticationFlowType: 'USER_PASSWORD_AUTH',
AUTHENTICATION_FLOW_TYPE: 'USER_PASSWORD_AUTH',
},
API: {
endpoints: [
{
name: 'PatientsList',
endpoint: 'https://uo992r7huf.execute-api.eu-west-1.amazonaws.com/Stage/patients',
//endpoint: 'https://uo992r7huf.execute-api.eu-west-1.amazonaws.com/Stage',
},
],
},
};
Auth.signIn({
username: process.env.username,
password: process.env.password,
}).then().catch(err => {
console.log(err)});
In another file this is my getaccesstoken function which i export to the main
function getAccessToken() {
const poolData = {
UserPoolId : COGNITO_USER_POOL_ID,
ClientId : COGNITO_CLIENT_ID,
};
const userPool = new CognitoUserPool(poolData);
var authenticationData = {
Username : process.env.username, // your username here
Password : process.env.password, // your password here,
authenticationFlowType: process.env.AUTHENTICATION_FLOW_TYPE,
Pool : userPool
};
var authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(
authenticationData);
var cognitoUser = new CognitoUser(authenticationData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
console.log('access token + ' + result.getAccessToken().getJwtToken());
},
onFailure: function(err) {
console.log(err);
},
});
}
And finally here is how i get the data in main :
The declarations :
const { Auth } = require('./cognitoAuth');
const theAccessToken = require('./cognitoAuth');
The code :
//Get Data From Cloud ECS
const API_URL = 'https://uo992r7huf.execute-api.eu-west-1.amazonaws.com/Stage/patients';
const headers = {
"Content-Type": "application/json",
//Authorization: theAccessToken.getAccessToken()
Authorization: "eyJraWQiOiJBbE1DZnBCTHYyVUlNazhXSG4xaTk4RG1QNlFmcFpSSjFaSW1qcVVFZnVBPSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiI4OWYyZGMxZi1iMTI3LTQzM2QtODJhYS1iMjNkNWJhNzY5NGEiLCJjb2duaXRvOmdyb3VwcyI6WyJkb2N0b3IiXSwiZXZlbnRfaWQiOiI1OTM0ZmIwNC0yYTUzLTQ2NmQtYTU1Ni0zNTM3M2RhZmU1Y2UiLCJ0b2tlbl91c2UiOiJhY2Nlc3MiLCJzY29wZSI6ImF3cy5jb2duaXRvLnNpZ25pbi51c2VyLmFkbWluIiwiYXV0aF90aW1lIjoxNTk1NDI2NjQ2LCJpc3MiOiJodHRwczpcL1wvY29nbml0by1pZHAuZXUtd2VzdC0xLmFtYXpvbmF3cy5jb21cL2V1LXdlc3QtMV9QMEpjcjduaWciLCJleHAiOjE1OTcxNTUxMDUsImlhdCI6MTU5NzE1MTUwNSwianRpIjoiNGRkN2U5ZGUtYmQ2YS00NTg4LWIzZDAtMTVjMWM1NWQxY2Y2IiwiY2xpZW50X2lkIjoiNG0xdXR1NTZoam04MzVkc2h0czlqZzYzb3UiLCJ1c2VybmFtZSI6Ijg5ZjJkYzFmLWIxMjctNDMzZC04MmFhLWIyM2Q1YmE3Njk0YSJ9.LYvzPRBxvKw2P3gHwV8NhYPg_EB3F7ZK2F5HpRWHtBHksr6D4N5Fw56ZVupkRCxVJSq0f93DdljI7BBcBnp9d_hpLzmJLTfBhA3t870omTxalTqpGXN_SvsZmuwcfCX-awn1Um6x_-fhq3zcfPkB9FBljbtwPN-kvCc-Iynei9anVxXI686nIbkfbYfnuRnHrbY0vg8FtyDIDBMv277FPoJ96NwPD4mJvNBxQHi_KfWxQ1DmLiAC6c_l2jP_wawAPBv788CjD_8OlKBbjAHinGEkaL1K9vjI5MhNPyTA5ym1IaWar7Jr8RkUDzQGvqEUPKoOUe9PswmOOxLBjehMgQ"
};
//console.log('Token Value:', theAccessToken.getAccessToken());
const getPatients = async(API_URL) => {
try {
//get data from cloud specifiying get method and headers which contain token
const response = await fetch(API_URL,{
method: 'GET', headers: headers}
);
var listPatients = await response.json();
listPatients.items.forEach(patient => {
//Checking what i got
console.log(patient);
});
} catch(err) {
console.log("Error" + err);
}
};
getPatients(API_URL);
Now when i make it dynamic by specifying theAccessToken.getAccessToken
I get this error, USER_SRP is not enabled even if specify it, when i asked team told me the cloud service doesn't want to enable it.
So how can i get this access token please?
For a desktop app it is recommended to do these 2 things, according to security guidance:
Use Authorization Code Flow (PKCE)
Login via the system browser, so that the app never sees the user's password
I have a couple of Electron code samples that use Cognito, which you can easily run - maybe start here:
First desktop app
In a Lambda, I would like to sign my AppSync endpoint with aws-signature-v4 in order to use it for a mutation.
The URL generated seems to be ok but it gives me the following error when I try it:
{
"errors" : [ {
"errorType" : "InvalidSignatureException",
"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. etc...
} ]
}
Here is my lambda function
import { Context, Callback } from 'aws-lambda';
import { GraphQLClient } from 'graphql-request';
const v4 = require('aws-signature-v4');
export async function handle(event: any, context: Context, callback: Callback) {
context.callbackWaitsForEmptyEventLoop = false;
const url = v4.createPresignedURL(
'POST',
'xxxxxxxxxxxxxxxxx.appsync-api.eu-west-1.amazonaws.com',
'/graphql',
'appsync',
'UNSIGNED-PAYLOAD',
{
key: 'yyyyyyyyyyyyyyyyyyyy',
secret: 'zzzzzzzzzzzzzzzzzzzzz',
region: 'eu-west-1'
}
);
const mutation = `{
FAKEviewProduct(title: "Inception") {
productId
}
}`;
const client = new GraphQLClient(url, {
headers: {
'Content-Type': 'application/graphql',
action: 'GetDataSource',
version: '2017-07-25'
}
});
try {
await client.request(mutation, { productId: 'jfsjfksldjfsdkjfsl' });
} catch (err) {
console.log(err);
callback(Error());
}
callback(null, {});
}
I got my key and secret by creating a new user and Allowing him appsync:GraphQL action.
What am I doing wrong?
This is how I trigger an AppSync mutation using by making a simple HTTP-request, using axios.
const AWS = require('aws-sdk');
const axios = require('axios');
exports.handler = async (event) => {
let result.data = await updateDb(event);
return result.data;
};
function updateDb({ owner, thingName, key }){
let req = new AWS.HttpRequest('https://xxxxxxxxxxx.appsync-api.eu-central-1.amazonaws.com/graphql', 'eu-central-1');
req.method = 'POST';
req.headers.host = 'xxxxxxxxxxx.appsync-api.eu-central-1.amazonaws.com';
req.headers['Content-Type'] = 'multipart/form-data';
req.body = JSON.stringify({
"query":"mutation ($input: UpdateUsersCamsInput!) { updateUsersCams(input: $input){ latestImage uid name } }",
"variables": {
"input": {
"uid": owner,
"name": thingName,
"latestImage": key
}
}
});
let signer = new AWS.Signers.V4(req, 'appsync', true);
signer.addAuthorization(AWS.config.credentials, AWS.util.date.getDate());
return axios({
method: 'post',
url: 'https://xxxxxxxxxxx.appsync-api.eu-central-1.amazonaws.com/graphql',
data: req.body,
headers: req.headers
});
}
Make sure to give the IAM-role your Lambda function is running as, permissions for appsync:GraphQL.
Adding an answer here because I had difficulty getting the accepted answer to work and I found an issue on the AWS SDK GitHub issues that said it's not recommended to use the AWS.Signers.V4 object in production. This is how I got it to work using the popular aws4 npm module that is recommended later on in the issue linked above.
const axios = require('axios');
const aws4 = require('aws4');
const query = `
query Query {
todos {
id,
title
}
}`
const sigOptions = {
method: 'POST',
host: 'xxxxxxxxxx.appsync-api.eu-west.amazonaws.com',
region: 'eu-west-1',
path: 'graphql',
body: JSON.stringify({
query
}),
service: 'appsync'
};
const creds = {
// AWS access tokens
}
axios({
url: 'https://xxxxxxxxxx.appsync-api.eu-west/graphql',
method: 'post',
headers: aws4.sign(sigOptions, creds).headers,
data: {
query
}
}).then(res => res.data))
You don't need to construct a pre-signed URL to call an AWS AppSync endpoint. Set the authentication mode on the AppSync endpoint to AWS_IAM, grant permissions to your Lambda execution role, and then follow the steps in the "Building a JavaScript Client" tutorial to invoke a mutation or query.