
API Gateway REST API + Cognito User Pool Authorizer + Lambda Functionな構成をAWS CDK v2で構築してみた
この記事は公開されてから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を使用しています。
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で返すだけです。
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
 
以上








