Boto3 `initiate_auth`引发有效刷新令牌的`NotAuthorizedException`



我们已经使用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(,并使用它来代替电子邮件。

而且,很明显,这个问题以前已经发布在这里了。

最新更新