Получение доступа к пользовательским атрибутам Cognito из Lambda
Я использую в своей архитектуре функции Cognito, API Gateway и Lambda.
На клиенте я использую AWS Amplify.API для выполнения запроса, и запрос авторизуется Cognito, как только он попадает на шлюз API. Если запрос авторизован, он передается в функцию Lambda, где мне нужно иметь доступ к зарегистрированному пользователю, который делает запрос, чтобы иметь возможность запускать мою бизнес-логику.
В контексте функции Lambda у меня есть доступ к некоторым переменным среды, CognitoUserPoolId
будучи одним из них.
У меня также есть доступ ко всему, что запрос передается через API, живет в event
.
{
"tok": {
"resource": "/some_resource",
"path": "/some_resource",
"httpMethod": "GET",
"headers": {
"Accept": "application/json",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "en-GB,en;q=0.9,sv;q=0.8,en-US;q=0.7,el;q=0.6,de;q=0.5,el-GR;q=0.4",
"CloudFront-Forwarded-Proto": "https",
"CloudFront-Is-Desktop-Viewer": "true",
"CloudFront-Is-Mobile-Viewer": "false",
"CloudFront-Is-SmartTV-Viewer": "false",
"CloudFront-Is-Tablet-Viewer": "false",
"CloudFront-Viewer-Country": "SE",
"content-type": "application/json",
"Host": "XXXXXX.execute-api.eu-central-1.amazonaws.com",
"origin": "http://localhost:8080",
"Referer": "http://localhost:8080/some_resource",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "cross-site",
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36",
"Via": "2.0 XXXXXXXXXXXX.cloudfront.net (CloudFront)",
"X-Amz-Cf-Id": "XXXXXXXXXXXXXXXXXXXXXXXXTelrg==",
"x-amz-date": "20200527T233945Z",
"x-amz-security-token": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX8fR",
"X-Amzn-Trace-Id": "Root=1-5XXXXa41-730XXXXXXXXXXabe42f1",
"X-Forwarded-For": "XXX.XXX.XX.XXX, XXX.XXX.XXX.XXX",
"X-Forwarded-Port": "443",
"X-Forwarded-Proto": "https"
},
"multiValueHeaders": {
"Accept": [
"application/json"
],
"Accept-Encoding": [
"gzip, deflate, br"
],
"Accept-Language": [
"en-GB,en;q=0.9,sv;q=0.8,en-US;q=0.7,el;q=0.6,de;q=0.5,el-GR;q=0.4"
],
"CloudFront-Forwarded-Proto": [
"https"
],
"CloudFront-Is-Desktop-Viewer": [
"true"
],
"CloudFront-Is-Mobile-Viewer": [
"false"
],
"CloudFront-Is-SmartTV-Viewer": [
"false"
],
"CloudFront-Is-Tablet-Viewer": [
"false"
],
"CloudFront-Viewer-Country": [
"SE"
],
"content-type": [
"application/json"
],
"Host": [
"XXXXXX.execute-api.eu-central-1.amazonaws.com"
],
"origin": [
"http://localhost:8080"
],
"Referer": [
"http://localhost:8080/some_resource"
],
"sec-fetch-dest": [
"empty"
],
"sec-fetch-mode": [
"cors"
],
"sec-fetch-site": [
"cross-site"
],
"User-Agent": [
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36"
],
"Via": [
"2.0 XXXXXXXXXXXX.cloudfront.net (CloudFront)"
],
"X-Amz-Cf-Id": [
"XXXXXXXXXXXXXXXXXXXXXXXXTelrg=="
],
"x-amz-date": [
"20200527T233945Z"
],
"x-amz-security-token": [
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX8fR"
],
"X-Amzn-Trace-Id": [
"Root=1-5XXXXa41-730XXXXXXXXXXabe42f1"
],
"X-Forwarded-For": [
"XXX.XXX.XX.XXX, XXX.XXX.XXX.XXX"
],
"X-Forwarded-Port": [
"443"
],
"X-Forwarded-Proto": [
"https"
]
},
"queryStringParameters": null,
"multiValueQueryStringParameters": null,
"pathParameters": null,
"stageVariables": null,
"requestContext": {
"resourceId": "5qXXXXXXX",
"resourcePath": "/some_resource",
"httpMethod": "GET",
"extendedRequestId": "NNwKXXXXXXXXX=",
"requestTime": "27/May/2020:23:39:45 +0000",
"path": "/dev/some_resource",
"accountId": "18XXXXXXXX",
"protocol": "HTTP/1.1",
"stage": "dev",
"domainPrefix": "XXXXXX",
"requestTimeEpoch": 1590622785474,
"requestId": "XXXXXX-XXXXXX-XXXXXX-XXXXXX-XXXXXX",
"identity": {
"cognitoIdentityPoolId": "eu-central-1:XXXXXX-YYY-YYYY-YYYY-YYYYY",
"accountId": "181606720624",
"cognitoIdentityId": "eu-central-1:XXXXXX-XXXXXX-XXXXXX-XXXXXX-XXXXXX",
"caller": "SOME_STRING_HERE:CognitoIdentityCredentials",
"sourceIp": "XXX.XXX.XXX.XXX",
"principalOrgId": null,
"accessKey": "ACCESS_KEY_HERE",
"cognitoAuthenticationType": "authenticated",
"cognitoAuthenticationProvider": "cognito-idp.eu-central-1.amazonaws.com/eu-central-1_XXXXXX,cognito-idp.eu-central-1.amazonaws.com/eu-central-1_XXXXXX:CognitoSignIn:XXXXXX-XXXXXX-XXXXXX-XXXXXX-XXXXXX",
"userArn": "arn:aws:sts::XXXXXX:assumed-role/amplify-XXXXXX-dev-XXXXXX-authRole/CognitoIdentityCredentials",
"userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36",
"user": "SOME_STRING_HERE:CognitoIdentityCredentials"
},
"domainName": "XXXXXX.execute-api.eu-central-1.amazonaws.com",
"apiId": "XXXXXX"
},
"body": null,
"isBase64Encoded": false
}
}
Имея всю эту информацию под рукой, как я могу получить доступ к пользовательским атрибутам пользователя, который сделал запрос?
Я изучал: Boto3 CognitoIdentityProvider.get_user() и Boto3 CognitoIdentityProvider.admin_get_user(), однако оба требуют либоAccessToken
или userId
и ни то, ни другое недоступно в контексте функции Lambda. Единственный способ, который я придумал, - передать некоторую дополнительную информацию в полезных данных запроса, но на самом деле это не кажется лучшим способом получить атрибуты пользователя.
РЕДАКТИРОВАТЬ
Я не использую настраиваемый авторизатор в API. При настройке API с помощью Amplify я выбрал защищенные пути, и все запросы аутентифицируются / авторизуются в пуле пользователей Cognito. Послеamplify push
Я вижу, что развернутый API AWS_IAM
под Auth
в методе Request для всех ресурсов.
Amplify.API.get
при отправке запроса включает заголовки для авторизации, а именно:
:authority: XXXXXX.execute-api.eu-central-1.amazonaws.com
:method: GET
:path: /dev/some_resource
:scheme: https
accept: application/json
accept-encoding: gzip, deflate, br
accept-language: en-GB,en;q=0.9,sv;q=0.8,en-US;q=0.7,el;q=0.6,de;q=0.5,el-GR;q=0.4
authorization: AWS4-HMAC-SHA256 Credential=ASIAXXXXXXXX/20200528/eu-central-1/execute-api/aws4_request, SignedHeaders=accept;content-type;host;x-amz-date;x-amz-security-token, Signature=b6a7aXXX1c447bXXX...XXX
content-type: application/json
origin: http://localhost:8080
referer: http://localhost:8080/dashboard
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: cross-site
user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36
x-amz-date: 20200528T075958Z
x-amz-security-token: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX...XXXXXXXXXX
Ресурс API интегрирован в функцию Lambda с помощью интеграции Lambda Proxy. Я вижу, что вызывается функция Lambda, поэтому запрос разрешен правильно. Однако все, что у меня есть вevent
в Lambda - это то, что я вставил в первый клип выше.
Учитывая, что это интеграция с прокси, я ожидаю, что все, что находится в контексте в API, должно быть перенаправлено в Lambda за API. Однако я не вижу, как получить доступ к атрибутам user_attributes с помощью любой информации, доступной в Lambda. Есть указатели?
ИЗМЕНИТЬ 2
Теперь я изучаю возможность передачи данных, связанных с пользователем, от клиента с использованием настраиваемых заголовков, возможно, что-то вроде:
'x-user-sub': '58ce94f6-53vd-4s3e-b088-cd6f85s0ff43'
а затем используйте приведенную ниже функцию в моей лямбда-функции, чтобы получить атрибуты пользователя:
c_client = boto3.client('cognito-idp')
response = c_client.admin_get_user(
UserPoolId='eu-central-1_XXXXXX',
Username='58ce94f6-53vd-4s3e-b088-cd6f85s0ff43'
)
Вышеуказанное возвращает:
{
'Username': '58ce94f6-53vd-4s3e-b088-cd6f85s0ff43',
'UserAttributes': [
{
'Name': 'sub',
'Value': '58ce94f6-53vd-4s3e-b088-cd6f85s0ff43'
},
{
'Name': 'email_verified',
'Value': 'true'
},
{
'Name': 'phone_number_verified',
'Value': 'true'
},
{
'Name': 'phone_number',
'Value': '+46XXXXXXXXXX'
},
{
'Name': 'email',
'Value': 'XXXXXX.XXXXX@YYYY.com'
}
],
'UserCreateDate': datetime.datetime(2020, 5, 14, 15, 43, 3, 370000, tzinfo=tzlocal()),
'UserLastModifiedDate': datetime.datetime(2020, 5, 15, 16, 32, 43, 424000, tzinfo=tzlocal()),
'Enabled': true,
'UserStatus': 'CONFIRMED',
'ResponseMetadata': {
'RequestId': 'e441edd4-XXX-46ba-XXX-922691471f2c',
'HTTPStatusCode': 200,
'HTTPHeaders': {
'date': 'Thu, 28 May 2020 12:58:18 GMT',
'content-type': 'application/x-amz-json-1.1',
'content-length': '431',
'connection': 'keep-alive',
'x-amzn-requestid': 'e441edd4-XXX-46ba-XXX-922691471f2c'
},
'RetryAttempts': 0
}
}
Есть ли что-то очевидное, чего мне не хватает с этим решением? Вношу ли я какие-либо риски безопасности, передаваяsub
в шапке клиента?
Любая помощь очень ценится.
1 ответ
Я думаю, что этот пост здесь может помочь вам.
Особенно эта часть:
Авторизатор API Gateway для пользовательских пулов Cognito
API Gateway недавно запустил поддержку Cognito User Pool Authorizer. Если вы используете Cognito User Pool Authorizer, вам не нужно настраивать собственный пользовательский авторизатор для проверки токенов. После того, как ваши методы API настроены с помощью Cognito User Pool Authorizer, вы можете передать токен идентификатора с неистекшим сроком действия в заголовке авторизации в свои методы API. Если это действительный токен идентификатора для пользователя вашего пула пользователей, вы можете получить доступ ко всем утверждениям идентификатора токена в своем API с помощью $context.authorizer.claims.
Например, "$context.authorizer.claims.email" вернет адрес электронной почты пользователя, а "$context.authorizer.claims.sub" вернет вам уникальный идентификатор пользователя. Если срок действия токена ID истек или он недействителен, Cognito User Pool Authorizer отправит вызывающей стороне ответ "Неавторизованный" (401).
Вот несколько примеров того, как переопределить параметры запроса / ответа apigateway.
Здесь вы можете увидеть, как настроить интеграцию и какие данные передаются в API Gateway.
Стоит проверить ссылки для отображения данных и регистрации доступа