在 React 中使用 aws-amplify 将文件上传到具有联合身份的 S3 时出错

Posted

技术标签:

【中文标题】在 React 中使用 aws-amplify 将文件上传到具有联合身份的 S3 时出错【英文标题】:Getting Error while uploading file to S3 with Federated Identity using aws-amplify in React 【发布时间】:2020-08-06 20:30:34 【问题描述】:

当使用 Facebook 联合身份的用户尝试上传图片时,我收到一个错误:AWSS3Provider - 上传错误错误:“请求失败,状态码为 403” 状态码:403 禁止

注意到请求中的 URL,而用户通过联合身份 (Facebook) 进行身份验证时,看起来: 请求网址:https://my-gallery-api-dev-photorepos3bucket-XXXX.s3.us-east-2.amazonaws.com/private/undefined/1587639369473-image.jpg?x-id=PutObject

将放置上传图像的文件夹是'undefined',而不是像通过 AWS UserPool 进行身份验证的用户那样的有效用户身份,请参阅: 请求网址: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

对于身份验证和上传,我正在使用 React aws 依赖项"aws-amplify": "^3.0.8"

Facebook 身份验证(Facebook 按钮):

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);
    
  

上传中:

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;

我了解 S3 以 403 拒绝,由于 IAM 角色,我对经过身份验证的用户有:

  # 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**'

对于注册在 AWS 用户池(电子邮件、密码)中的用户可以正常工作,但是对于联合身份的用户,AWS 用户池中没有记录,只有联合身份,所以不会有 cognito-identity.amazonaws .com:sub 为那些用户和目录 'undefined' 不属于使用联合身份标识的用户的角色限额。

请指教: 1. 在哪里/如何在 URL 中修复这个 'undefined'? 2. 另外,我可能希望将上传 URL 中的 thouse Id 替换为我将在不久的将来添加的用户数据库中生成的用户 ID。如何修复 IAM 角色以使用自定义 ID?

【问题讨论】:

【参考方案1】:

我在做Serverless Stack教程时遇到了同样的问题

当您执行 Extra Credit > React > Facebook Login with Cognito using AWS Amplify 时会出现此错误,因为您注意到如果您已通过 Facebook 身份验证,则上传文件会失败。

将 PUT 发送到:https://<bucket>.s3.amazonaws.com/private/<identity-id>/<file> 时出现错误 ...<identity-id> 未定义,因此 PUT 失败。

如果您在运行登录命令时记录所获得的内容,则可以追踪此未定义的来源。例如,当您使用您的电子邮件和密码登录时,如果您这样做:

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

...可以看到identityId设置正确。

另一方面,当您通过 Auth.federatedSignIn 登录 Facebook 时,如果您记录了回复,则不会收到 identityId。注意:如果您之前使用电子邮件和密码登录,它将保持不变,因此此错误配置也会导致上传失败。

我使用的解决方法是添加一个简单的 lambda,它为已登录的用户返回 identityId,因此一旦用户使用 facebook 登录,我们就会要求它,我们可以使用将 PUT 发送到正确的 url AWS.S3().putObject

如果您想尝试这个,请注意您应该在 https 中托管您的 React 应用程序,因为 Facebook 不允许使用 http 域。你可以设置这个添加 HTTPS=true 到你的 React .env 文件。

您可以查看我的回购作为示例:APIFrontend

【讨论】:

以上是关于在 React 中使用 aws-amplify 将文件上传到具有联合身份的 S3 时出错的主要内容,如果未能解决你的问题,请参考以下文章

aws-amplify-react 和 @aws-amplify/ui-react 有啥区别?

无法解析模块“@aws-amplify/ui-react/styles.css”的路径

如何使用 aws-amplify 验证 node/express 中的 accessToken?

AWS-amplify 在请求中包含 cognito Authorization 标头

如何将 React 本机导航与 Android 中的 AWS Amplify 推送通知集成?

尝试将我的“aws-amplify”包导入我的入口点时收到 JSLint 错误