Amplify S3 Image - 404 Not Found

Viewed 1260

I am trying to access an image that I have uploaded to my S3 bucket. I created my bucket using the Amplify CLI (amplify add storage) and granted access to all of my cognito groups. I have also granted my AuthRole AmazonS3FullAccess. My Bucket is set to allow all public access as well.

I have tried all the different ways I can find online to access this image and the only way that works so far is to leave it open to the public and use the image url directly. But even if I use the public method of accessing the image using Amplify's tools, I get the 404 error. Below is my code, am I doing something wrong with the url generation?

resources:

import React, { Component} from 'react'
import Amplify, { Auth, Storage } from 'aws-amplify';
import { AmplifyS3Image} from "@aws-amplify/ui-react";
import { Card } from 'reactstrap';

// FYI, this all matches my aws-exports and matches what I see online in the console
Amplify.configure({
    Auth: {
        identityPoolId: 'us-east-1:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', //REQUIRED - Amazon Cognito Identity Pool ID
        region: 'us-east-1', // REQUIRED - Amazon Cognito Region
        userPoolId: 'us-east-1_XXXXXXXXX', //OPTIONAL - Amazon Cognito User Pool ID
        userPoolWebClientId: 'XXXXXXXXXXXXXXXXX', //OPTIONAL - Amazon Cognito Web Client ID
    },
    Storage: {
        AWSS3: {
            bucket: 'xxxxxxxxx-storage123456-prod', //REQUIRED -  Amazon S3 bucket name
            region: 'us-east-1', //OPTIONAL -  Amazon service region
        }
    }
});

class TestPage extends Component {
    constructor(props) {
        super(props);
        this.state = {  }
    };

    async componentDidMount() {
        const user = await Auth.currentAuthenticatedUser();
        const deviceKey = user.signInUserSession.accessToken.payload['device_key']
        console.log( deviceKey, user );

        const storageGetPicUrl = await Storage.get('test_public.png', {
            level: 'protected',
            bucket: 'xxxxxxxxx-storage123456-prod',
            region: 'us-east-1',
          });
        console.log(storageGetPicUrl);
        
        this.setState({
            user,
            deviceKey,
            profilePicImg: <img height="40px" src={'https://xxxxxxxxx-storage123456-prod.s3.amazonaws.com/test_public.png'} />,
            profilePicPrivate: <AmplifyS3Image imgKey={"test_default.png"} />,
            profilePicPublic: <AmplifyS3Image imgKey={"test_public.png"} />,
            profilePicPrivate2: <AmplifyS3Image imgKey={"test_default.png"} level="protected" identityId={deviceKey} />,
            profilePicPublic2: <AmplifyS3Image imgKey={"test_public.png"} level="protected" identityId={deviceKey} />,
            profilePicStorage: <img src={storageGetPicUrl} />,
          });
    };

    render() { 
        return ( 
            <table>
                <tbody>
                    <tr><td><Card>{this.state.profilePicImg}</Card></td></tr>
                    <tr><td><Card>{this.state.profilePicPrivate}</Card></td></tr>
                    <tr><td><Card>{this.state.profilePicPublic}</Card></td></tr>
                    <tr><td><Card>{this.state.profilePicPrivate2}</Card></td></tr>
                    <tr><td><Card>{this.state.profilePicPublic2}</Card></td></tr>
                    <tr><td><Card>{this.state.profilePicStorage}</Card></td></tr>
                </tbody>
            </table>
        );
    };
};
 
export default TestPage;

render and console ouput bucket

1 Answers

Okay, I've got it figured out! There were 2 problems. One, AWS storage requires you to organize your folder structure in the bucket a certain way for access. Two, I had to update my bucket policy to point at my AuthRole.

  1. When you configure your storage bucket, Amplify CLI will setup your S3 bucket with access permission in such a way that contents in 'public' folder can be accessed by everyone in who's logged into your app. 'private' for user specific contents,' protected' for user specific and can be accessed by other users in the platform. SOURCE
  2. The bucket policy itself needs to be updated to give authentication to your AuthRole which you are using with your webpage login. For me this was the AuthRole that my Cognito users are linked to. This link helped me set the Actions in my policy, but I think it's an old policy format. This link helped me with getting the policy right.

My image is located at: public/test.png within my bucket. The folder name 'public' is necessary to match up with the level specified in the Storage call below. I tested this by setting all permissions Block Public Access. I ran my code without the change to my policy and the images would not load, so they were definitely blocked. After updating the policy, the images loaded perfectly.

Simplified version of the parts of my code that matter:

import { Storage } from 'aws-amplify';

// image name should be relative to the public folder
// example: public/images/test.png => Storage.get('images/test.png' ...
const picUrl= await Storage.get('test.png', {
  level: 'public',
  bucket: 'bucket-name',
  region: 'us-east-1',
});
const img = <img width="40px" name="test" src={picUrl} alt="testImg" />

My bucket polcy:

{
    "Version": "2012-10-17",
    "Id": "Policy1234",
    "Statement": [
        {
            "Sid": "AllowReadWriteObjects",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::the-rest-of-my-authrole-arn"
            },
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:DeleteObject"
            ],
            "Resource": [
                "arn:aws:s3:::bucket-name/*"
            ]
        }
    ]
}
Related