3

我正在尝试设计一个系统,在该系统中在我的 AWS 用户池中创建用户并将其分配给四个用户组之一。这些用户组具有附加到他们的角色,这些角色指定了他们被允许进行的 API 调用。我为每个组创建了一个用户,并且能够在我的 Android 应用程序中成功登录到他们。我的用户池也附加到一个身份池,用于使用身份联合处理单点登录。

问题是,当我登录用户时,不是假设分配给用户组的角色,而是分配给用户的角色似乎来自身份池而不是他们的用户组,因此他们无法进行他们应该有权访问的 api 调用。

我试图通过在 Node.js 中实现自定义授权器来解决此问题,但脚本似乎遇到了一些问题。每当它进入ValidateToken()方法时,它都无法说明令牌不是 JWT 令牌。

console.log('Loading function');

var jwt = require('jsonwebtoken'); 
var request = require('request'); 
var jwkToPem = require('jwk-to-pem');

var groupName = 'MY_GROUP_NAME';
var roleName = 'MY_ROLE_NAME';
var policyName = 'MY_POLICY_NAME';
var userPoolId = 'MY_USER_POOL_ID';
var region = 'MY_REGION';
var iss = 'https://cognito-idp.' + region + '.amazonaws.com/' + userPoolId;
var pems;

exports.handler = function(event, context) {
//Download PEM for your UserPool if not already downloaded
if (!pems) {
//Download the JWKs and save it as PEM
request({
   url: iss + '/.well-known/jwks.json',
   json: true
 }, function (error, response, body) {
    if (!error && response.statusCode === 200) {
        pems = {};
        var keys = body['keys'];
        for(var i = 0; i < keys.length; i++) {
            //Convert each key to PEM
            var key_id = keys[i].kid;
            var modulus = keys[i].n;
            var exponent = keys[i].e;
            var key_type = keys[i].kty;
            var jwk = { kty: key_type, n: modulus, e: exponent};
            var pem = jwkToPem(jwk);
            pems[key_id] = pem;
        }
        //Now continue with validating the token
        ValidateToken(pems, event, context);
    } else {
        //Unable to download JWKs, fail the call
        context.fail("error");
    }
});
} else {
    //PEMs are already downloaded, continue with validating the token
    ValidateToken(pems, event, context);
};
};

function ValidateToken(pems, event, context) {

var token = event.authorizationToken;
//Fail if the token is not jwt
var decodedJwt = jwt.decode(token, {complete: true});
if (!decodedJwt) {
    //THIS IS WHERE THE SCRIPT ENDS UP
    console.log("Not a valid JWT token");
    context.fail("Unauthorized - Invalid Token Provided");
    return;
}

//Fail if token is not from your UserPool
if (decodedJwt.payload.iss != iss) {
    console.log("invalid issuer");
    context.fail("Unauthorized - Invalid Issuer Provided");
    return;
}

//Reject the jwt if it's not an 'Access Token'
if (decodedJwt.payload.token_use != 'access') {
    console.log("Not an access token");
    context.fail("Unauthorized - Not an Access Token");
    return;
}

//Get the kid from the token and retrieve corresponding PEM
var kid = decodedJwt.header.kid;
var pem = pems[kid];
if (!pem) {
    console.log('Invalid access token');
    context.fail("Unauthorized - Invalid Access Token Provided");
    return;
}

//Verify the signature of the JWT token to ensure it's really coming from your User Pool
jwt.verify(token, pem, { issuer: iss }, function(err, payload) {
  if(err) {
        console.log(err, err.stack); // an error occurred
        context.fail("Unauthorized - Could not verify token signature");
  } 
  else {
    //Valid token. Generate the API Gateway policy for the user
    //Always generate the policy on value of 'sub' claim and not for 'username' because username is reassignable
    //sub is UUID for a user which is never reassigned to another user.
    var principalId = payload.sub;
    var username = payload.username;

    var cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();
    var params = {
        UserPoolId: userPoolId, /* ID of the Target User Pool */
        Username: username, /* Provided by event object??? */
        Limit: 0,
        NextToken: ''       //May need actual token value
    };

    cognitoidentityserviceprovider.adminListGroupsForUser(params, function(err, data) {
        if (err){
            console.log(err, err.stack); // an error occurred
            context.fail("Unauthorized - Could not obtain Groups for User");
        } 
        else{
            var groups = data.Groups;
            var numGroups = groups.length;
            var isFound = false;
            for(var i = 0; i < numGroups; i++){
                if(groups[i].GroupName == groupName){
                    isFound = true;
                }
            }

            if(isFound){
                var iam = new AWS.IAM();
                var iamParams = {
                    PolicyName: policyName, /* Name of the Policy in the User Group Role */
                    RoleName: roleName /* Name of the User Group Role */
                };

                iam.getRolePolicy(params, function(err, data) {
                    if (err){
                        console.log(err, err.stack); // an error occurred
                        context.fail("Unauthorized - Could not acquire Policy for User Group Role");
                    } 
                    else {
                        var policy = data.PolicyDocument;
                        context.succeed(policy);        //May need to build policy
                    }
                });
            }
            else{
                context.fail("Unauthorized - Could not find the required User Group under the User");
            }
        }
    });
  }
});
}

谁能确定这个脚本的问题,或者帮助我确定为什么设置的令牌不是有效的 JWT 令牌?令牌由 Android 应用程序使用 AWS Cognito 开发工具包发送。

编辑:经过进一步调查,从 event.authorizationToken 检索到的令牌具有以下格式([VALUE] 块用于隐藏潜在的敏感信息):

AWS4-HMAC-SHA256 Credential=[VALUE1]/20170329/us-east-1/execute-api/aws4_request, 
SignedHeaders=host;x-amz-date;x-amz-security-token, 
Signature=[VALUE2]
4

2 回答 2

2

如果客户端在登录后获取 AWS 凭证,您只能在 API Gateway 方法上使用 AWS_IAM 授权类型。您看到的 authorizationToken 值是客户端使用 Cognito 提供的凭证生成的 AWS 签名。您无法在自定义授权方中验证 AWS 签名。

您在关注这篇Cognito 博客文章吗?如果是这样,我认为您可能会将用户组角色与身份池上的已验证角色选择混淆。当您将联合身份与用户池提供商一起使用时,您的客户端将从身份池的 Cognito 选项卡中的该部分取回具有“已验证角色”权限的 AWS 凭证。在博客文章中,这将是身份池上设置的“EngineerRole”。

于 2017-03-30T05:36:35.707 回答
2

我想通了:这个文档(特别是底部)说“如果您在 Amazon Cognito 用户池中为组设置角色,这些角色将通过用户的 ID 令牌传递。要使用这些角色,您还必须设置从选择角色身份池的经过身份验证的角色选择的令牌。”

所需要做的就是为每个角色设置适当的信任策略,调整身份池以使用“从令牌中选择角色”和用户池身份验证提供程序,并且现在正在承担适当的角色。对于遇到此问题的其他人,这是我的信任策略:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Federated": "cognito-identity.amazonaws.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "cognito-identity.amazonaws.com:aud": "[IDENTITY_POOL_ID]"
        },
        "ForAnyValue:StringLike": {
          "cognito-identity.amazonaws.com:amr": "authenticated"
        }
      }
    }
  ]
}
于 2017-03-30T14:10:56.767 回答