この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは、CX事業本部 IoT事業部の若槻です。
前回に引き続き、今回もAWS CDK v2の肩慣らしをしていこうと思います。
今回は、API Gateway REST API + Cognito User Pool Authorizer + Lambda Functionな構成をAWS CDK v2で構築してみました。
環境
- react@18.0.0
- typescript@4.6.3
やってみた
このような構成を構築してみます。
実装
CDK Stackのコードは次のようになります。Lambda Functionコードの参照はReference project architectureを使用しています。
lib/aws-cdk-v2-app-stack.ts
import { Construct } from 'constructs';
import {
Stack,
StackProps,
RemovalPolicy,
aws_cognito,
aws_lambda_nodejs,
aws_apigateway,
} from 'aws-cdk-lib';
export interface AwsCdkV2AppStackProps extends StackProps {
callbackUrls: string[];
logoutUrls: string[];
domainPrefix: string;
}
export class AwsCdkV2AppStack extends Stack {
constructor(scope: Construct, id: string, props: AwsCdkV2AppStackProps) {
super(scope, id, props);
// User Pool
const userPool = new aws_cognito.UserPool(this, 'userPool', {
userPoolName: 'testUserPool',
selfSignUpEnabled: false,
standardAttributes: {
email: { required: true, mutable: true },
phoneNumber: { required: false },
},
signInCaseSensitive: false,
autoVerify: { email: true },
signInAliases: { email: true },
accountRecovery: aws_cognito.AccountRecovery.EMAIL_ONLY,
removalPolicy: RemovalPolicy.DESTROY,
});
// User Pool Domain
userPool.addDomain('domain', {
cognitoDomain: { domainPrefix: props.domainPrefix },
}); //今回はログインUIを使わないので無くてもOK
// User Pool Client
userPool.addClient('client', {
userPoolClientName: 'testUserPoolClient',
generateSecret: false,
oAuth: {
callbackUrls: props.callbackUrls,
logoutUrls: props.logoutUrls,
flows: { authorizationCodeGrant: true },
scopes: [
aws_cognito.OAuthScope.EMAIL,
aws_cognito.OAuthScope.OPENID,
aws_cognito.OAuthScope.PROFILE,
],
},
authFlows: { adminUserPassword: true }, //cognitoIdp:adminInitiateAuth APIでユーザートークンを取得可能にする
});
// Lambda Function
const lambdaFunc = new aws_lambda_nodejs.NodejsFunction(this, 'lambdaFunc');
// Cognito Authorizer
const cognitoAuthorizer = new aws_apigateway.CognitoUserPoolsAuthorizer(
this,
'cognitoAuthorizer',
{
authorizerName: 'CognitoAuthorizer',
cognitoUserPools: [userPool],
}
);
// Rest API
const restApi = new aws_apigateway.RestApi(this, 'restApi');
// Rest API Resource/Method
restApi.root
.addResource('data')
.addMethod('GET', new aws_apigateway.LambdaIntegration(lambdaFunc), {
authorizer: cognitoAuthorizer,
});
}
}
Lambda Functionのコードは次のようになります。固定のデータを200 OK
で返すだけです。
lib/aws-cdk-v2-app-stack.lambdaFunc.ts
exports.handler = async () => {
const response = {
statusCode: 200,
body: JSON.stringify({ greeting: 'Hello from Lambda!' }),
};
return response;
};
cdk deploy
で構成をデプロイします。OutputでREST APIのエンドポイントのURI(`https://{apiId}.execute-api.ap-northeast-1.amazonaws.com/prod/`)が表示されるので控えます。
動作確認
先程控えたエンドポイントのURIを使用してdata
リソースのURLを変数に指定します。
$ endpointUri=<endpointUri>
$ dataResourceUri=${endpointUri}data
作成されたUser Poolにユーザーを作成します。
ユーザーのID Token取得に必要な情報を変数に指定します。
$ userName=<userName>
$ userPassword=<userPassword>
$ userPoolId=<userPoolId>
$ clientId=<userPoolClientId>
cognitoIdp:adminInitiateAuth APIを使用してユーザーのID Tokenを取得します。
$ idToken=$(aws cognito-idp admin-initiate-auth \
--user-pool-id ${userPoolId} \
--client-id ${clientId} \
--auth-flow "ADMIN_USER_PASSWORD_AUTH" \
--auth-parameters USERNAME=${userName},PASSWORD=${userPassword} \
--query "AuthenticationResult.IdToken" \
--output text)
リソースURIにID Tokenを使用してGETリクエストすると、認証が成功すればLambda Functionからデータが返されます。
$ curl -H "Authorization: ${idToken}" ${dataResourceUri}
{"greeting":"Hello from Lambda!"}
少しハマったところ
トークン違いによるUnauthorizedエラー
Authorization
ヘッダーに間違えてAccess Tokenを指定して401 Unauthorized
エラーとなった。
正しくはID Tokenを指定します。
HTTP APIのHigh Level ConstructはまだExperimental
APIとしてaws_apigatewayv2
(HTTP API)を使いたかったのですが、CDK v2のHigher level constructsがまだExperimentalでした。
なのでaws_apigateway
(REST API)を今回は使いました。
おわりに
API Gateway REST API + Cognito User Pool Authorizer + Lambda Functionな構成をAWS CDK v2で構築してみました。
よくある構成だと思うので、今後よく使うテンプレートとなりそうです。ただHandler Fileの参照で使用したReference project architectureは少々挑戦的だったので、場合によってはEntryによる参照を使うかもしれません。
参考
- [AWS CDK] API Gateway(REST API)のCORSの動作を確認してみた | DevelopersIO
- AWS CDKでCognito認証されたAPI Gateway(HTTP API)を構築する | DevelopersIO
- API Gateway→Lambdaをcurlでテストする - Qiita
- AWS CLIでCognitoユーザーのアクセストークン/IDトークンを取得する | DevelopersIO
- npm scriptsから実行してCognitoユーザーのIDトークンを取得できるスクリプト(TypeScript)を作ってみた | DevelopersIO
以上