我们已经使用Cognito授权人保护了Chalice端点,并能够通过在Authorization
标头中传递有效的ID令牌来访问它。
以下是我们保护端点的代码:
authorizer = CognitoUserPoolAuthorizer(
'USER_POOL_NAME', provider_arns=['USER_POOL_ARN'])
@app.route('/ping', methods=['GET'], authorizer=authorizer)
def ping() -> str:
return 'The site is up!'
以下是我们如何获得ID令牌,并在我们的用户池中注册和确认电子邮件和密码:
_USER_POOL_ID: Final[str] = ''
_USER_POOL_REGION: Final[str] = 'ap-southeast-1'
_APP_CLIENT_ID: Final[str] = ''
_APP_CLIENT_SECRET: Final[str] = ''
_USERNAME_PASSWORD_AUTH_FLOW = 'USER_PASSWORD_AUTH'
_aws_client = boto3.client('cognito-idp', config=Config(region_name=_USER_POOL_REGION))
def _get_secret_hash(username, cognito_client_id, cognito_secret) -> str:
msg = username + cognito_client_id
dig = hmac.new(str(cognito_secret).encode('utf-8'),
msg=str(msg).encode('utf-8'),
digestmod=hashlib.sha256).digest()
return base64.b64encode(dig).decode()
@app.route('/authenticate', methods=['POST'], content_types=['application/json', 'text/plain'])
def authenticate():
# Callers will pass an email and password to this endpoint.
return _aws_client.initiate_auth(ClientId=_APP_CLIENT_ID,
AuthFlow=_USERNAME_PASSWORD_AUTH_FLOW,
AuthParameters={
'USERNAME': 'email',
'SECRET_HASH': _get_secret_hash('email',
_APP_CLIENT_ID,
_APP_CLIENT_SECRET),
'PASSWORD': 'password'
})
以下是initiate_auth
文件的回复格式:
{
'ChallengeName': 'SMS_MFA'|'SOFTWARE_TOKEN_MFA'|'SELECT_MFA_TYPE'|'MFA_SETUP'|'PASSWORD_VERIFIER'|'CUSTOM_CHALLENGE'|'DEVICE_SRP_AUTH'|'DEVICE_PASSWORD_VERIFIER'|'ADMIN_NO_SRP_AUTH'|'NEW_PASSWORD_REQUIRED',
'Session': 'string',
'ChallengeParameters': {
'string': 'string'
},
'AuthenticationResult': {
'AccessToken': 'string',
'ExpiresIn': 123,
'TokenType': 'string',
'RefreshToken': 'string',
'IdToken': 'string',
'NewDeviceMetadata': {
'DeviceKey': 'string',
'DeviceGroupKey': 'string'
}
}
}
我们使用AuthenticationResult.IdToken
作为Authorization
标头的值。
我们的问题是,当我们试图通过AuthenticationResult.RefreshToken
获得新的ID令牌时,我们得到的是NotAuthorizedException
。
我们已将用户池配置为NOT记住用户设备,因此我们假设在使用刷新令牌时不必传递DEVICE_KEY
。我们还配置了我们的应用程序客户端,使刷新令牌持续180天,而ID和访问令牌仅持续1小时。
我们遵循了本教程,下面是我们的代码:
_REFRESH_TOKEN_AUTH_FLOW = 'REFRESH_TOKEN_AUTH'
@app.route('/refreshTokens', methods=['POST'], content_types=['application/json', 'text/plain'])
def refresh_tokens():
# Callers will pass an email and refresh token to this endpoint.
return _aws_client.initiate_auth(ClientId=_APP_CLIENT_ID,
AuthFlow=_REFRESH_TOKEN_AUTH_FLOW,
AuthParameters={
'USERNAME': 'email',
'SECRET_HASH': _getSecretHash(email,
_APP_CLIENT_ID,
_APP_CLIENT_SECRET),
'REFRESH_TOKEN': 'refresh token'
})
让我们困惑的是,刷新令牌应该是有效的,因为它来自我们获得有效ID令牌的同一响应。
我们做错了什么吗?
事实证明,我们不能使用用户的电子邮件("@"字符会导致问题(。
解决方案是还传递ID令牌,这样我们就可以提取用户的Cognito用户名(这是某种UUID(,并使用它来代替电子邮件。
而且,很明显,这个问题以前已经发布在这里了。