尝试在需要经过身份验证的访问的 S3 存储桶中获取对象时出现持续类型错误

Persistent typeerror on trying to getObject in S3 bucket requiring authenticated access

使用 AWS JavaScript SDK,我正在尝试读取特定的 S3 存储桶,作为已验证的 Cognito 用户登录到我网站的受保护区域。凭借确认我的 Cognito 身份后获得的有效 authToken,我已经验证我是以经过身份验证的用户身份进入的。事实上,我已经测试并证明了注册、多因素验证、登录和退出的整个 Cognito 流程。

出于某种原因,当我发出我的第一个 s3.getObject 请求时,我得到了以下 TypeError,即使我确定我正在传递一个字符串(密钥)作为第一个参数。这是错误:

TypeError: First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.

现在,首先,我通过 HTML 页面上的脚本标记包含 JavaScript SDK,如下所示:

script src="https://sdk.amazonaws.com/js/aws-sdk-2.154.0.min.js"></script>

我有以下由 Cognito 创建的 IAM 角色:

Cognito_MyAppNameAuth_Role

我添加了以下 S3 策略:

{
  "Sid": "VisualEditor1",
  "Effect": "Allow",
  "Action": "s3:ListBucket",
  "Resource": "arn:aws:s3:::my-bucket-name"
},
{
  "Sid": "VisualEditor2",
  "Effect": "Allow",
  "Action": "s3:GetObject",
  "Resource": "arn:aws:s3:::my-bucket-name/*"
}

注意:Visual Editor 会在我编辑后自动插入这些 Sids。

现在,我为读取 S3 存储桶而执行的代码驻留在我通过标签包含的 .js 文件中。它在加载后运行...

<body onLoad="bodyOnLoad();">

代码做的第一件事是验证用户凭据。我得到了用户池;然后我得到当前用户。然后我得到了会话。然后我使用 AWS.CognitoIdentityCredentials() 调用用户凭据,如下所示:

AWS.config.update({ //testing... 12/29
  region: 'us-east-1',
  accessKeyId: 'anything',
  secretAccessKey: 'anything',
  credentials: new AWS.CognitoIdentityCredentials({
     IdentityPoolId: 'us-east-1:numbers-and-letters-and-bears-oh-my',
     Logins: {
        'cognito-idp.us-east-1.amazonaws.com/us-east-1-xxxyyyxxx': session.getIdToken().getJwtToken()
        }
     })
});

我确实为 accessKeyId 和 secretAccessKey 指定了 "anything",因为我认为这些不是必需的。运行时,没有错误。一切都按顺序显示,以验证我的经过身份验证的用户属于所需的 Cognito 身份池。

这是接下来发生的 s3.getObject 尝试,并导致上面给出的 TypeError:

const s3 = new AWS.S3({
  apiVersion: '2006-03-01',
  params: {
    Bucket: 'my-bucket-name'
  }
});

var params = {
  Bucket: 'my-bucket-name',
  Key: 'index.json'
};

s3.getObject(params, (err, data) => {
  if (err) {
    // EXECUTION LANDS HERE!
    callback(err);
    return;
  }

  const content = data.Body.toString('utf-8');
  callback(null, content);
});
}

我在将参数传递给 getObject 方法的过程中尝试了很多排列。包括..

s3.getObject({Key: 'index.json'}, (err, data) => { ... }

我每次都得到同样的错误。 我到底做错了什么??? 提前致谢!!!

2019 年 1 月 1 日更新 - HNY!

响应下面的 John,我修改了我的代码以将所有内容移动到同一范围内。 (再次感谢。)很好的建议,但我仍然遇到相同的 TypeError(如上所示)。现在,我认为 John 可能是对的,真正的问题在于我的身份验证或身份池策略。如果我从 AWS.config.update 中删除伪造的 accessKeyId 和 secretAccessKey(正如 John 在他的示例中删除的那样),我会得到一个不同的错误:我得到一个 400 错误然后这个..

Error: Invalid login token. Issuer doesn't match providerName

这是我在 config.js:

中的代码
window._config = {
    cognito: {
        userPoolId: 'user-pool-id',
        userPoolClientId: 'user-pool-client-id',
        region: 'us-east-1'
    },
    api: {
        invokeUrl: 'https://<invoke-url>.execute-api.us-east-1.amazonaws.com/prod'
    }
};

这是将所有内容滚动到同一范围后的新代码流:

function loadFileFromS3(key, callback) {
  console.log('loadFileFromS3: ' + key); //key is 'index.json'

  var poolData = {
      UserPoolId: _config.cognito.userPoolId,
      ClientId: _config.cognito.userPoolClientId
  };

  var userPool;

  userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);

  if (typeof AWSCognito !== 'undefined') {
      AWSCognito.config.region = _config.cognito.region;
  }

  var cognitoUser = userPool.getCurrentUser();

  if (cognitoUser) {
      cognitoUser.getSession(function sessionCallback(err, session) {
          if (err) {
              reject(err);
          } else if (!session.isValid()) {
              resolve(null);
          } else {
              console.log('authToken is ' + session.getIdToken().getJwtToken());
              // Configure the credentials provider to use your identity pool
              AWS.config.update({ //testing... 12/29
                region: 'us-east-1',
                accessKeyId: 'anything', //remove gives me 400 error as shown above
                secretAccessKey: 'anything', // 
                credentials: new AWS.CognitoIdentityCredentials({
                  IdentityPoolId: 'us-east-1:<identity-pool-id>',
                  Logins: {
                    'cognito-idp.us-east-1.amazonaws.com/us-east-1-<user-pool-id>': session.getIdToken().getJwtToken()
                  }
                })
              });
              console.log('AWS.config update happened...');

              // call refresh to authenticate user and get credentials
              AWS.config.credentials.refresh((error) => {
                if (error) {
                  console.error(error);
                } else {
                  console.log('Success');
                  // NOTE: The credential are valid HERE
                  const s3 = new AWS.S3({
                    apiVersion: '2006-03-01',
                    params: {
                      Bucket: 'my-bucket-name'
                    }
                  });
                  var params = {
                    Bucket: 'my-bucket-name',
                    Key: key
                  };

                  console.log('s3.getObject with key: ' + key);
                  s3.getObject(params, (err, data) => {
                    if (err) {
                      console.log('getObject ERROR: ' + err);
                      callback(err);
                      return;
                    }

                    const content = data.Body.toString('utf-8');
                    callback(null, content);
                  });
                }
              });
          }
      });
  } else {
      resolve(null);
  }

}

更新

好的。该问题与我传递给 s3.getObject 的参数无关,而与我在 Logins 中的 UserPoolId 中的拼写错误有关。我错过了一个下划线。啊!感谢 John 带领我进行修复。如果你没有建议我仔细检查,我会永远陷入困境。

更正以上代码:

Logins: {
   'cognito-idp.us-east-1.amazonaws.com/us-east-1_<user-pool-id>': session.getIdToken().getJwtToken() //underscore after us-east-1_ !!!

我认为您真正的问题是您没有用户凭据。

AWS.config.update({ //testing... 12/29
  region: 'us-east-1',
  credentials: new AWS.CognitoIdentityCredentials({
    IdentityPoolId: 'us-east-1:numbers-and-letters-and-bears-oh-my',
    Logins: {
      'cognito-idp.us-east-1.amazonaws.com/us-east-1-xxxyyyxxx': session.getIdToken().getJwtToken()
    }
  })
});

// call refresh to authenticate user and get credentials
AWS.config.credentials.refresh((error) => {
  if (error) {
    console.error(error);
  } else {
    console.log('Success');
    // NOTE: The credential are valid HERE
    // Put your S3 code HERE.
    const s3 = new AWS.S3({
      apiVersion: '2006-03-01',
      params: {
        Bucket: 'my-bucket-name'
      }
    });
    // Continue with the rest of your S3 code HERE
  }
});
// The credentials are not valid HERE