検索してもいまいち情報がなく、めちゃくちゃ試行錯誤した記憶があるのでメモしておきます。
■結論
認証Headerに含まれるidToken
を元に、Lambda Function側でGetId - Amazon Cognito Federated Identitiesを叩いてidentityId
を取得する。
例: (Python3:boto3)
event.headers.Authorization
にCognito認証に使ったidToken1が含まれるので、それを利用。
import os
import re
import json
import boto3
COGNITO_USER_POOL_ARN = os.environ.get('COGNITO_USER_POOL_ARN')
COGNITO_IDENTITY_POOL_ID = os.environ.get('COGNITO_IDENTITY_POOL_ID')
def _get_identity_id_from_cognito_idToken(idToken):
"""
cognitoIdentityIdを取得
"""
pattern = 'arn:aws:cognito-idp:(?P<aws_region>.*):(?P<aws_account_id>[0-9]{12}):userpool/(?P<user_pool_id>.*)'
m = re.match(pattern, COGNITO_USER_POOL_ARN)
region = m.group('aws_region')
account_id = m.group('aws_account_id')
user_pool_id = m.group('user_pool_id')
cognito_user_pool_keyname = f"cognito-idp.{region}.amazonaws.com/{user_pool_id}"
# @see https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cognito-identity.html#CognitoIdentity.Client.get_id
response = boto3.client('cognito-identity', region).get_id(
AccountId=account_id,
IdentityPoolId=COGNITO_IDENTITY_POOL_ID,
Logins={
# Amazon Cognito user pool Example:
# "cognito-idp.us-east-1.amazonaws.com/us-east-1_123456789": "${idToken}"
cognito_user_pool_keyname: idToken,
}
)
return response['IdentityId']
def hello(event, context):
idToken = event['headers']['Authorization']
body = {
"message": "Go Serverless v1.0! Your function executed successfully!",
"input": event,
"cognito_id": _get_identity_id_from_cognito_idToken(idToken)
}
response = {
"statusCode": 200,
"body": json.dumps(body)
}
return response
以下にServerless Frameworkでの動作確認用サンプルを用意しました。長々と説明するよりわかりやすいかと思いますので、ご確認いただければ幸いです。
terukizm/sls-lambda-proxy-userpool-auth-sample
IdentityProviderはCognito UserPoolだけに決め打ちしていますが、仮にFacebookとか他のやつも使う場合は、よしなに設定してやる必要があるんじゃないかと思います。(未確認)
■背景
以下の環境において、「Lambda Function側で」「identityIdを」取得する必要があった。
- Cognito + API Gateway + Lambda Function(Serverless Framework) + S3 の標準的なFaaS構成のREST API
- 認証形式は
Cognito UserPools Authorizer
+Lambda Proxy Integration
- slsでCognito認証がかかったREST APIを作る上で、一番簡単に設定できるため
- API Gatewayのcontext mappingをするのはしんどそうだったのでlambda proxyで逃げたかった
-
$ curl -sS -X GET -H "Authorization: ${idToken}" "${ENDPOINT}/sample"
で動作確認済
- slsでCognito認証がかかったREST APIを作る上で、一番簡単に設定できるため
- REST APIの認証を通したユーザのみ、認証したidentity_idを含むパスの下のファイルを処理させたい
-
Amplify Storageの保存形式、
s3://{bucket_name}/private/{identity_id}/
以下のファイル- Amplify SDKで作ったWebAppからファイルアップロードしたりすると上記のパスに格納される
-
Amplify Storageの保存形式、
- 認証形式は
以下のような方法はあったが、「Lambda Integration」もしくは「AWS_IAM」方式だったり、「eventに値が入ってこない」2といった理由でidentityIdを取得することができなかった。
- 参考:
なお、Amplify SDKによってブラウザ側ではidentityIdを取得することができるが、それをバックエンド側にパラメータとして直接渡すのはためらわれた。このidentityIdが取得できない話は調べたところかなり昔から議論されてたっぽいが、おそらく正攻法ではいまのところIdentityIdを取得できないのではないかと思う。(2019/07時点)
- 参考:
というわけで、おとなしくLambda Function側でAWS APIを直接叩くことにした。get_userとか色々あったが、identity_idが取得でき、かつidTokenで処理できるものはget_idしかなかったのでこれを利用した。
(参考) Serverless Framework(sls)でのCognito UserPools Authorizer + Lambda Proxy Integration設定
- Serverless Framework
functions:
hello:
handler: handler.hello
events:
- http:
path: hello
method: get
#integration: lambda-proxy (default)
cors:
origin: '*'
headers:
- Authorization
authorizer:
name: authorizer
arn: ${env:COGNITO_USER_POOL_ARN}
ご参考になれば…