AWS网关响应未传回401未经授权的授权者上下文

时间:2020-08-03 16:47:03

标签: amazon-web-services go aws-lambda aws-api-gateway aws-sam

我正在为某些lamba和授权者使用AWS SAM。确认API网关正在使用Postman(而不是控制台中的测试功能)使用授权者,并确认日志显示在授权者功能中。尚未对授权者进行任何花哨的操作,只是在令牌无效或丢失的情况下尝试传递回动态生成的WWW-Authenticate标头(现在仅作为示例来处理Authorization: bearer [承载后无文本])

整个周末,我一直在撞墙,似乎无法让授权者传递上下文的任何值。我已经尝试了多种方法,例如$context.authorizer.challenge$event.requestContext.authorizer.challenge,将它们放在正文和标头中,甚至尝试了$context.authorizer.principalId也不产生任何东西。

处理请求的授权者代码:

func (a *authHandler) Handler(request events.APIGatewayCustomAuthorizerRequest) (events.APIGatewayCustomAuthorizerResponse, error) {
    fmt.Printf("checking auth\n")

    token := request.AuthorizationToken
    tokenSlice := strings.Split(token, " ")
    var bearerToken string
    if len(tokenSlice) > 1 {
        bearerToken = tokenSlice[len(tokenSlice)-1]
    }
    if bearerToken == "" {
        fmt.Printf("about to return policy for empty token")
        // I've tried multiple combinations of Deny/Allow, context maps, error response, etc. 
        return generatePolicy("user", "Deny", request.MethodArn, map[string]interface{}{"challenge": "testing"}), errors.New("Unauthorized")
    }

    return generatePolicy("user", "Allow", request.MethodArn, map[string]interface{}{"name": bearerToken}), nil
}

func generatePolicy(principalID, effect, resource string, context map[string]interface{}) events.APIGatewayCustomAuthorizerResponse {
    authResponse := events.APIGatewayCustomAuthorizerResponse{PrincipalID: principalID}

    if effect != "" && resource != "" {
        authResponse.PolicyDocument = events.APIGatewayCustomAuthorizerPolicy{
            Version: "2012-10-17",
            Statement: []events.IAMPolicyStatement{
                {
                    Action:   []string{"execute-api:Invoke"},
                    Effect:   effect,
                    Resource: []string{resource},
                },
            },
        }
    }
    authResponse.Context = context
    return authResponse
}

这只是一个占位符,如果收到Unauthorized之类的空令牌承载,应该返回Authorization: bearer

template.yaml:

Resources:
  BaseApi:
      Type: AWS::Serverless::Api
      Properties:
        StageName: Prod
        Auth:
          DefaultAuthorizer: TokenAuthorizer
          Authorizers:
            TokenAuthorizer:
              FunctionPayloadType: TOKEN
              FunctionArn: !GetAtt AuthTokenFunction.Arn
              Identity:
                Headers:
        GatewayResponses:
          UNAUTHORIZED:
            StatusCode: 401
            ResponseParameters:
              Headers:
                Access-Control-Expose-Headers: "'WWW-Authenticate'"
                WWW-Authenticate: "context.authorizer.challenge"
                Principal-ID: "context.authorizer.principalId"
            ResponseTemplates:
              "application/json": '{ "message": $context.error.messageString, "testing": "test", "challenge": $context.authorizer.challenge, "challengeEvent": $event.requestContext.authorizer.challenge }'

  AuthTokenFunction:
    Type: AWS::Serverless::Function 
    Properties:
      CodeUri: cmd/lambda/auth
      Handler: services-auth
      Runtime: go1.x
      Role: !GetAtt ParameterAuthTokenFunctionRole.Arn
      Tracing: Active
      Environment:
        Variables:
          APP_CONFIG_PATH: 'parameterAuthToken'
          AWS_XRAY_TRACING_NAME: 'AuthTokenFunction'

  ParameterAuthTokenFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          -
            Effect: Allow
            Principal:
              Service:
                - 'lambda.amazonaws.com'
            Action:
              - 'sts:AssumeRole'
      ManagedPolicyArns:
        - 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
      Policies:
        -
          PolicyName: 'ParameterAuthTokenParameterAccess'
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              -
                Effect: Allow
                Action:
                  - 'ssm:GetParameter*'
                #Resource: !Sub 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/prod/parameterAuthToken*'
                Resource: '*'
        -
          PolicyName: 'ParameterAuthTokenXRayAccess'
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              -
                Effect: Allow
                Action:
                  - 'xray:PutTraceSegments'
                  - 'xray:PutTelemetryRecords'
                Resource: '*'

  ParameterAuthTokenEncryptionKeyAlias:
    Type: AWS::KMS::Alias
    Properties:
      AliasName: 'alias/ParameterAuthTokenKey'
      TargetKeyId: !Ref ParameterAuthTokenEncryptionKey

  ParameterAuthTokenEncryptionKey:
    Type: AWS::KMS::Key
    Properties:
      Description: 'Encryption key for secret config values for the Parameter Auth Token'
      Enabled: True
      EnableKeyRotation: False
      KeyPolicy:
        Version: '2012-10-17'
        Id: 'key-default-1'
        Statement:
          -
            Sid: 'Allow administration of the key & encryption of new values'
            Effect: Allow
            Principal:
              AWS: 
                - !Sub 'arn:aws:iam::${AWS::AccountId}:user/anthony-local'
            Action:
              - 'kms:Create*'
              - 'kms:Encrypt'
              - 'kms:Describe*'
              - 'kms:Enable*'
              - 'kms:List*'
              - 'kms:Put*'
              - 'kms:Update*'
              - 'kms:Revoke*'
              - 'kms:Disable*'
              - 'kms:Get*'
              - 'kms:Delete*'
              - 'kms:ScheduleKeyDeletion'
              - 'kms:CancelKeyDeletion'
            Resource: '*'
          -
            Sid: 'Allow use of the key'
            Effect: Allow
            Principal:
              AWS: !GetAtt ParameterAuthTokenFunctionRole.Arn
            Action:
              - 'kms:Encrypt'
              - 'kms:Decrypt'
              - 'kms:ReEncrypt*'
              - 'kms:GenerateDataKey*'
              - 'kms:DescribeKey'
            Resource: '*'

  GhostFunction:
    Type: AWS::Serverless::Function 
    Properties:
      CodeUri: cmd/lambda/ghost
      Handler: services-ghost
      Runtime: go1.x
      Tracing: Active 
      Events:
        GetGhost:
          Type: Api 
          Properties:
            Path: /ghost/{id}
            Method: GET
            RestApiId: !Ref BaseApi
            Auth:
              Authorizer: TokenAuthorizer


响应:

401 Unauthorized

Headers: 
  WWW-Authenticate: 
  Principal-ID:
  Access-Control-Expose-Headers: WWW-Authenticate
  x-amzn-ErrorType: UnauthorizedException 

Body:
{ "message": "Unauthorized", "testing": "test", "challenge": , "challengeEvent":  }

以及一些控制台屏幕截图:

enter image description here enter image description here enter image description here

有任何线索吗?我将放弃w / authorizer,并在每个lambda函数本身中添加自己的逻辑。似乎更容易进行诸如应用标头的操作。

1 个答案:

答案 0 :(得分:0)

扔掉毛巾,说这目前是不可能的,至少对于golang来说是不可能的。

如果我没有返回错误,则能够显示上下文。因此上下文出现在这里:

return generatePolicy("user", "Deny", request.MethodArn, map[string]interface{}{"challenge": "testing"}), nil

当我在aws控制台中调用auth lambda时,可以看到它。问题似乎是您不能同时传递策略信息和错误。因此上下文永远不会出现在响应模板中。

此外,如此thread所示,其他人也无法通过其中任何一个获取自定义的WWW-Authenticate标头。使用golang库,似乎都不可能通过任何一种获得自定义HTTP错误代码。因此,我无法将402 Payment RequiredWWW-Authenticate标头一起使用的计划。


解决方案:

保留授权者,在其中处理auth逻辑,但将质询传递给lambda。

return generatePolicy("user", "Allow", request.MethodArn, map[string]interface{}{"challenge": "testing"}), nil

并通过$event.requestContext.authorizer.challenge在上下文中读取我要与此授权程序耦合的每个lambda,并以这种方式处理标头的402响应。绝对不理想,但是在授权者可以被定制得足以进行标准化之前,我认为这必须是中间立场。