S3 Exercise 2.3: Add Objects to a Bucket Using a Browser-Side Script with AWS SDK and Cognito

Kerry Sheldon
6 min readMay 27, 2018

--

(This post is part of the AWS for the Bootcamp Grad series. The series consists of exercises to build familiarity with various aspects of the AWS ecosystem. Again, all of these posts are “exercises” for introductory exposure to AWS — they are NOT represented as best practices.)

Background

In this exercise, you will create an S3 bucket to store image files. You will also create a static website hosted on S3 as an interface to upload files to the bucket and see the contents of the bucket. To do that, you will write a browser-side script that uses the AWS SDK, and authentication through Amazon Cognito.

Specifically, you will do the following:

  • Create an S3 bucket to store images.
  • Configure CORS on that bucket to allow get requests to that bucket.
  • Create an Amazon Cognito identity pool (your browser script will need to provide credentials to access your S3 resources).
  • Configure the Amazon Cognito’s IAM role for unauthenticated users to provide access to your S3 bucket.
  • Create another S3 bucket and configure it to host a static website.
  • Create a browser side script to list and display all images in the other bucket.
  • Create a browser side script to upload a new image to the other bucket.

Create an S3 bucket to Store Images

Follow the Create an S3 Bucket section of Exercise 2.1. Upload an image file to the bucket and grant public access to the object.

Configure CORS on the Bucket

Add a CORS configuration to that bucket from the Permissions tab. Select CORS configuration and add the following.

<CORSConfiguration>
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedHeader>*</AllowedHeader>
<MaxAgeSeconds>3000</MaxAgeSeconds>
</CORSRule>
</CORSConfiguration>

This rule will allow GET requests from any origin, with any header.

Create an Amazon Cognito Identity Pool and Configure IAM Role

In order to allow your browser side script to use the AWS SDK to access your S3 resources (to get and upload images from the bucket you just created), you need to create a Cognito identity pool to use for credentialing.

Navigate to the Cognito service from the AWS dashboard. Select Manage Identity Pools and click Create new identity pool.

Enter an identity pool name of your choice. Select Enable access to unauthenticated identities. Click Create pool.

In the next step, expand the Details section. You will see two role summaries — one for authenticated users and one for unauthenticated identities. You do not need to do anything with the role for authenticated identities as your website will not have authentication.

In the role summary for unauthenticated identities, keep Create a new IAM Role selected. Click View Policy Document and click Edit. Insert the following (using your bucket name in the Resource section) to allow users with this role the ability to perform any S3 action on your S3 bucket:


{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": [
"arn:aws:s3:::BUCKET_NAME/*"
]
}
]
}

Click Allow.

Create Another S3 Bucket and Configure it for Website Hosting

Follow the Create an S3 Bucket and Configure the Bucket to Host a Static Website sections of Exercise 2.1.

Create a Browser Side Script to List and Display All Images

In this exercise, you will be uploading two objects to the website bucket you just created — index.html and app.js. The app.js file will be the browser-side script that performs actions on the S3 bucket containing your images.

Create an index.html file with the following:

<html>
<head>
<!-- Javascript AWS SDK -->
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.247.1.min.js"></script>
<!-- Your Browser-Side script -->
<script src="./app.js"></script>
<!-- Latest compiled and minified Boostrap CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<style>
img { width:200px; }
.panel-body { height:250px; }
</style>
</head>
<body>
<div class="container">
<div class="row">
<h4>My Images</h4>
<div id="content"></div>
</div>
</div>
<script>
//calls a method on your browser-side script - app.js
listImages();
</script>
</body>
</html>

Create an app.js file with a listImages function (called in the index.html file). This function will make use of the JavaScript AWS SDK to get the list of images in your bucket (see the S3 class documentation here):

//insert name of your image bucket
const Bucket = 'YOUR-BUCKET-NAME';
//insert identity pool id for your cognito pool
//you can get this by clicking Edit Identity Pool from your Cognito Pool
const IdentityPoolId = 'YOUR-POOL-ID';
const credentials = new AWS.CognitoIdentityCredentials({ IdentityPoolId })//insert region for your image bucket
const region = 'YOUR-REGION'; //e.g. 'us-east-1'
const bucketUrl = `https://s3.amazonaws.com/${Bucket}`//uses the AWS SDK
//updates its configuration with your credentials and bucket region
AWS.config.update({
region,
credentials
});
//uses the AWS SDK
//creates an S3 service object that can interact with your bucket
const s3 = new AWS.S3({
apiVersion: '2006-03-01',
params: { Bucket }
});
function contentContainer() {
return document.getElementById('content')
}
//function called from index.html
//gets name of all objects in the s3 bucket
//updates HTML with the file name and image
function listImages() {
s3.listObjects({Delimiter: '/'}, function(err, data) {
if (err) {
return alert('Error: ' + err.message);
} else {
const images = data.Contents;
const fileNames = images.map(function (image) { return image.Key; });
const html = fileNames.map(function(file) {
return `<div class="col-md-4 panel panel-default">
<div class="panel-heading">${file}</div>
<div class="panel-body">
<img src="${bucketUrl}/${file}">
</div>
</div>`
})
contentContainer().innerHTML = html.join('');
}
});
}

Upload both files to your website bucket (granting public read permission to both). Visit your website URL and you should see your image displayed.

Create a Browser Side Script to Upload An Image

In this step, you will update your index.html file to include an input and button to submit a file for upload to S3. Update your index.html file to the following:

<html>
<head>
<!-- Javascript AWS SDK -->
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.247.1.min.js"></script>
<!-- Your Browser-Side script -->
<script src="./app.js"></script>
<!-- Latest compiled and minified Boostrap CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<style>
img { width:200px; }
.panel-body { height:250px; }
</style>
</head>
<body>
<div class="container">
<!-- new input and button to upload an image -->
<div class="row">
<form>
<div class="form-group">
<label for="upload">Add Image</label>
<input id="upload" type="file" class="form-control-file" accept="image/*" >
</div>
<button type='button' class="btn btn-info btn-xs" onclick="addImage()">Upload</button>
<form>
</div>
<div class="row">
<h4>My Images</h4>
<div id="content"></div>
</div>
</div> <script>
//calling a method on your browser-side script - app.js
listImages();
</script>
</body>
</html>

The Add Image button calls a function addImage. You will need to add this function to your app.js file (refer to the putObject documentation here):

function addImage() {
const files = document.getElementById('upload').files;
const file = files[0];
const fileName = file.name;

//build the params needed for the putObject call
const params = {
Key: fileName,
Body: file,
ACL: 'public-read' //this makes the object readable
}
s3.putObject(params, function(err, data) {
if (err) {
return alert('There was an error uploading your photo: ', err.message);
} else {
//if successful, call listImages again so we see the new image
listImages()
}
});
}

Upload the new index.html and app.js files to your website bucket (grant public read permission). Navigate to your website URL, refresh the page, and try to upload an image file.

You will receive an error (see the output in your browser console). This is a CORS error that you can resolve by updating the CORS configuration on your image bucket.

Your current CORS configuration only allows for GET requests. As you can see in the AWS S3 API documentation, putObject will make a PUT request. Navigate to your image bucket, click Permissions then CORS configuration. Add the following to your CORS rule and click Save.

<AllowedMethod>PUT</AllowedMethod>

Return to your website URL and try to upload the photo again. It should work and you will see the new photo on the page.

Clean Up

Navigate to your S3 dashboard. Click the bucket symbol next to the image bucket name. Then click Delete Bucket. Type the name of the bucket, and click Confirm. Do the same for the website bucket.

Delete your Cognito identity pool following the instructions shown here.

Next: Exercise 2.4: Cross Region Replication (coming soon)

--

--

Kerry Sheldon

Software Developer. Graduate of Turing School of Software and Design.