AWS CDKでCognitoを使用してREST API 認証を実装してみた。
概要
API Gateway REST API はリソースとメソッドのコレクションです。REST API は HTTPエンドポイント、Lambda関数、と他のAWSサービスと統合できます。REST API はリクエスト/レスポンスモデルを使用します。
IAMロール、Lambdaオーソライザー、Amazon Cognitoオーソライザーを使用してRESTAPIへのアクセスを制御できます。この記事では、Cognito ユーザープールをオーソライザーとして使用して REST API 認証を実装してみました。
やってみた
CDKアプリの作成
CDKをインストールする
- 次のコマンドを使用してCDKをインストールしておきます。
npm install aws-cdk-lib
CDKアプリを作成する
- 新しいディレクトリを作成しておきます。
- CDKは、プロジェクトディレクトリの名前に基づいてソースファイルとクラスに名前を付けます。
#create new directory mkdir api cd api
- cdk initコマンドを使用してアプリを初期化しておきます。
cdk init --language typescript
AWS サービスの作成
- 新しいファイル [lib/index.ts] を作成して、作成する必要のあるAWSサービスを定義しておきます。
- ファイルに次のAWSサービスを定義しておきます。
- DynamoDBテーブル : パーティションキーを持つDynamoDBテーブル。
- Lambda関数 : 環境変数とPythonランタイムを備えたLambda関数。
- 環境変数にはテーブル名が含まれています。
- コードは、DynamoDBにアクセスするための読み取りおよび書き込み権限をラムダに付与します。
- API Gateway - GETメソッドのAPI。
- Cognito User Pool : ドメインとクライアントを備えた User Pool。
- Authorizer: APIを認証できるAuthorizerを定義しておきます。
import { Construct } from 'constructs'; import { LambdaIntegration, RestApi, Deployment, Stage, CfnAuthorizer, AuthorizationType} from 'aws-cdk-lib/aws-apigateway'; import { Table, AttributeType } from 'aws-cdk-lib/aws-dynamodb'; import { Runtime, Function, AssetCode } from 'aws-cdk-lib/aws-lambda'; import { RemovalPolicy, StackProps} from 'aws-cdk-lib'; import { UserPool } from 'aws-cdk-lib/aws-cognito' export class apiStack extends Construct { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id); const dynamoTable = new Table(this, "items", { partitionKey: { name: "id", type: AttributeType.STRING, }, tableName: "usersDB", removalPolicy: RemovalPolicy.DESTROY }); const getAllLambda = new Function(this, "getAllItemsFunction", { code: new AssetCode("resources"), handler: "get-all.handler", runtime: Runtime.PYTHON_3_9, environment: { TABLE_NAME: dynamoTable.tableName, }, functionName: "usersfunction", }); dynamoTable.grantReadWriteData(getAllLambda); const getAllIntegration = new LambdaIntegration(getAllLambda); // Create an API Gateway const api = new RestApi(this, 'api', { restApiName: 'users-api', deploy: false }); //create cognito user pool const userPool = new UserPool(this, 'userPool', { userPoolName: 'api-user-pool', standardAttributes: { email: {required: true, mutable: true}, }, removalPolicy: RemovalPolicy.DESTROY }); //Add Domain to the user pool userPool.addDomain('domain', { cognitoDomain: {domainPrefix: 'api-users-domain'}, }); const userPoolClient = userPool.addClient('client', { userPoolClientName: 'api-client', preventUserExistenceErrors: true, authFlows: { adminUserPassword: true, userPassword: true } }); //Define Authorizer const authorizer = new CfnAuthorizer(this, 'cfnAuth', { restApiId: api.restApiId, name: 'api-Authorizer', type: 'COGNITO_USER_POOLS', identitySource: 'method.request.header.Authorization', providerArns: [userPool.userPoolArn], }) const items = api.root.addResource('users'); items.addMethod('GET', getAllIntegration,{ authorizationType: AuthorizationType.COGNITO, authorizer: { authorizerId: authorizer.ref } }); const deployment = new Deployment(this, 'Deployment', {api}); const stage = new Stage(this, 'dev_stage', {deployment, stageName : 'dev'}); api.deploymentStage = stage; } }
Lambda関数の作成
- プロジェクトのメインディレクトリにresourcesディレクトリを作成しておきます。
mkdir resources
- resourcesディレクトリに次のPythonファイルを作成しておきます。[resources/get-all.py]
- 以下のコードは、DynamoDBテーブルのすべての項目を一覧表示します。
import json import os import boto3 from botocore.exceptions import ClientError dynamodb = boto3.resource('dynamodb') TABLE_NAME = os.environ['TABLE_NAME'] def handler(event, context): table = dynamodb.Table(TABLE_NAME) statusCode = 200 body = "" try: result = table.scan() body = json.dumps(result) except ClientError as e: statusCode = 404 body = str(e) response = { 'statusCode': statusCode, 'body': body, 'headers': { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' } } return response
アプリにサービスを追加する
- /lib/api_stack.ts ファイルに次のコードを追加しておきます。
#Import the Index file created in the previous step import * as api from '../lib/index'; new api.apiStack(this, 'api');
CDK Deploy
- Deploy する前に、環境をブートストラップする必要があります。
- CDK Bootstrapは、AWS CDKがスタックをデプロイするために使用するS3バケットを作成します。
- 次のコマンドを実行して、AWS環境をブートストラップしておきます。
cdk bootstrap
- 次のコマンドを使用してCDKを展開しておきます。
cdk deploy
- コンソールでは、サービスが作成されたことを見ることができます。
Cognito User Pool
API Gateway
Authorizer
DynamoDBテーブル
Lambda関数
API 認証とテストをする
- APIに直接アクセスすると、Unauthorized エラーが発生します。APIにアクセスするにはトークンIDを提供する必要があります。
curl 'https://id.execute-api.us-east-1.amazonaws.com/dev/users' //output {"message":"Unauthorized"}
- 次のコマンドを使用してトークンを取得します。
//Command to get Token aws cognito-idp admin-initiate-auth \ --user-pool-id 'User Pool Id' \ --client-id 'App Client Id' \ --auth-flow "ADMIN_USER_PASSWORD_AUTH" \ --auth-parameters USERNAME='user name',PASSWORD='password' \ --region us-east-1
- 前のコマンドの出力からのトークンIDを使用してAPIにアクセスしておきます。APIを使用してユーザーのリストを取得できるようになりました。
curl 'https://id.execute-api.us-east-1.amazonaws.com/dev/users'' \ --header 'Authorization: token-id'
まとめ
CDKでCognitoを使用してREST API 認証を実装してみました。
Reference :