【问题标题】:AWS API Gateway Custom Authorizer based on User Groups基于用户组的 AWS API Gateway 自定义授权方
【发布时间】:2017-08-23 10:00:10
【问题描述】:

我正在尝试设计一个系统,在该系统中在我的 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 令牌?令牌由使用 AWS Cognito 开发工具包的 Android 应用程序发送。

编辑:经过进一步调查,从 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]

【问题讨论】:

    标签: android amazon-web-services jwt aws-api-gateway aws-cognito


    【解决方案1】:

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

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

    【讨论】:

    • 这似乎更接近我的需要,但我不确定它是否是完美的解决方案。在博客文章中,用户利用单个用户属性使用联合身份分配角色。我想根据用户的组来分配这个角色,这样在系统实施后,就像将用户添加到适当的组一样简单(尽管我认为编辑单个属性也相当简单)。是否可以针对用户组而不是属性实施类似的解决方案?
    【解决方案2】:

    我想通了: 这个document(特别是底部)说“如果您为 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"
            }
          }
        }
      ]
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-06-26
      • 2016-11-30
      • 2019-03-04
      • 2018-04-17
      • 2020-06-17
      • 2018-03-21
      • 2018-03-09
      • 2019-09-06
      相关资源
      最近更新 更多