こんにちは。
インフラ・エンジニアです。
AWS API Gateway のIAM認証を試してみました。
IAM認証で使用するロールは、COGNITOユーザープールに作成したグループに設定し、
このロールでAPIが実行できるか試してみました。
検証環境の構築は、CloudFormation でやってみたので、手順を紹介します。
ここでは、ソースなどの細かい説明は割愛していますので、
参考にしたサイトやAWSマニュアルの説明など、他のよくできたページを見てください。
■前提
・作業はMACで実施
・awscliが作業用のMACにインストール済み。 コマンド実行時は profileを指定。
$ python --version
Python 2.7.15
$ aws --version
aws-cli/1.16.17 Python/2.7.15 Darwin/17.7.0 botocore/1.12.7
■参考にしたサイト
・https://qiita.com/ykarakita/items/f58f861165db5b81aea0
・https://aws.amazon.com/jp/blogs/mobile/building-fine-grained-authorization-using-amazon-cognito-user-pools-groups/
・https://forums.aws.amazon.com/thread.jspa?threadID=255584
#1. API Gateway の構築
API Gatewayを構築します。
IAM認証のGETメソッドをMOCで1つ作成して、デプロイします。
GETメソッドのレスポンスはJSONです。
CloudFormationのソースは以下の通り。
AWSTemplateFormatVersion: 2010-09-09
Description: API Gateway Sandbox
Resources:
# Rest API
RestAPI:
Type: 'AWS::ApiGateway::RestApi'
Properties:
Description: sandbox
Name: !Ref 'AWS::StackName'
EndpointConfiguration:
Types:
- REGIONAL
# GET on the /. MOCK json response
RestAPIMockGET:
Type: 'AWS::ApiGateway::Method'
Properties:
ApiKeyRequired: false
AuthorizationType: AWS_IAM
HttpMethod: GET
Integration:
IntegrationHttpMethod: GET
PassthroughBehavior: WHEN_NO_MATCH
Type: MOCK
RequestTemplates:
application/json: |
{
"statusCode": 200
}
IntegrationResponses:
- StatusCode: 200
ResponseParameters:
method.response.header.Content-Type: "'application/json'"
ResponseTemplates:
application/json: |
{
"message": "hello!"
}
MethodResponses:
- StatusCode: 200
ResponseModels:
text/html: Empty
ResponseParameters:
method.response.header.Content-Type: true
ResourceId: !GetAtt RestAPI.RootResourceId
RestApiId: !Ref RestAPI
DependsOn:
- RestAPI
# Deploy
ApiDeployment:
Type: 'AWS::ApiGateway::Deployment'
Properties:
RestApiId: !Ref RestAPI
Description: sandbox deployment
DependsOn:
- RestAPIMockGET
# create stage01
ApiStage01:
Type: 'AWS::ApiGateway::Stage'
Properties:
RestApiId: !Ref RestAPI
DeploymentId: !Ref ApiDeployment
StageName: stage1
MethodSettings:
- ResourcePath: /*
HttpMethod: '*'
aws-cli で、このソースコードをチェックします。
$ aws cloudformation validate-template --profile sandbox --template-body file://`pwd`/SandboxApiGateway.yaml
{
"Description": "API Gateway Sandbox",
"Parameters": []
}
エラーが表示されなければ、aws-cli でスタックを作成します。
スタック名がAPIGatewayのAPI名になります。
$ aws cloudformation create-stack --profile sandbox --stack-name SandboxApi --template-body file://`pwd`/SandboxApiGateway.yaml
AWSコンソールでCloudFormationの実行結果を確認します。 [状況]が CREATE_COMPLETE であればOKです。
AWSコンソールでAPI Gatewayのエンドポイント確認します。
curlコマンドでAPIを実行してみます。
認証してないのでエラーになります。
以下のようにエラーメッセージが表示されたらOKです。
$ curl -XGET https://2037kuo2d5.execute-api.ap-northeast-1.amazonaws.com/stage1/
{"message":"Missing Authentication Token"}
#2. COGNITOの構築
##2.1. 動的マッピング用の Lambda関数を登録
COGNITOのIDプールをCloudFormationで構築しますが、
認証プロバイダの設定で、CloudFormationのカスタムリソースを使用します。
ここでは、カスタムリソースで使用するLambda関数を登録します。
CloudFormationのソースコードは以下の通り。
AWSTemplateFormatVersion: "2010-09-09"
Description: Custom resource for cognito role mapping.
Resources:
DynamicMapTransformLambda:
Type: AWS::Lambda::Function
Properties:
Description: Transform to generate maps with computed keys
Handler: index.handler
Role: !GetAtt LambdaExecutionRole.Arn
Runtime: nodejs6.10
Code:
ZipFile: !Sub |
const { send, SUCCESS, ERROR } = require('cfn-response');
exports.handler = (event, context, callback) => {
console.log(JSON.stringify(event, null, 2));
const { RequestType, ResourceProperties: props = { } } = event;
const { Entries: entries = [ ], AttributeName: attName } = props;
switch(RequestType) {
case 'Create':
case 'Update':
const result = { };
for (let i = 0; i < entries.length; i++) {
const { Key, Value } = entries[i]
if (Key) {
result[Key] = Value
}
}
send(event, context, SUCCESS, { [attName]: result });
break;
case 'Delete':
send(event, context, SUCCESS);
break;
}
};
LambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: "sts:AssumeRole"
Principal:
Service: lambda.amazonaws.com
Path: "/"
Policies:
- PolicyName: root
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: "logs:*"
Resource: "arn:aws:logs:*:*:*"
Outputs:
TransformLambda:
Description: The CloudFormation Custom ServiceToken Function Arn
Value: !GetAtt DynamicMapTransformLambda.Arn
Export:
Name: TransformLambda
aws-cli でスタックを作成します。
$ aws cloudformation create-stack --profile sandbox --stack-name SandboxCognitoMap --template-body file://`pwd`/SandboxCognitoMapping.yaml --capabilities CAPABILITY_IAM
##2.2. COGNITOのIDプール、ユーザプール構築とユーザ登録
ここでは、COGNITOのIDプール、ユーザプールを作成し、ユーザ/グループを登録します。
また、APIGatewayのAPI実行を許可するロールを作成して、グループに紐つけます。
ユーザの作成時に使用するメールアドレスは、メール受信可能なものを使用します。
あとで、仮パスワードをメールするので、仮パスワードを変更してユーザをアクティブにします。
CloudFormationのソースコードは以下の通り。
AWSTemplateFormatVersion: "2010-09-09"
Description: "Cognito Identity Pool and User Pool."
Resources:
# Cognito User Pool
UserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: !Sub "${AWS::StackName}Users"
AliasAttributes:
- email
AutoVerifiedAttributes:
- email
Schema:
- AttributeDataType: "String"
Name: email
Required: True
UserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
ClientName: !Sub "${AWS::StackName}Users-client"
GenerateSecret: false
ExplicitAuthFlows:
- ADMIN_NO_SRP_AUTH
RefreshTokenValidity: 7
UserPoolId:
Ref: UserPool
# Cognito Identity Pool
IdentityPool:
Type: AWS::Cognito::IdentityPool
Properties:
AllowUnauthenticatedIdentities: true
IdentityPoolName: !Sub "${AWS::StackName}Id"
CognitoIdentityProviders:
- ClientId: !Ref UserPoolClient
ProviderName: !GetAtt UserPool.ProviderName
UnauthenticatedRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: "sts:AssumeRoleWithWebIdentity"
Principal:
Federated: cognito-identity.amazonaws.com
Condition:
StringEquals:
"cognito-identity.amazonaws.com:aud": !Ref IdentityPool
ForAnyValue:StringLike:
"cognito-identity.amazonaws.com:amr": unauthenticated
Path: "/"
Policies:
- PolicyName: cognito
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- mobileanalytics:PutEvents
- cognito-sync:*
Resource:
- "*"
- PolicyName: api
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Deny
Action:
- execute-api:Invoke
Resource:
- "*"
AuthenticatedRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: "sts:AssumeRoleWithWebIdentity"
Principal:
Federated: cognito-identity.amazonaws.com
Condition:
StringEquals:
"cognito-identity.amazonaws.com:aud": !Ref IdentityPool
ForAnyValue:StringLike:
"cognito-identity.amazonaws.com:amr": authenticated
Path: "/"
Policies:
- PolicyName: cognito
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- mobileanalytics:PutEvents
- cognito-sync:*
- cognito-identity:*
Resource:
- "*"
IdentityPoolRoleAttachmentMapping:
Type: Custom::DynamicMapTransform
Properties:
ServiceToken: !ImportValue TransformLambda
AttributeName: RoleMappings
Entries:
- Key: !Sub ${UserPool.ProviderName}:${UserPoolClient}
Value:
Type: Token
AmbiguousRoleResolution: Deny
RoleAttachment:
Type: AWS::Cognito::IdentityPoolRoleAttachment
Properties:
IdentityPoolId: !Ref IdentityPool
Roles:
unauthenticated: !GetAtt UnauthenticatedRole.Arn
authenticated: !GetAtt AuthenticatedRole.Arn
RoleMappings: !GetAtt IdentityPoolRoleAttachmentMapping.RoleMappings
# Cognito User / Group
UserPoolGroup:
Type: AWS::Cognito::UserPoolGroup
Properties:
GroupName: exgroup
RoleArn: !GetAtt GroupRole.Arn
UserPoolId: !Ref UserPool
Precedence: 0
UserPoolUser:
Type: AWS::Cognito::UserPoolUser
Properties:
DesiredDeliveryMediums:
- EMAIL
UserAttributes:
- Name: email
Value: loco-shiroma@example.com
- Name: email_verified
Value: true
MessageAction: SUPPRESS
Username: exuser
UserPoolId: !Ref UserPool
UserGroupAttach:
Type: AWS::Cognito::UserPoolUserToGroupAttachment
Properties:
GroupName: exgroup
Username: exuser
UserPoolId: !Ref UserPool
DependsOn:
- UserPoolGroup
- UserPoolUser
GroupRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: "sts:AssumeRoleWithWebIdentity"
Principal:
Federated: cognito-identity.amazonaws.com
Condition:
StringEquals:
"cognito-identity.amazonaws.com:aud": !Ref IdentityPool
Path: "/"
Policies:
- PolicyName: api
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- execute-api:Invoke
- execute-api:InvalidateCache
Resource:
- "*"
aws-cliでスタックを作成します。
$ aws cloudformation create-stack --profile sandbox --stack-name SandboxCognito --template-body file://`pwd`/SandboxCognito.yaml --capabilities CAPABILITY_IAM
AWSコンソールを開いて、API実行に必要な値を確認してメモしておきます。
まず、IDプールのIDを確認します。
ユーザプールのIDを確認します。
アプリクライアントのIDを確認します。
##2.3 仮パスワードの変更
上記2.2でユーザが登録されますが、下図のようにステータスが、FORCE_CHANGE_PASSWORDと表示されます。
このままでは、ユーザを使用できないので、パスワードを変更します。
まず、ユーザプールのIDを指定して、 以下のように aws-cli を実行して仮パスワードをメールします。
$ aws cognito-idp admin-create-user --user-pool-id ${uid} --username exuser --message-action RESEND --profile sandbox
{
"User": {
"Username": "exuser",
"Enabled": true,
"UserStatus": "FORCE_CHANGE_PASSWORD",
"UserCreateDate": 1543298193.167,
"UserLastModifiedDate": 1543300350.554,
"Attributes": [
{
"Name": "sub",
"Value": "e105d2dc-4f76-4ad1-99e0-5d55be36986a"
},
{
"Name": "email_verified",
"Value": "true"
},
{
"Name": "email",
"Value": "loco-shiroma@example.com"
}
]
}
}
下図のようにメールが届きます。
ピリオドは、パスワードに含みません。
メールでもらったパスワードでログインします。
$ aws cognito-idp admin-initiate-auth --user-pool-id ${uid} --client-id ${cid} --auth-flow ADMIN_NO_SRP_AUTH --auth-parameters "USERNAME=exuser,PASSWORD=${pwd}" --profile sandbox
{
"ChallengeName": "NEW_PASSWORD_REQUIRED",
"ChallengeParameters": {
"USER_ID_FOR_SRP": "exuser",
"requiredAttributes": "[]",
"userAttributes": "{\"email_verified\":\"true\",\"email\":\"loco-shiroma@example.com\"}"
},
"Session": "jOaPpSiBKzxV0UU8qtkgGCscprHzrerFFefuugP7km_jvYrZpgidrF5H1GxyHfhKj3pROwGxFlt2fzlQjhfvomKyUW-Sy-Q1LvY8gyW2UkDRN1CaPap_Kkh7wGVa_PeXNlp1uAhDqc0Jro-Ga3c5Hucruvkp5oZKt30c3iIyULwTNdBL619HEUWQhFACjkxn1NZSLQUI_dhszdB_M4bCG-7BaRDBcD1ZQazmONtWuMDjReK1vg_HVVWiXMRs-0ZKva_hdKt7NwJyCBysUAjS3LxSHNOFywfhJ4XWMLuU7oq0w7_7Z1U1_sEhDLJzwFLKe-PIG9ihFJkkf-UVvFUQG2EElewFY9xmuRNySjQQ-gHgB2J80gAK8n6EyCluT8TTLfPfeC-Vpe1XGTOMYbv9GLvl8CBlrVvIBfhKuZp-5fA4AQZcnns75oi69Qgwxw6rRdq2T-cweYaXHMi3Z7c8flf6DAurCRKEdhed2YQ6BI0m4GDr_qFa0Mdb9V5Zi9tsiVg9y7qX3N05GIV2c_OIby_SqETHWRRqXaNiJGTRgxCYdd-1jkt5VUz8DVqgtQEJTH04tFH6d-tagtScBaP5ERB1Bkfs0GXobRSHP7PhaFG7EgShqpZ1Eq_jMeJywqIIlH8HLcaBpcCITKMU-_QUY-fghrvQ4QLVO17-MlquTvfJL58XBc-KUaZju1RJPYtVNgxtbJhPUEz7CVJVnda19_-FUibf93fLV6klLoZr-AozPyti40UDCcPOX-ArZi6f2HRBjGZ3mDsbkQm495FuHPzbEsIMMlDdivJKy-MBxrc6BfvxeAcXaE6_bPDlcclvet5zxpXQ2n5FB2FrWNEQKQ"
}
上記コマンドで表示された Sessionの値を使用してパスワードを変更します。
$ aws cognito-idp admin-respond-to-auth-challenge --user-pool-id ${uid} --client-id ${cid} --challenge-name NEW_PASSWORD_REQUIRED --challenge-responses NEW_PASSWORD='Passw0rd!',USERNAME=exuser --session "jOaPpSiBKzxV0UU8qtkgGCscprHzrerFFefuugP7km_jvYrZpgidrF5H1GxyHfhKj3pROwGxFlt2fzlQjhfvomKyUW-Sy-Q1LvY8gyW2UkDRN1CaPap_Kkh7wGVa_PeXNlp1uAhDqc0Jro-Ga3c5Hucruvkp5oZKt30c3iIyULwTNdBL619HEUWQhFACjkxn1NZSLQUI_dhszdB_M4bCG-7BaRDBcD1ZQazmONtWuMDjReK1vg_HVVWiXMRs-0ZKva_hdKt7NwJyCBysUAjS3LxSHNOFywfhJ4XWMLuU7oq0w7_7Z1U1_sEhDLJzwFLKe-PIG9ihFJkkf-UVvFUQG2EElewFY9xmuRNySjQQ-gHgB2J80gAK8n6EyCluT8TTLfPfeC-Vpe1XGTOMYbv9GLvl8CBlrVvIBfhKuZp-5fA4AQZcnns75oi69Qgwxw6rRdq2T-cweYaXHMi3Z7c8flf6DAurCRKEdhed2YQ6BI0m4GDr_qFa0Mdb9V5Zi9tsiVg9y7qX3N05GIV2c_OIby_SqETHWRRqXaNiJGTRgxCYdd-1jkt5VUz8DVqgtQEJTH04tFH6d-tagtScBaP5ERB1Bkfs0GXobRSHP7PhaFG7EgShqpZ1Eq_jMeJywqIIlH8HLcaBpcCITKMU-_QUY-fghrvQ4QLVO17-MlquTvfJL58XBc-KUaZju1RJPYtVNgxtbJhPUEz7CVJVnda19_-FUibf93fLV6klLoZr-AozPyti40UDCcPOX-ArZi6f2HRBjGZ3mDsbkQm495FuHPzbEsIMMlDdivJKy-MBxrc6BfvxeAcXaE6_bPDlcclvet5zxpXQ2n5FB2FrWNEQKQ" --profile sandbox
{
"AuthenticationResult": {
"ExpiresIn": 3600,
"IdToken": "eyJraWQiOiJnajkwencxK2VLZE53a09xNVJHNU5Ma3hic1pkNXBvSG1Db3F3bTZBcGhvPSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiJlMTA1ZDJkYy00Zjc2LTRhZDEtOTllMC01ZDU1YmUzNjk4NmEiLCJjb2duaXRvOmdyb3VwcyI6WyJleGdyb3VwIl0sImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJjb2duaXRvOnByZWZlcnJlZF9yb2xlIjoiYXJuOmF3czppYW06OjM1MzYzNjUxNzQxNzpyb2xlXC9TYW5kYm94Q29nbml0by1Hcm91cFJvbGUtMUlEMDBIQkZURVgwQSIsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC5hcC1ub3J0aGVhc3QtMS5hbWF6b25hd3MuY29tXC9hcC1ub3J0aGVhc3QtMV9BdFJzTUZUcTAiLCJjb2duaXRvOnVzZXJuYW1lIjoiZXh1c2VyIiwiY29nbml0bzpyb2xlcyI6WyJhcm46YXdzOmlhbTo6MzUzNjM2NTE3NDE3OnJvbGVcL1NhbmRib3hDb2duaXRvLUdyb3VwUm9sZS0xSUQwMEhCRlRFWDBBIl0sImF1ZCI6IjNpNjduYTU2Z3ZlaWk3cWtmMGxpa3NwdGw4IiwiZXZlbnRfaWQiOiJmZDI1ZmZmNi1mMjBlLTExZTgtODFmMC1iZmY1MjZiNTcxZmQiLCJ0b2tlbl91c2UiOiJpZCIsImF1dGhfdGltZSI6MTU0MzMwMDY4MiwiZXhwIjoxNTQzMzA0MjgyLCJpYXQiOjE1NDMzMDA2ODIsImVtYWlsIjoicy5zaGlyb21hQGxvY28tcGFydG5lcnMuY29tIn0.MPiuA6x-MnnzB55JWrZT8y1zuwWVNBxCNgLAz8NtNL6Lt_EbJVeNzjS3WgaPQhihXbeOxCSoQeoV_RGilGJblW7kfHeIyMOY3pA1Bl7uykyTI2gt8iiOvwufCjYGhU5prNAZYUbsmzkvoZDfs1QGjQi2CciRqSkxVzDxj_g3d6KFqYceUuPbjyWUXX-VswH-Kj12pQo6ns6yMjI-PC3-LjuFbMSjR9vJPOl8OzVvDSnIKxfSCNGk1K4gF4P3me8bXCarCRYol6oTRBjxbFbPip9kh7nue2lnxQcOV-MWkkqial1m9JxxaKis_2b8GJzpyR2NI8LhZPFXb5T-8DJ1Jw",
"RefreshToken": "eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ.kgfFVsor_rztEd7KmBWDd_9VbYAG-aZ_Tk5VBX9kmXee3ih3urmNRuNfs2Hw3NYhuLXqRJngbm5NhIl1Z_-b3A6VpgNWxLo6HhGemklmbnrrqP-SXGRF_Dyfu4CnADTch57LKQzLhCy0aJLE4AS6kZo9a7PdLWGJFSkZBaz1O-PE2oRSoYELMkS9DUQsHT67sBEWI-nxXIsQXj-KCGGBmjJZ9BTdJYUb4nWS3tOGU30OMbfYeTAqHMUVJm5SI9BC-ptnRj2P6uQ1ilZz6rHo7BjPgVTMevptAaHwO9xYSeFOSU9SQeUTlVc690qPEqRNgV_SdTaolu30VPqROZkhIQ.W6RfvwSCJMGzNgmI.PidWCSwDwI1Jp8gRvPuBBFXHLeg4nWn0IIKQw-1yFlBpely_uCmcpRa5b1YE5dPDtuRlZCzcAHfYy_CBPTmS1Uui_ea7l96GmGFhfbM_lTu3vcxgCULWAZgWKopC-jk1cqCk2VY_nGekpandGq9A7Nv7aYyu89jwKxfpb_Rg8xyhfYpz3CrQc35W0CqrVVd2G0WWPyWl2XwPnKl1LF8NgzVE4xqfJlVKtS1Kqgc93huVdwqEjwZuBY6tDgpdDJtQcJDEAF_SmXO8RYPCaUoY8TAquSrSRRLlqBLlvN47iyWwLr6NpXNhJgmED8sTi9NP2kcR1Q0nH-XGsrJnEh2cZSesXTVnUOWEs0zeDGP9OuNPqrI7-aO_rlEqnQQUsLMYAJepRYOW40rd3yPEKnaZnnZCgBKOHhoXxfFDCKVHnKR9NHm-ziGuRv-3TrIr7pGNZWI-Ow4KyRZKF5wTz5vDV9ck74PR2M9JR-OOdIcHaCbDWEOm2jBJoK1hu1wFDBoG8_WO9dliEFvtkw2U4JToigadsxKm8ghnsepU0b423P0QLiab0wErAUITZpccebuLmvzZWGkQSMW8o9SERxgXLpNPIEAz6e1XHfaCByEYLfNXKw9TVBeEOQOWVX3wR6eYUSRGtspVSmKFrmGvPARGkiuojmqjy-pvZx7TczVwh0FAHtLWjyxX3wFfRiEaj00zwYncAZluGR2vPqEcW4t20gnbNk65ZQbJkxc4TRYvz5pKThtXPA68xQJ83RNv_542yXBdV_cMnj_NIW4gqTVHfKrjhDINgr4PoeHME_PByrFm2dwNAiTXolCYkw5R4pfKZKdcGo161B3PpdM43B80_r2HtPa92hfVvKtTvOVPVqG7jVZZqOhx-wjSM4jhme6TMK8MF6huZ1W4GKNe2CqeGcsGAxcS1bT-vlJKQgjPw3kxzMY2p4O157teLzbY3pd2lBmNOESDcMiI1r2DZByekeW6wYh813lv9TEYrRIctUB4mJsmrDlHQ3KIyIxjJGwqJ3OQ2AqXztEExz5aAfovfCcLnFlTDFMVE7Lamu10h_8Zmn-42lU4Rxj49xyQ8xdS8GvLHzCVIsR7Rkp5nKDaMe-9d9oDGPmPD1a7QVr4z2EBoLCH2NidUvkxgxVgjOovc_-wOKOlC_cUmocpK4WrbsXrkxe8iz6EbOqVTUHvfYA0UlXTQdw2TVx68EN6Ts5yMtlzlcWPq9TePR-xtOwP35MQtvHTs-vrQMurDYN2StGy_rlNli8G2Wi0wKG9R9hkiiSXEmVrhRTGJi-zpIu4rG0.pB5sY6GN0fUMJsFLEIuEWQ",
"TokenType": "Bearer",
"AccessToken": "eyJraWQiOiJIeHJhTTlvdThuMHdPakxtb0FDM3F5ZWowNFNKbyt6UEk4bXN3Qmt1enRnPSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiJlMTA1ZDJkYy00Zjc2LTRhZDEtOTllMC01ZDU1YmUzNjk4NmEiLCJjb2duaXRvOmdyb3VwcyI6WyJleGdyb3VwIl0sImV2ZW50X2lkIjoiZmQyNWZmZjYtZjIwZS0xMWU4LTgxZjAtYmZmNTI2YjU3MWZkIiwidG9rZW5fdXNlIjoiYWNjZXNzIiwic2NvcGUiOiJhd3MuY29nbml0by5zaWduaW4udXNlci5hZG1pbiIsImF1dGhfdGltZSI6MTU0MzMwMDY4MiwiaXNzIjoiaHR0cHM6XC9cL2NvZ25pdG8taWRwLmFwLW5vcnRoZWFzdC0xLmFtYXpvbmF3cy5jb21cL2FwLW5vcnRoZWFzdC0xX0F0UnNNRlRxMCIsImV4cCI6MTU0MzMwNDI4MiwiaWF0IjoxNTQzMzAwNjgyLCJqdGkiOiI4NDFlYTRiMS00MTJmLTQyMWMtODVhZi1kY2M3N2E4MWZiZjkiLCJjbGllbnRfaWQiOiIzaTY3bmE1Nmd2ZWlpN3FrZjBsaWtzcHRsOCIsInVzZXJuYW1lIjoiZXh1c2VyIn0.NoY2xt4Eed8OtwsASIM8wBUHk1BEOsMf1nh0lbo68Vz0MGigYe5m-y9bl8rrdyYFkVdnnbQeCsx8V3kHLfaFI70FnYy42lyu6Nwq48tiheRKyraEeHR6vuf6j8J7AklsMraU9mDUTCfvgitQjhmQn2OMK1qwZaaDYkKPS9ICCh-CsHzXDaETb-wnIgtYGG7u6h0geyONdb4Yr2Y92ACYDKTo_LjG6U8xhdTZJafUWu0EyI1QPpi5BA80FtS2Gg6tt04lsrdc3jo4qZJ6dGXyvmdoMGVyjehzRtg13syi9t0twSWCVF7LB31JJKduA5cBR7vwONNSbp8Y4vw45KRM9g"
},
"ChallengeParameters": {}
}
AWSコンソールでユーザのステータスを確認して、CONFIRMED になっていたらOKです。
#3. API動作確認
##3.1 クライアントアプリケーションの用意
APIを実行するアプリケーションをPythonで作成しました。
ソースコードは以下の通り。
import requests
import boto3
from warrant.aws_srp import AWSSRP
from requests_aws_sign import AWSV4Sign
USERNAME = 'exuser'
PASSWORD = 'Passw0rd!'
ACCOUNT_ID = "123456789012"
REGION = "ap-northeast-1"
API_ENDPOINT = "https://2037kuo2d5.execute-api.ap-northeast-1.amazonaws.com/stage1"
USER_POOL_ID = "ap-northeast-1_AtRsMFTq0"
CLIENT_ID = "3i67na56gveii7qkf0liksptl8"
IDENTITY_POOL_ID = "ap-northeast-1:e0794416-2b06-4abc-907e-130a4ac2b89f"
def main():
aws = AWSSRP(username=USERNAME, password=PASSWORD, pool_id=USER_POOL_ID, client_id=CLIENT_ID)
tokens = aws.authenticate_user()
id_token = tokens['AuthenticationResult']['IdToken']
logins = {'cognito-idp.' + REGION + '.amazonaws.com/' + USER_POOL_ID : id_token}
client = boto3.client('cognito-identity', region_name=REGION)
cognito_identity_id = client.get_id(
AccountId=ACCOUNT_ID,
IdentityPoolId=IDENTITY_POOL_ID,
Logins=logins
)
id_credentials = client.get_credentials_for_identity(
IdentityId=cognito_identity_id['IdentityId'],
Logins=logins
)
session = boto3.session.Session(
aws_access_key_id=id_credentials['Credentials']['AccessKeyId'],
aws_secret_access_key=id_credentials['Credentials']['SecretKey'],
aws_session_token=id_credentials['Credentials']['SessionToken'],
region_name=REGION
)
credentials = session.get_credentials()
service = 'execute-api'
auth = AWSV4Sign(credentials, REGION, service)
headers = {"Content-Type": "application/json"}
request_path = '/'
url = API_ENDPOINT + request_path
response = requests.request('GET', url, auth=auth, headers=headers)
print('GET', request_path, response.status_code)
print(response.headers)
print(response.text)
return
if __name__ == '__main__':
main()
AWSSRP は、 以下のようにしてインストールします。
$ pip install warrant
AWSV4Sign は、GitHUB から取得して、 requests-aws-sign ディレクトリを上記の api_request.py と同じ場所に格納します。
##3.2 API実行
APIを実行してみます。
うまくいけば、以下のように表示されます。
レスポンスステータスが200で、APIGatewayのMOCで定義したレスポンス(JSON)が表示されます。
$ python ./api_request.py
('GET', '/', 200)
{'x-amzn-RequestId': '895b2387-f210-11e8-a22c-8f58e6559e46', 'Content-Length': '26', 'x-amz-apigw-id': 'RAlTgH3_NjMFpFQ=', 'Connection': 'keep-alive', 'Date': 'Tue, 27 Nov 2018 06:49:07 GMT', 'Content-Type': 'application/json'}
{
"message": "hello!"
}