使用 Cognito 用户池调用 AWS APIGateway
Posted
技术标签:
【中文标题】使用 Cognito 用户池调用 AWS APIGateway【英文标题】:Calling AWS APIGateway with a Cognito user pool 【发布时间】:2021-09-05 19:59:19 【问题描述】:我使用 AWS APIGateway 在我的 AWS 账户上配置了一个 REST API。 REST API 使用 Cognito Authorizer 进行身份验证。
我正在使用 Python 脚本与 API 进行交互。在这个 Python 脚本中,我设法验证了一个用户。为方便起见,以下是我如何处理用户需要更新其密码(首次身份验证)的情况:
region = 'eu-west-1'
cognito = boto3.client('cognito-idp', region)
clientId = 'abcdefghijk27261819'
username = "myUsername"
password = "myPassword"
response = cognito.initiate_auth(AuthFlow='USER_PASSWORD_AUTH', AuthParameters="USERNAME": username,
"PASSWORD": password, ClientId = clientId)
try :
response['ChallengeName']
response = cognito.respond_to_auth_challenge(ClientId=clientId, ChallengeName=response['ChallengeName'], Session=response['Session'],
ChallengeResponses="USERNAME": username,
"NEW_PASSWORD": "myNewPassword")
except KeyError:
pass
# store credentials
accessToken = response['AuthenticationResult']['AccessToken']
idToken = response['AuthenticationResult']['IdToken']
我能够通过 APIGateway 控制台验证身份令牌 idToken
是 API 所期望的:
但是,即使令牌看起来没问题,我也无法调用 API。
我尝试了两个电话:
HOST = "https://<APIID>.execute-api.eu-west-1.amazonaws.com"
STAGE = "dev"
BASE_URL = f"HOST/STAGE"
r = requests.post(f"BASE_URL/S3Operations/CreateSimulationBucket", headers="Authorization": idToken) # the call I want
r = requests.post(f"BASE_URL", headers="Authorization": idToken) # the test call
两个调用都返回错误:
# first call
'Content-Type': 'application/json', 'Content-Length': '23', 'Connection': 'keep-alive', 'Date': 'Tue, 22 Jun 2021 07:36:47 GMT', 'x-amzn-RequestId': 'd87ee0c8-d004-4199-8974-1727830c9f25', 'x-amzn-ErrorType': 'ForbiddenException', 'x-amz-apigw-id': 'BUPqaFQcDoEF3Xg=', 'X-Cache': 'Error from cloudfront', 'Via': '1.1 5e828cc6ff056cb59ec35c3467ec45f5.cloudfront.net (CloudFront)', 'X-Amz-Cf-Pop': 'AMS1-C1', 'X-Amz-Cf-Id': '6sbLUxCEmUWQYTWD6BHAommN6eTVieLmL-6XOJh30fNW-IhdcztJIQ=='
# second call
'Content-Type': 'application/json', 'Content-Length': '1379', 'Connection': 'keep-alive', 'Date': 'Tue, 22 Jun 2021 07:45:19 GMT', 'x-amzn-RequestId': 'b69a442e-3b3c-4447-bec8-8eff344ac7cb', 'x-amzn-ErrorType': 'IncompleteSignatureException', 'x-amz-apigw-id': 'BUQ6bFLqjoEFjhA=', 'X-Cache': 'Error from cloudfront', 'Via': '1.1 043fc2faaa02eeb59193e3fa300adb6b.cloudfront.net (CloudFront)', 'X-Amz-Cf-Pop': 'AMS1-C1', 'X-Amz-Cf-Id': '8btFflDUE2I7LxiKbvnmreOE-oKrIwbudgORbWaRrASNUmIPdYtwYQ=='
我的问题是:
-
为什么第一个调用返回
ForbiddenException
?我相信我的路径配置正确:
为什么第二个调用返回IncompleteSignatureException
?
进展
对于问题 1。实际上,我可以在 AWS 文档页面 here 上找到信息,并且能够理解我只是调用了未经授权的方法。
对于问题 2。通过使用 API Gateway 响应,我能够更准确地诊断问题。如下所示,我为所有可能的问题制作了自定义消息
我发现问题出在未提供的 API 密钥上。事实上,我的 API 有一个 UsagePlan,因此需要一个 API Key。
我已修改我的脚本以获取此 API 密钥,如下所示
session = boto3.Session(profile_name='my-profile')
region = 'eu-west-1' # or 'us-west-1' or whatever
cognito = session.client('cognito-idp', region)
apigw = session.client('apigateway', region)
# Cognito information
cognito_userpool_client_id = 'abcdefghijk27261819'
cognito_username = 'my_username'
cognito_password = 'my_password|'
# API information
apigw_id = 'my_api_id'
def get_api_key(apigw_client, apigw_id):
return apigw_client.get_api_key(apiKey=apigw_id, includeValue=True)['value']
response = cognito.initiate_auth(AuthFlow='USER_PASSWORD_AUTH', AuthParameters="USERNAME": cognito_username,
"PASSWORD": cognito_password, ClientId = cognito_userpool_client_id)
# store credentials
accessToken = response['AuthenticationResult']['AccessToken']
idToken = response['AuthenticationResult']['IdToken']
# Call API
HOST = "https://<APIID>.execute-api.eu-west-1.amazonaws.com"
STAGE = "dev"
BASE_URL = f"HOST/STAGE"
r = requests.post(f"BASE_URL/S3Operations/CreateSimulationBucket", headers="Authorization": idToken, "x-api-key": get_api_key(apigw, apigw_id))
尽管进行了这些修改,我仍然收到相同的错误:
'Content-Type': 'application/json', 'Content-Length': '23', 'Connection': 'keep-alive', 'Date': 'Tue, 22 Jun 2021 15:24:27 GMT', 'x-amzn-RequestId': '29743683-dbaf-4d60-bbae-8f2581e1c45a', 'x-amzn-ErrorType': 'ForbiddenException', 'x-amz-apigw-id': 'BVUKxF9FjoEFgbA=', 'issue': 'Invalid API Key', 'X-Cache': 'Error from cloudfront', 'Via': '1.1 bdba42cf1410fb617eeb4ffd3e0b9cb7.cloudfront.net (CloudFront)', 'X-Amz-Cf-Pop': 'AMS1-C1', 'X-Amz-Cf-Id': 'fMbbQSLlDd7OHErlPCoVNJRZM_P9mq5DYxgsfkJVy4sKHw9lijFYeA=='
【问题讨论】:
对于第一个问题:我其实已经找到原因了:因为这是一条禁止的路径。可以在 AWS 文档 (aws.amazon.com/premiumsupport/knowledge-center/…) 上找到有用的信息以了解这些消息。 对于第二个问题:我能够使用 API Gateway 响应找到更多详细信息。我为所有可能的事件添加了自定义响应,并发现问题出在我的 API 使用 Cognito 和使用计划(即我没有提供的 API 密钥)这一事实。我修改了我的脚本以提供 API 密钥。但我仍然遇到问题......相应地更新了问题 【参考方案1】:你在 api 网关中部署了 api 吗?在使用 python 之前,您是否尝试过在 postman 中构建 api 调用?
【讨论】:
API 被部署为我的工作流程的一部分。我已经尝试重新部署它。没有变化。【参考方案2】:好的。经过一番分析,我能够理解这个问题。
对于问题 1。我可以在 AWS 文档页面 here 上找到信息,并且能够理解我只是调用了未经授权的方法,即调用 API 的根未被授权,因为根没有注册的方法/资源。
对于问题 2。我花了一些时间才找到问题的核心。事实上,通过制作自定义网关响应,我知道问题来自Invalid API Key
。但这实际上令人困惑,因为真正的问题来自无效的授权者!
在解决问题之前,这是我用来生成资源的 SAM 模板:
API:
Type: AWS::Serverless::Api
Properties:
StageName: !Sub $Env
# Authentication on the API will be performed via a Key
Auth:
Authorizers:
CognitoAuthorizer:
UserPoolArn: !GetAtt CognitoUserPool.Arn
ApiKeyRequired: true
# this creates a key, a usage plan and associate them
UsagePlan:
CreateUsagePlan: PER_API
UsagePlanName: !Sub $Project-$Env-UsagePlan
所以...我定义了一个 Cognito Authorizer ...但我并没有实际使用它。我能够在 AWS API Gateway 控制台中识别它。进入资源页面,我到达了我想要调用的路径,然后单击 Method
框:
然后我到达如下所示的页面。 在更正之前,Authorization
字段为 None
,这导致了我的问题
确实...我正在使用授权标头进行 API 调用,而方法本身没有设置授权。现在我发现 AWS 不发送更详细的错误消息而只发送 Invalid API key
令人困惑。我花了一段时间才明白是什么导致了这个问题,因为我认为该方法上有一个 Authorizer。
为了解决这个问题,我不得不修改我的 SAM 模板,如下所示,这个修改允许默认为 API 的所有方法设置 Cognito Authorizer,并允许我成功调用:
API:
Type: AWS::Serverless::Api
Properties:
StageName: !Sub $Env
# Authentication on the API will be performed via a Key
Auth:
Authorizers:
CognitoAuthorizer:
UserPoolArn: !GetAtt CognitoUserPool.Arn
DefaultAuthorizer : CognitoAuthorizer
ApiKeyRequired: true
# this creates a key, a usage plan and associate them
UsagePlan:
CreateUsagePlan: PER_API
UsagePlanName: !Sub $Project-$Env-UsagePlan
注意添加记录DefaultAuthorizer : CognitoAuthorizer
希望这对其他人有帮助!
【讨论】:
以上是关于使用 Cognito 用户池调用 AWS APIGateway的主要内容,如果未能解决你的问题,请参考以下文章
如何通过 Cognito 用户 ID 限制访问 AWS API Gateway 端点
如何将 AWS Cognito 用户池用户存储在数据库(例如 DynamoDB)中?
具有 Cognito 用户池授权方的 AWS SAM API
使用 Lambda 授权方的 AWS Cognito 和 API 网关