[英]How can I get a JWT Access Token from AWS Cognito as admin in Python with boto3?
I am trying to write an API test in Python for my web service.我正在尝试用 Python 为我的 Web 服务编写 API 测试。 I would like to avoid using the password of the test user from my AWS Cognito pool.
我想避免使用我的 AWS Cognito 池中测试用户的密码。 My strategy for this, and let me know if there's a better way here, is to require that the API test be run with Cognito admin privileges.
我对此的策略(如果有更好的方法请告诉我)是要求 API 测试以 Cognito 管理员权限运行。 Then use the boto3 library to get the JWT AccessToken for the user which I will add to the header of every request for the API test.
然后使用 boto3 库为用户获取 JWT AccessToken,我将其添加到 API 测试的每个请求的标头中。
The documentation doesn't seem to give me a way to get the AccessToken.该文档似乎没有给我一种获取 AccessToken 的方法。 I'm trying to use this here: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cognito-idp.html#CognitoIdentityProvider.Client.admin_initiate_auth
我想在这里使用它: https : //boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cognito-idp.html#CognitoIdentityProvider.Client.admin_initiate_auth
admin_initiate_auth needs one of three auth modes. admin_initiate_auth 需要三种身份验证模式之一。 USER_PASSWORD_AUTH requires the password, USER_SRP_AUTH requires a client secret, CUSTOM_AUTH requires a secret hash.
USER_PASSWORD_AUTH 需要密码,USER_SRP_AUTH 需要客户端密码,CUSTOM_AUTH 需要密码哈希。 I'm hoping to find a way to write this script so that I just need to have the right IAM privileges and not need to check in a public test user password.
我希望找到一种编写此脚本的方法,以便我只需要拥有正确的 IAM 权限,而无需签入公共测试用户密码。
Or... I guess... be told that this is not a great way to be doing this and that another way is more appropriate.或者...我猜...被告知这不是一个很好的方式来做这件事,而另一种方式更合适。 The end goal is to have an API black box test for a service that is secured by Cognito.
最终目标是对 Cognito 保护的服务进行 API 黑盒测试。
For my own project, I was also thinking a similar strategy to test Cognito-protected APIs.对于我自己的项目,我也在考虑类似的策略来测试受 Cognito 保护的 API。
I think making a temporary user with a random password for each test run is a fair approach.我认为为每次测试运行使用随机密码创建一个临时用户是一种公平的方法。
To create a user from command line, I think there are simpler cognito API calls, which are sign-up and admin-confirm-sign-up provided in cognito-idp CLI tool.要从命令行创建用户,我认为有更简单的 cognito API 调用,即cognito-idp CLI 工具中提供的sign-up和admin-confirm-sign-up 。 With this, you can skip the steps to resolve the challenges and the user is ready to use.
有了这个,您可以跳过解决挑战的步骤,用户就可以使用了。
If you want to use boto3, here is a simple function to create a new user:如果你想使用 boto3,这里有一个简单的函数来创建一个新用户:
def create_user(username: str, password: str,
user_pool_id: str, app_client_id: str) -> None:
client = boto3.client('cognito-idp')
# initial sign up
resp = client.sign_up(
ClientId=app_client_id,
Username=username,
Password=password,
UserAttributes=[
{
'Name': 'email',
'Value': 'test@test.com'
},
]
)
# then confirm signup
resp = client.admin_confirm_sign_up(
UserPoolId=user_pool_id,
Username=username
)
print("User successfully created.")
Then, to obtain JWT,然后,要获得 JWT,
def authenticate_and_get_token(username: str, password: str,
user_pool_id: str, app_client_id: str) -> None:
client = boto3.client('cognito-idp')
resp = client.admin_initiate_auth(
UserPoolId=user_pool_id,
ClientId=app_client_id,
AuthFlow='ADMIN_NO_SRP_AUTH',
AuthParameters={
"USERNAME": username,
"PASSWORD": password
}
)
print("Log in success")
print("Access token:", resp['AuthenticationResult']['AccessToken'])
print("ID token:", resp['AuthenticationResult']['IdToken'])
If the API test must be secured using Cognito, you're always going to need some kind of password.如果必须使用 Cognito 保护 API 测试,您总是需要某种密码。 The best way I can think of to avoid storing it is to create a temporary user before running the test suite, and then delete it when finished.
我能想到的避免存储它的最好方法是在运行测试套件之前创建一个临时用户,然后在完成后将其删除。
You can use AdminCreateUser to accomplish this.您可以使用AdminCreateUser来完成此操作。 Generate a new password at runtime and pass it as the temporary password for the user, along with
SUPRESS
specified for MessageAction
.在运行时生成一个新密码并将其作为用户的临时密码以及为
MessageAction
指定的SUPRESS
。 The temporary password is good for one login, which is all you need in this use case.临时密码适用于一次登录,这是您在此用例中所需要的全部内容。 Then you can run
AdminInitiateAuth
with the ADMIN_NO_SRP_AUTH
auth mode, specifying your generated password.然后,您可以使用
ADMIN_NO_SRP_AUTH
身份验证模式运行AdminInitiateAuth
,并指定您生成的密码。 Cleanup with AdminDeleteUser after the tests have finished.测试完成后使用AdminDeleteUser进行清理。
To expand on @xlem's answer and @mmachenry's comment with an example:用一个例子来扩展@xlem 的回答和@mmachenry 的评论:
Using the Cognito client of AWS SDK使用 AWS SDK 的 Cognito 客户端
@pytest.fixture()
def given_a_new_user_token(request):
client = boto3.client("cognito-idp")
user_pool_id = "eu-west-2_xxxxxxxxx"
username = f"test_user-{uuid.uuid4().hex}"
temp_pwd = f"{uuid.uuid4().hex}"
ci_test_client_id, secret_hash = get_cognito_secrets(username)
user_response = client.admin_create_user(
UserPoolId=user_pool_id,
Username=username,
UserAttributes=[
{"Name": "email", "Value": f"{username}@example.com"},
],
TemporaryPassword=temp_pwd,
ForceAliasCreation=False,
MessageAction="SUPPRESS",
DesiredDeliveryMediums=[
"EMAIL",
],
)
assert user_response["ResponseMetadata"]["HTTPStatusCode"] == 200
def delete_user():
client.admin_delete_user(
UserPoolId=user_pool_id,
Username=username,
)
request.addfinalizer(delete_user)
set_pwd_response = client.admin_set_user_password(
UserPoolId=user_pool_id,
Username=username,
Password=temp_pwd,
Permanent=True
)
assert set_pwd_response["ResponseMetadata"]["HTTPStatusCode"] == 200
auth_info = client.admin_initiate_auth(
UserPoolId=user_pool_id,
ClientId=ci_test_client_id,
AuthFlow="ADMIN_NO_SRP_AUTH",
AuthParameters={
"USERNAME": username,
"PASSWORD": temp_pwd,
"SECRET_HASH": secret_hash,
},
)
assert auth_info["ResponseMetadata"]["HTTPStatusCode"] == 200
return auth_info["AuthenticationResult"]["AccessToken"]
def get_cognito_secrets(username: str) -> Tuple[str, str]:
ci_test_client_id = "client_id_xxxxxxx"
ci_test_client_secret = "client_secret_xxxxx"
# convert str to bytes
key = bytes(ci_test_client_secret, 'latin-1')
msg = bytes(username + ci_test_client_id, 'latin-1')
new_digest = hmac.new(key, msg, hashlib.sha256).digest()
secret_hash = base64.b64encode(new_digest).decode()
return ci_test_client_id, secret_hash
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.