How to Allow Users to Upload and Download Data from AWS S3 Without Direct Access Using Pre-Signed URLs?
Tags: tutorial, devjournal, aws, typescript
Published: 2022-12-24 05:49:34

## Introduction

Amazon Simple Storage Service (AWS S3) is a popular cloud storage service that allows users to store and retrieve data from a variety of sources. While S3 buckets are set to public by default, there are times when it is necessary to restrict access to specific users or groups. Direct access to the bucket in these scenarios may pose security risks and limit flexibility.

---

## Problem

Direct access to an AWS S3 bucket necessitates exposing the AWS access keys and secret keys to users, which can result in unauthorized access to the bucket and its contents. Furthermore, using this method, it may be impossible to control specific actions or limit access time.

---

## Solution

Pre-signed URLs provide a secure solution by allowing users to access specific objects in the bucket without exposing the AWS access keys and secret keys. Pre-signed URLs can also be used to restrict specific actions that users can take and to limit the amount of time that a user has access to the data.

-

To generate pre-signed URLs in TypeScript, you can use the `aws-sdk` package, which provides a `getSignedUrl` method that can be used to generate a pre-signed URL for a specific S3 object. Here's an example of how to use `getSignedUrl` to generate a pre-signed URL for downloading an S3 object:

```ts
// Generate a pre-signed URL for an S3 object
async function generatePresignedUrl(
  bucket: string,
  key: string,
  expiration: number
): Promise<string> {
  const s3 = new AWS.S3()
  const params = {
    Bucket: bucket,
    Key: key,
    Expires: expiration,
  }
  const url = await s3.getSignedUrlPromise('getObject', params)
  return url
}

// Example usage
const bucket = 'my-s3-bucket'
const key = 'path/to/my/object.jpg'
const expiration = 60 // URL will expire in 60 seconds
const url = await generatePresignedUrl(bucket, key, expiration)
console.log(url) // Outputs a pre-signed URL that can be used to access the object
```

To generate a pre-signed URL for uploading an S3 object, you can use the putObject method in a similar way:

```ts
import * as AWS from "aws-sdk";
import * as express from "express";

// Configure the AWS SDK with your S3 bucket's credentials
AWS.config.update({
  accessKeyId: "ACCESS_KEY_ID",
  secretAccessKey: "SECRET_ACCESS_KEY",
});

const s3 = new AWS.S3();

// Create an Express.js router
const router = express.Router();

// Route for generating a pre-signed URL for uploading a file to S3
router.post("/upload", async (req, res) => {
  try {
    // Check if the file was included in the request
    if (!req.body.file) {
      throw new Error("No file provided");
    }

    // Get the file and other parameters from the request body
    const { file, bucket, key } = req.body;

    // Generate a pre-signed URL for uploading the file to S3
    const params = {
      Bucket: bucket,
      Key: key,
      ContentType: file.type,
      ACL: "private", // Set the ACL to "private"
    };
    const url = s3.getSignedUrl("putObject", params);

    // Send the pre-signed URL back to the client
    res.send({ url });
  } catch (error) {
    // Send an error response if something went wrong
    res.status(500).send({ error: error.message });
  }
});

// Export the router
export default router;

---

// Example Usage
import * as axios from "axios";

// Function to upload a file to S3 using a pre-signed URL
async function uploadFile(url: string, file: File): Promise<void> {
  // Set the headers for the request
  const headers = {
    "Content-Type": file.type,
  };

  // Make the PUT request to the pre-signed URL
  const response = await axios.put(url, file, { headers });

  // Check the status of the response
  console.log(response.status); // Outputs 201 if upload was successful
}

// Example usage
const file = new File(["Hello, world!"], "hello.txt", { type: "text/plain" });
const url = "<server_url>/upload";
await uploadFile(url, file);

```

---

## Conclusion

Pre-signed URLs are a convenient and secure way for users to access data in an AWS S3 bucket without granting direct access. To use pre-signed URLs, use the AWS SDK to generate a URL for a specific object in the bucket. The generated URL can then be provided to the user, who can use it to access the object within the time period specified in the URL.

---

### References

- [AWS documentation on pre-signed URLs](https://docs.aws.amazon.com/AmazonS3/latest/dev/PresignedUrlUploadObject.html)
- [AWS SDK for JavaScript documentation on getSignedUrl method](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#getSignedUrl-property):