简体   繁体   中英

Getting Error while uploading file to S3 with Federated Identity using aws-amplify in React

While the user with Facebook federated Identity trying to upload Image, I'm getting an error: AWSS3Provider - error uploading Error: "Request failed with status code 403" Status Code: 403 Forbidden

Noticed that URL in request, while user authenticated with Federated Identity (Facebook), looks:
Request URL: https://my-gallery-api-dev-photorepos3bucket-XXXX.s3.us-east-2.amazonaws.com/private/undefined/1587639369473-image.jpg?x-id=PutObject

The folder where the uploaded image will be placed is 'undefined' instead of being a valid user identity like for users authenticated with from AWS UserPool, see:
Request URL: https://my-gallery-api-dev-photorepos3bucket-XXXX.s3.us-east-2.amazonaws.com/private/us-east-2%3Aa2991437-264a-4652-a239-XXXXXXXXXXXX/1587636945392-image.jpg?x-id=PutObject

For Authentication and upload I'am using React aws dependency "aws-amplify": "^3.0.8"

Facebook Authentication (Facebook Button):

async handleResponse(data) {
    console.log("FB Response data:", data);
    const { userID, accessToken: token, expiresIn } = data;
    const expires_at = expiresIn * 1000 + new Date().getTime();
    const user = { userID };

    this.setState({ isLoading: true });
    console.log("User:", user);
    try {
      const response = await Auth.federatedSignIn(
        "facebook",
        { token, expires_at },
        user
      );
      this.setState({ isLoading: false });
      console.log("federatedSignIn Response:", response);
      this.props.onLogin(response);
    } catch (e) {
      this.setState({ isLoading: false })
      console.log("federatedSignIn Exception:", e);
      alert(e.message);
      this.handleError(e);
    }
  }

Uploading:

import { Storage } from "aws-amplify";

export async function s3Upload(file) {
  const filename = `${Date.now()}-${file.name}`;

  const stored = await Storage.vault.put(filename, file, {
    contentType: file.type
  });

  return stored.key;
}
     const attachment = this.file
        ? await s3Upload(this.file)
        : null;

I'm understand that rejection by S3 with 403, because of the IAM role, I have for authenticated users:

  # IAM role used for authenticated users
  CognitoAuthRole:
    Type: AWS::IAM::Role
    Properties:
      Path: /
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: 'Allow'
            Principal:
              Federated: 'cognito-identity.amazonaws.com'
            Action:
              - 'sts:AssumeRoleWithWebIdentity'
            Condition:
              StringEquals:
                'cognito-identity.amazonaws.com:aud':
                  Ref: CognitoIdentityPool
              'ForAnyValue:StringLike':
                'cognito-identity.amazonaws.com:amr': authenticated
      Policies:
        - PolicyName: 'CognitoAuthorizedPolicy'
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: 'Allow'
                Action:
                  - 'mobileanalytics:PutEvents'
                  - 'cognito-sync:*'
                  - 'cognito-identity:*'
                Resource: '*'

              # Allow users to invoke our API
              - Effect: 'Allow'
                Action:
                  - 'execute-api:Invoke'
                Resource:
                  Fn::Join:
                    - ''
                    -
                      - 'arn:aws:execute-api:'
                      - Ref: AWS::Region
                      - ':'
                      - Ref: AWS::AccountId
                      - ':'
                      - Ref: ApiGatewayRestApi
                      - '/*'

              # Allow users to upload attachments to their
              # folder inside our S3 bucket
              - Effect: 'Allow'
                Action:
                  - 's3:*'
                Resource:
                  - Fn::Join:
                    - ''
                    -
                      - Fn::GetAtt: [PhotoRepoS3Bucket, Arn]
                      - '/private/**${cognito-identity.amazonaws.com:sub}/***'
                  - Fn::Join:
                    - ''
                    -
                      - Fn::GetAtt: [PhotoRepoS3Bucket, Arn]
                      - '/private/**${cognito-identity.amazonaws.com:sub}**'

It works fine for users registered in AWS User Pool (Email, Password), but for federated users, there is no record in AWS User Pool only in Federated Identities, so there will be no cognito-identity.amazonaws.com:sub found for those users and directory 'undefined' not falling in role allowance for user identified with Federated Identity.

Please advise:
1. Where/how to fix this 'undefined' in URL?
2. Also, I would like, probably, to replace thouse Id's in upload URL to genereted user Id's from user database I'm going to add in near future. How to fix IAM Role to use custom Id's?

I stumbled with the same problem when doing Serverless Stack tutorial

This error arises when you do the Extra Credit > React > Facebook Login with Cognito using AWS Amplify , as you have notice uploading a file fails if you're authenticated with Facebook.

The error comes up when sending a PUT to:
https://<bucket>.s3.amazonaws.com/private/<identity-id>/<file>
...the <identity-id> is undefined so the PUT fails.

You can track down the source of this undefinition if you log what you get when running the login commands. For example, when you login using your email and password, if you do:

await Auth.signIn(fields.email, fields.password);
const currCreds = await Auth.currentCredentials();
console.log('currCreds', currCreds);

...you can see that identityId is set correctly.

On the other hand when you login with Facebook through Auth.federatedSignIn if you log the response you don't get identityId . Note: In the case you've previously logged in using email and password, it will remain the same, so this misconfiguration will also make uploading fail.

The workaround I've used is adding a simple lambda which returns the identityId for the logged in user, so once the user logs in with facebook, we ask for it and we can send the PUT to the correct url using AWS.S3().putObject

In the case you want to try this out, take into account that you should host your React app in https as Facebook doesn't allow http domains. You can set this adding HTTPS=true to your React.env file.

You can check my repos as example:
API
Frontend

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM