I've configured S3 with access only through CloudFront and protected with lambda executed on Viewer request. The problem is that I'm not able to access the files from SPA because of a failing preflight call.
When I removed the lambda function everything is starting to work. This is surprising to me because lambda is not modifying the request at all.
Here is my configuration:
CloudFront:
Lambda@Edge (executed at Viewer request)
exports.handler = async (event, context, callback) => {
let request;
let token;
try {
request = event.Records[0].cf.request;
const headers = request.headers;
const authorization = headers['authorization'][0];
const authorizationValue = authorization.value;
token = authorizationValue.substring(7);
} catch (error) {
console.error("Missing authorization header", error);
callback(null, missingAuthorizationHeaderResponse);
}
if (token) {
try {
if (!secret) {
secret = await getSecret();
}
jwt.verify(token, secret);
console.log("Token valid");
callback(null, request);
} catch (error) {
console.error("Token not valid", error);
callback(null, invalidTokenResponse);
}
} else {
console.error("Token not found");
callback(null, missingAuthorizationHeaderResponse);
}
};
I will be very grateful for help since I've spent a lot of time on this case, thanks!
CodePudding user response:
The problem is that the preflight call are executed without any additional headers and in my case "authorization" header was missing and was generating 403. I found that by looking into logs of the lambda. I've added handling of options call to the lambda. Also I had to change s3 config to have the response with visible CORS headers.
Lambda Code:
const preflightCall = {
status: "204",
headers: {
'access-control-allow-origin': [{
key: 'Access-Control-Allow-Origin',
value: "*",
}],
'access-control-request-method': [{
key: 'Access-Control-Request-Method',
value: "PUT, GET, OPTIONS, DELETE",
}],
'access-control-allow-headers': [{
key: 'Access-Control-Allow-Headers',
value: "*",
}]
},
};
exports.handler = async (event, context, callback) => {
let request;
let token;
try {
request = event.Records[0].cf.request;
if(request.method === 'OPTIONS') {
console.log('preflight call');
callback(null, preflightCall);
return;
}
const headers = request.headers;
const authorization = headers['authorization'][0];
const authorizationValue = authorization.value;
token = authorizationValue.substring(7);
} catch (error) {
console.error("Missing authorization header", error);
callback(null, missingAuthorizationHeaderResponse);
}
if (token) {
try {
if (!secret) {
secret = await getSecret();
}
jwt.verify(token, secret);
console.log("Token valid");
callback(null, request);
} catch (error) {
console.error("Token not valid", error);
callback(null, invalidTokenResponse);
}
} else {
console.error("Token not found");
callback(null, missingAuthorizationHeaderResponse);
}
};
S3:
[
{
"AllowedHeaders": [
"Authorization"
],
"AllowedMethods": [
"GET",
"HEAD",
"DELETE",
"POST",
"PUT"
],
"AllowedOrigins": [
"*"
],
"ExposeHeaders": [
"Access-Control-Allow-Origin"
],
"MaxAgeSeconds": 3000
}
]