405 Error with S3 Presigned URL's

I currently have a 'PDF viewer' on my website which is suppose to render a PDF from my s3 bucket and prevent the user from being able to download, print, etc the pdf. Here's the code for the /exams/page.tsx file (the PDF viewer)
"use client";
import React, { useState, useEffect } from "react";

const PDFViewer = (presignedUrl: RequestInfo | URL) => {
const [pdfUrl, setPdfUrl] = useState("");

useEffect(() => {
const fetchPdf = async () => {
try {
const objectKey = "biology/Photosynthesis 1.pdf";
const response = await fetch(`/api/exams?objectKey=${objectKey}`);
const pdfObjectUrl = URL.createObjectURL(await response.blob());
setPdfUrl(pdfObjectUrl);
} catch (error) {
console.error("Error fetching PDF:", error);
}
};

fetchPdf();

return () => {
if (pdfUrl) {
URL.revokeObjectURL(pdfUrl);
}
};
}, []);

const handleContextMenu = (
event: React.MouseEvent<HTMLDivElement, MouseEvent>
) => {
event.preventDefault();
};

const handleMouseDown = (
event: React.MouseEvent<HTMLDivElement, MouseEvent>
) => {
if (event.button === 2) {
event.preventDefault();
}
};

return (
<div
className="fixed inset-0 flex items-center justify-center"
onContextMenu={handleContextMenu}
onMouseDown={handleMouseDown}
>
<div className="absolute inset-0 bg-gray-900 opacity-75 pointer-events-none" />
<div className="relative w-full h-full flex items-center justify-center">
{pdfUrl ? (
<embed
src={`${pdfUrl}#toolbar=0`}
type="application/pdf"
className="w-full h-full"
onContextMenu={handleContextMenu}
/>
) : (
<p>Loading PDF...</p>
)}
</div>
</div>
);
};

export default PDFViewer;
"use client";
import React, { useState, useEffect } from "react";

const PDFViewer = (presignedUrl: RequestInfo | URL) => {
const [pdfUrl, setPdfUrl] = useState("");

useEffect(() => {
const fetchPdf = async () => {
try {
const objectKey = "biology/Photosynthesis 1.pdf";
const response = await fetch(`/api/exams?objectKey=${objectKey}`);
const pdfObjectUrl = URL.createObjectURL(await response.blob());
setPdfUrl(pdfObjectUrl);
} catch (error) {
console.error("Error fetching PDF:", error);
}
};

fetchPdf();

return () => {
if (pdfUrl) {
URL.revokeObjectURL(pdfUrl);
}
};
}, []);

const handleContextMenu = (
event: React.MouseEvent<HTMLDivElement, MouseEvent>
) => {
event.preventDefault();
};

const handleMouseDown = (
event: React.MouseEvent<HTMLDivElement, MouseEvent>
) => {
if (event.button === 2) {
event.preventDefault();
}
};

return (
<div
className="fixed inset-0 flex items-center justify-center"
onContextMenu={handleContextMenu}
onMouseDown={handleMouseDown}
>
<div className="absolute inset-0 bg-gray-900 opacity-75 pointer-events-none" />
<div className="relative w-full h-full flex items-center justify-center">
{pdfUrl ? (
<embed
src={`${pdfUrl}#toolbar=0`}
type="application/pdf"
className="w-full h-full"
onContextMenu={handleContextMenu}
/>
) : (
<p>Loading PDF...</p>
)}
</div>
</div>
);
};

export default PDFViewer;
(continued because im running out of characters)
W
wlvz329d ago
I also have an API in /api/exams with a function to get a presigned URL from S3 /api/exams/route.ts
const AWS = require('aws-sdk');

// Configure AWS credentials and region
AWS.config.update({
accessKeyId: 'removed',
secretAccessKey: 'removed',
region: 'me-central-1',
});

const s3 = new AWS.S3();

const generatePresignedUrl = async (bucketName: String, objectKey: any) => {
try {
// Generate a pre-signed URL for the S3 object
const signedUrl = await s3.getSignedUrlPromise('getObject', {
Bucket: "bucketname",
Key: objectKey,
Expires: 60 * 5, // URL expires in 5 minutes
});

return signedUrl;
} catch (error) {
console.error('Error generating pre-signed URL:', error);
throw error;
}
};

export default generatePresignedUrl;
const AWS = require('aws-sdk');

// Configure AWS credentials and region
AWS.config.update({
accessKeyId: 'removed',
secretAccessKey: 'removed',
region: 'me-central-1',
});

const s3 = new AWS.S3();

const generatePresignedUrl = async (bucketName: String, objectKey: any) => {
try {
// Generate a pre-signed URL for the S3 object
const signedUrl = await s3.getSignedUrlPromise('getObject', {
Bucket: "bucketname",
Key: objectKey,
Expires: 60 * 5, // URL expires in 5 minutes
});

return signedUrl;
} catch (error) {
console.error('Error generating pre-signed URL:', error);
throw error;
}
};

export default generatePresignedUrl;
When I change
const response = await fetch(`/api/exams?objectKey=${objectKey}`);
const response = await fetch(`/api/exams?objectKey=${objectKey}`);
to a presigned URL I get directly from S3 console, the PDF works and renders completely fine. However, when I try and get a new presigned URL by calling my api, and then passing that response as oppose to getting a presigned URL from the S3 console, I get the error:
GET http://localhost:3000/api/exams?objectKey=biology/Photosynthesis%201.pdf 405 (Method Not Allowed)
page.tsx:11
GET http://localhost:3000/api/exams?objectKey=biology/Photosynthesis%201.pdf 405 (Method Not Allowed)
page.tsx:11
I've made an IAM user with the permissions required and passed in the credentials to those in "accessKeyId" and "secretAccessKey". The bucket name is also correct, and so is the object key. Anyone know how I can fix this? I also get the following errors in my terminal
Detected default export in '/Users/user/Code/projectName/app/api/exams/route.ts'. Export a named export for each HTTP method instead.
- error No HTTP methods exported in '/Users/user/Code/projectName/app/api/exams/route.ts'. Export a named export for each HTTP method.
Detected default export in '/Users/user/Code/projectName/app/api/exams/route.ts'. Export a named export for each HTTP method instead.
- error No HTTP methods exported in '/Users/user/Code/projectName/app/api/exams/route.ts'. Export a named export for each HTTP method.
as well as a warning to migrate to the AWS SDK (v3) (not sure if the outdated AWS SDK could be causing the issue)
W
whatplan329d ago
not a ton of aws experience but my guess is this is a permissions thing you could try generating a new set of credentials with every permission and see if it works then take away one at a time to see what mightve been causing the error
I
iukea328d ago
https://link.medium.com/B21i2jAJlAb Maybe encryption keys with the objects that are at rest
theburningmonk.com
Yes, S3 now encrypts objects by default, but your job is not done y...
Learn to build production-ready serverless applications on AWS
I
iukea328d ago
Posting this because when you create a pre signed URL you are in essence sharing the exception key to the user "Any request through the AWS SDK, AWS CLI or via the public URL of an object would give the attacker access to the unencrypted object contents. S3 would decrypt the data and return the unencrypted data because it owns the encryption keys."
W
wlvz327d ago
thought it was this too but my IAM user (the one im using to make the api call) has "AmazonS3FullAccess" as part of its permissions fixed it 👍
Want results from more Discord servers?
Add your server
More Posts
unless if want to share components / utils between packages , why we need a mono repo ? T3 cli app ?The T3 Cli have www for the website and a cli , but they dont share any thing in common except the tNeed clerk webhook exampleI have been trying to configure a webhook to be called as soon an organization has been created and GitHub Action Failing Type-Checks but Working LocallyI'm setting up an automatic github action that will run type checks on my nextjs app when pushed / plooking for some tutorialscoming from a rails background, im new to node & t3 in general, is there a tutorial i can follow to is it worth it to have meaningful errors returned from server side form validation?like is it worth it at that point to return an error for every field that is invalid from the backenGood setup for CRA + Serverless in a monorepo?Hey folks, what’s a good setup for a serverless function and a CRA in a monorepo? If I’m able to stHow to redirect with new headers back to the same page?User goes to page `/foo`, we check if this page is protected, if it is, we rewrite the page to sometThe Edge - what's the pointso as far as i know the edge move a server close as possible to your user right? but then what's thcloudflare R2 in Versel?Has anyone here used R2 in their NextJS app hosted on Versel? Trying to save money on bandwidth WhReact.FC equivalent for RSCIs there a "React.FC" equivalent for React Server Components?How do I use the return type of a function with overloads that will be returned in the same functionI'm creating a library, but I have a problem when I use a function with overloads to return itself iMedia not rendering on first try, only after a refreshHey, first of all, this is my first complex Next.js project, so it is very probable that I am doing any good resource for browser extension development?just titleRoute Guarding with nextjsHow do you implement route guarding with nextjs? Not too sure what would be the recommended way to dthe URL must start with the protocol `mongo`I have been geting this error recently, and I have no idea why. my mongodb DATABASE_URL is without "Read and Write in Next Js Is not working in production (vercel)this code is working in local environment but not working in production can anyone tell me how to reDesperate for help with Trpc resolver.How can i get resolve({ default: base64Data }); to only trigger once trpc has returned success or fuseRouter queryHow do y'all handle typescript with nextjs's query params? Currently I'm doing ```const cartId = quPassword hashing on EDGE?I'm making an app and noticing that vercel's serverless functions are taking very much time to complAuth.js Muti-device SupportI'm implementing auth right now in a site that the user will likely be switching from mobile to desk