aws cognito && apiGateway returns status 401 - amazon-web-services

I did cors configuration in response header of my api correctly.
when I tested my api via postman with a validated token
attached in header('Authorization'), it returned 200.
i checked my frontend fetch code to request that api, it seems that there is no error or fault.
how can it happen? does anyone who suffered from same as what I'm struggling now.
Added:
my front end fetch code looks like this.
export const getDoc = async (docId, token) => {
const path = `${apiGateway.URL}`;
const body = {
docId: docId
};
const headers = {
Authorization: token,
'Content-Type': 'application/json'
};
const result = await fetch(path, {
body,
headers,
});
if (result.status !== 200) {
throw new Error('failed to get doc');
}
return result.json();
};

you should just enter "Authorization" into the "Token Source" field, NOT 'method.request.headers.Authorization'. Otherwise, you will get a 401 error.

Related

Sending custom status codes from AWS Lambda

I have a Lambda#Edge function set up in AWS that checks incoming requests for a given cookie and rejects the request if it's not present, or if it is present it modifies the request for another operation. So far the code works correctly in that the request is 'rejected' if the cookie isn't present, and passes if it is present, but the response I am getting from the Lambda is a 502 which isn't really appropriate for what I'm trying to achieve. Abbreviated code below:
exports.handler = (event, context, callback) => {
const request = event.Records[0].cf.request;
const requestHeaders = request.headers;
const response = event.Records[0].cf.response;
// Perform cookie check
const downloadCookie = 'download-cookie=true';
let downloadCookieFound = false;
if (requestHeaders.cookie) {
for (let i = 0; i < requestHeaders.cookie.length; i++) {
if (requestHeaders.cookie[i].value.indexOf(downloadCookie) >= 0) {
downloadCookieFound = true;
break;
}
}
}
// Reject if cookie not present
if (!downloadCookieFound) callback(null, request);
// I have tried this as well but it doesn't seem to work
// if (!downloadCookieFound) callback(null, { statusCode: 403, body: 'Cookie missing' };
// Perform other operations
// ...
//Return modified response
callback(null, response);
};
How do I get the Lambda to return a 403 response instead of this 502:
502 ERROR
The request could not be satisfied.
The Lambda function returned invalid JSON: The JSON output must be an object type. We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.
Solved it with luk2302's aid - change the line:
if (!downloadCookieFound) callback(null, request);
To:
if (!downloadCookieFound) callback(null, { status: '403' });
Other information can be added to the returned object if desired.

Postman pre-request script for Auth, actual POST call returns Invalid Token

forgive my lack of tests and error logic. I know my pre-request script is working but for the life of me I can't figure out why the subsequent POST request fails with "Invalid token".
I'm using Postman to execute a pre-request script for auth and it's returning 200 and is giving the { id } from the res. BUT!! when I set the env variable of { token } and then call on { token } from the header of the actual POST request, I'm getting an "Invalid token" response, 401.
I checked the logs and the POST request header is matching the { token } that was returned in my pre-request script. Why would
the server reject it?
const auth = pm.environment.get('auth')
const user = btoa(`${pm.environment.get('username')}:${pm.environment.get('password')}`)
if (!auth) console.log('Missing authorization endpoint')
if (!user) console.log('Missing credentials')
if (pm.environment.get('token')) pm.environment.set('token', '')
const echoPostRequest = {
url: auth,
method: 'POST',
header: `Authorization: Basic ${user}`
};
pm.sendRequest(echoPostRequest, function (err, res) {
const { id } = res
pm.environment.set("token", id)
});
You would need to reference the access_token key to set the value in the environment variable. It looks like you're setting the whole response body as your token value.
pm.sendRequest(echoPostRequest, function (err, res) {
const { id } = res.json().access_token
pm.environment.set("token", id)
});

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

AWS Cognito TOKEN Endpoint giving a 400 Bad Request error "unauthorized_client"

Following the documentation from https://docs.aws.amazon.com/cognito/latest/developerguide/token-endpoint.html after successfully retrieving an authentication code.
As far as I can tell this is exactly how the request is supposed to be setup:
import request from 'request'
function fetchToken(code: any, clientId: string, clientSecret: string) {
try {
let tokenEndpoint = `https://example.auth.us-east-1.amazoncognito.com/oauth2/token`
const clientIdEncoded = Buffer.from(`${clientId}:${clientSecret}`).toString('base64')
request.post({
url:tokenEndpoint,
headers: {
'Content-Type':'application/x-www-form-urlencoded',
'Authorization':`Basic ${clientIdEncoded}`
},
form: {
code,
'grant_type':'authorization_code',
'client_id':clientId,
'redirect_uri':'http://localhost:3000'
}},
function(err,httpResponse,body){
console.log(httpResponse.statusCode)
//400
console.log(httpResponse.statusMessage)
//Bad Request
if(err) {
console.error(err)
}
console.log(body)
//{"error":"unauthorized_client"}
})
} catch (error) {
console.error(error)
}
}
Why would be getting unauthorized_client? Is there an easier way to debug this?
Edit: tested this in Postman with the same request and getting the same error
Headers
Body
Please check if the Cognito User Pool App is using secret key. If you have created with secret key option, that must be included in the Authorization header of the request.

CORS error with API Gateway and Lambda **only** when using Proxy Integration

I am trying to add an Item to DynamoDB upon a post request from API Gateway using Lambda.
This is what my Lambda code looks like:
var AWS = require('aws-sdk');
var dynamoDB = new AWS.DynamoDB();
exports.handler = (event, context, callback) => {
var temp_id = "1";
var temp_ts = Date.now().toString();
var temp_calc = event['params']['calc'];
var params = {
TableName:"calc-store",
Item: {
Id: {
S: temp_id
},
timestamp: {
S: temp_ts
},
calc: {
S: temp_calc
}
}
};
dynamoDB.putItem(params,callback);
const response = {
statusCode: 200,
headers: {
'content-type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
body: event['params']['calc']
};
callback(null, response);
};
This is how I am calling the function from my client
axios.post(apiURL, {params:{calc:calc}})
.then ((res) => {
console.log(res);
})
I have enabled CORS over 30 times on my API Gateway, and I've also double checked by adding headers to the response. But no matter what I do, I keep getting a CORS error and for some reason, in my response, I can see that the "Access-Control-Allow-Origin" header is not being appended.
POST https://egezysuta5.execute-api.us-east-1.amazonaws.com/TEST 502
localhost/:1 Failed to load https://egezysuta5.execute-api.us-east-
1.amazonaws.com/TEST: No 'Access-Control-Allow-Origin' header is
present on the requested resource. Origin 'http://localhost:3000' is
therefore not
allowed access. The response had HTTP status code 502.
createError.js:17 Uncaught (in promise) Error: Network Error
at createError (createError.js:17)
at XMLHttpRequest.handleError (xhr.js:87)
I tried not using Lambda Proxy Integration, and it worked then, however, I was unable to access the params I passed.
EDIT: After spending hours on this, here is what I've boiled the problem down to. My client is making a successful pre-flight request to OPTIONS. The OPTIONS is successfully returning the correct CORS headers, but for some reason, these are not being passed to my POST request!
EDIT2: (This does not solve the problem) If I change the response body to a string there is no error!! There is something wrong with
event['params]['calc']
Your problem is with the flow of the code. Basically you're not waiting for putItem to complete before callback gets executed...Try this...
dynamoDB.putItem(params,(err,data) => {
if(err){
return callback(err, null)
}
const response = {
statusCode: 200,
headers: {
'content-type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
body: JSON.parse(event.body).calc
};
return callback(null, response);
});
There are 2 issues going on here:
your code is crashing because you are probably trying to access a null property in the event object
because your code fails before you can return the full response, the proper cors headers don’t get sent back to the browser.
Always try and catch errors in your lambda code. Always log the error and return the full response with a status code of 500, in the case of an error. Also, it’s important to handle async functions, like putItem with promises. Really grasp that concept before working with JavaScript!