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

问题描述

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

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

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/*"
}

注意:在我编辑之后,可视化编辑器会自动插入这些 Sid。

现在,我为读取 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 指定了“任何东西”,因为我认为这些不是必需的。当它运行时,没有错误。一切都按顺序显示,以验证我的经过身份验证的用户属于所需的 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我的 UserPoolId in 中的错字有关Logins。我错过了一个下划线。呸!感谢 John 引导我进行修复。如果你不建议我仔细检查,我会永远挣扎。

更正上面的代码:

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

标签: amazon-web-servicesamazon-s3aws-sdkamazon-cognitoamazon-iam

解决方案


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

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

推荐阅读