I was stuck on this bugs for weeks, thanks in advance for any help and suggestions.
I am trying to
- Using aws4.sign() to sign a HTTP POST request at backend end.
- Get the policy from yaml file, and construct S3policy manually.
- Return the S3policy and signuare back to front end.
- At front end, construct a formData, append attributes of policy, then POST to S3 end point.
Here are my codes:
- Backend sign endpoints:
const policy: Policy | undefined = getPolicyByUuid(uuid);
if (!policy) {
return NextResponse.json(
{ error: `Policy for uuid ${uuid} not found` },
{ status: 400 }
);
}
const currentDate = new Date();
const amzDate = currentDate.toISOString().replace(/[:-]|.d{3}/g, "");
const dateStamp = amzDate.substr(0, 8);
const folderPath = `uploads/${policy.folderName}/`;
const filePath = `${folderPath}${fileName}`;
const s3policy = {
expiration: new Date(Date.now() + 60 * 60 * 1000).toISOString(),
conditions: [
{ bucket: bucketName },
["starts-with", "$key", folderPath],
{ acl: policy.acl },
["starts-with", "$Content-Type", fileType],
{ "x-amz-meta-uuid": "14365123651274" },
{ "x-amz-server-side-encryption": "AES256" },
["starts-with", "$x-amz-meta-tag", ""],
{
"x-amz-credential": `${accessKeyId}/${dateStamp}/${region}/s3/aws4_request`,
},
{ "x-amz-algorithm": policy.x_amz_algorithm },
{ "x-amz-date": amzDate },
],
};
const base64Policy = Buffer.from(JSON.stringify(s3policy)).toString("base64");
// const endpoint = `https://${bucketName}.s3.${region}.amazonaws.com/${filePath}`;
const endpoint = `https://${bucketName}.s3.${region}.amazonaws.com/`;
const options = {
service: "s3",
region: region,
method: "POST",
path: `/${bucketName}`,
headers: {
"Content-Type": fileType,
"x-amz-meta-uuid": "14365123651274",
"x-amz-acl": policy.acl,
"x-amz-credential": `${accessKeyId}/${dateStamp}/${region}/s3/aws4_request`,
"x-amz-date": amzDate,
Policy: base64Policy,
},
};
aws4.sign(options, {
accessKeyId,
secretAccessKey,
});
console.log("Options after sign: ", options);
// Exclude 'Host' header from the response
const signedHeaders = { ...options.headers };
delete signedHeaders.Host;
try {
return NextResponse.json(
{
url: endpoint,
signedHeaders: options.headers,
key: filePath,
acl: policy.acl,
expiration: s3policy.expiration,
"x-amz-credential": options.headers["x-amz-credential"],
amzAlgorithm: policy.x_amz_algorithm,
amzDate: amzDate,
Policy: base64Policy,
"x-amz-signature":
options.headers["Authorization"].split("Signature=")[1],
},
{ status: 200 }
);
} catch (error) {
console.log("Error generating signed URL:", error);
return NextResponse.json({ signedUrl: "error" }, { status: 500 });
}
- Then at front end, the upload codes are:
for (let i = 0; i < files.length; i++) {
const fileName = files[i].name;
const fileType = files[i].type;
console.log(`Upload ${fileName} to S3`); // Print each file name
const {
data: { url,
signedHeaders,
key,
acl,
expiration,
'x-amz-credential': amzCredential,
amzAlgorithm,
amzDate,
Policy,
'x-amz-signature': amzSignature,}
} = await axios.get(`/api/aws-sign`, {
params: { userName, organization: ORG_NAME, fileName, fileType},
});
const formData = new FormData();
formData.append('key', key);
formData.append('acl', acl);
formData.append("success_action_status", "201");
formData.append('Content-Type', fileType);
formData.append('x-amz-meta-uuid', '14365123651274');
formData.append("x-amz-server-side-encryption", "AES256");
formData.append('x-amz-credential', amzCredential);
formData.append('x-amz-algorithm', amzAlgorithm);
formData.append('x-amz-date', amzDate);
formData.append('Policy', Policy);
formData.append('x-amz-signature', amzSignature);
formData.append('file', files[i]);
const uploadResponse = await axios.post(url, formData, {
headers: {
...signedHeaders,
'Content-Type': 'multipart/form-data',
}
});
console.log('Upload response:', uploadResponse);
But I always get 400 Error codes
from S3 service ‘response: PRO FEATURE ONLY
Please help, I really don’t know what is wrong with the codes. Thanks!