Swagger と AWS SAM を使って Cognito オーソライザーを使って簡単な REST API HTTP 統合してみる

2021.03.18

はじめに

おはようございます、もきゅりんです。

皆さん、API Gateway 使ってますか?

API Gateway といえば、 Lambda というくらいのセットイメージが一般的には強いと思うのですが、いろいろなサービスと関連付けることができますよね。

API の Gateway なのですから、APIを利用する側とAPIを提供する側の仲介、調整役として、 API Gateway は様々な機能を有しております。

ということで、本稿では REST API のHTTP統合を Swagger と Serverless Application Model (以下 SAM) でデプロイします。

実は大分前に、本稿を書いている途中、【初心者向け】SwaggerとAWS SAMを使ってWebAPIを簡単に作ってみた | DevelopersIO というグレートなブログを見つけて、あ、このブログいらないな、ということでそのままになっていました。

が、しばらくしてまた SAM を触る機会があったのですが、(普段あまり触らないので) あれ、サムってどんなんだったけ、とド忘れしてしまっていたため、自身の備忘録の意味もあり、まとめておくこととしました。

【初心者向け】SwaggerとAWS SAMを使ってWebAPIを簡単に作ってみた | DevelopersIO との構成の違いは下記になります。

  • Cognito オーソライザーと連携する
  • Lambda プロキシ統合ではなく、HTTP統合である
  • CloudWatchLogs にログ出力する

構成図

agw_archi

API は Spring BootのREST APIをAWS Fargateにデプロイしてみた | DevelopersIO を再利用しています。

やること

  1. Cognito UserPool を作る
  2. SAMテンプレートを作る (Swaggerテンプレートも作る)
  3. デプロイする
  4. Postman で確認する

1 Cognito UserPool を作る

温かみある手動で Cognito UserPool は作成します。

基本的にデフォルト設定です。クライアントシークレットは解除、認証用の管理 API のユーザー名パスワード認証は有効にします。

手順は Cognitoで認証されたユーザーだけがAPI Gatewayを呼び出せるオーソライザーを使ってみた | DevelopersIO で丁寧にまとめられていますのでご参照下さい。

今回はIDトークンが欲しいだけなんです!

Cognito のサインイン時に発行されるトークンとそれらの役割についての詳細な説明は、Cognitoのサインイン時に取得できる、IDトークン・アクセストークン・更新トークンを理解する | DevelopersIO がとても分かりやすいです。

2 SAMテンプレートを作る

何はともあれ、 sam init で開始します。

Getting started with AWS SAM - AWS Serverless Application Model を参考に SAM CLI を手に入れて、その使い方や作業の流れを、 Tutorial: Deploying a Hello World application - AWS Serverless Application Model を確認して進めます。

デフォルトテンプレートを AWS Serverless Application Model (AWS SAM) specification - AWS Serverless Application Model の仕様を参考に更新していって、ビルドしてデプロイします。

そんなこんなで、SAM テンプレートは以下になりました。

なお、 AWS SAMで「Stage」ステージが作られるバグを回避する に注意します。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: demo-project
Globals:
  Api:
    OpenApiVersion: 3.0.2
Resources:
  ApiGatewayLogs:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: /demo/sam-rest-api
      RetentionInDays: 30
  ApiGatewayApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: dev
      AccessLogSetting:
        DestinationArn: !GetAtt ApiGatewayLogs.Arn
        Format: $context.identity.sourceIp $context.identity.caller  \
          $context.identity.user [$context.requestTime] \
          "$context.httpMethod $context.resourcePath $context.protocol" \
          $context.status $context.responseLength $context.requestId
      EndpointConfiguration: REGIONAL
      Name: sam-rest-api
      Auth:
        Authorizers:
          cognitoAuth:
            UserPoolArn: arn:aws:cognito-idp:reagion:account-id:userpool/reagion_cognito_id
        DefaultAuthorizer: cognitoAuth
        InvokeRole: CALLER_CREDENTIALS
      DefinitionBody:
        Fn::Transform:
          Name: AWS::Include
          Parameters:
            Location: ./swagger.yml
      MethodSettings:
        - DataTraceEnabled: true
          LoggingLevel: INFO
          ResourcePath: '/*'
          HttpMethod: '*'
Outputs:
  HelloWorldApi:
    Description: API Gateway endpoint URL for Prod stage for Hello World function
    Value:
      Fn::Sub: https://${ApiGatewayApi}.execute-api.${AWS::Region}.amazonaws.com/dev/greeting/

Swagger を使ってドキュメントを作る

OpenAPI (Swagger) 超入門 - Qiita とかを参考に Swagger Editor, Swagger UI, Swagger Codegen など好きなもので作ります。

この辺は身近な API 職人にアドバイス貰うと良いでしょう。(アドバイス貰いたい...)

なお、API Gateway 独自の仕様があるので、手元の Swagger ファイルをそのまま使えるわけではない可能性もあるので、注意が必要です。

詳細は、OpenAPI への API Gateway 拡張機能の使用 - Amazon API Gateway をご参照下さい。

自分が頑張って作ったのが以下です。

openapi: 3.0.1
info:
  title: Sample Hello API
  version: 1.0.0
servers:
- url: /
paths:
  /greeting:
    get:
      description: Return Hello
      responses:
        200:
          description: 200 response
          content: {}
      x-amazon-apigateway-integration:
        responses:
          default:
            statusCode: "200"
        uri: http://xxxxxxxxxxxxxxxxx.ap-northeast-1.elb.amazonaws.com/greeting
        passthroughBehavior: when_no_match
        httpMethod: GET
        type: http

頑張ったけど、ドキュメントを眺めながら気合で頑張るよりも、最初に手で自由に設定した後に API Gateway から REST API をエクスポートする - Amazon API Gateway を参考にエクスポートしちゃった方が効率的だと思いました。

## sample export command
aws apigateway get-export --parameters extensions='apigateway' --rest-api-id abcdefg123 --stage-name dev --export-type swagger latestswagger2.json

APIを起動する

Spring BootのREST APIをAWS Fargateにデプロイしてみた | DevelopersIO を参考にAPIを起動させておきます。

デプロイする

AWS CLIがAWS::Include transformをS3へアップロードできるようになりました | DevelopersIO を参考に AWS::Include transform を使ってS3へSwaggerテンプレートをアップロードしてデプロイします。

## バケットがなければ適当にバケットを作成します
aws s3 mb s3://everyday-sam-bucket

aws cloudformation package \
    --s3-bucket everyday-sam-bucket \
    --template-file sam.yml \
    --output-template-file template.yml
    
Uploading to 002d01928360fe5180926ecbf91836a4  588 / 588.0  (100.00%)
Successfully packaged artifacts and wrote output template to file template.yml.
Execute the following command to deploy the packaged template
aws cloudformation deploy --template-file <YOUR PATH>/template.yml --stack-name <YOUR STACK NAME>

言われるがままコマンド叩きます。

aws cloudformation deploy --template-file <YOUR PATH>/template.yml --stack-name <YOUR STACK NAME>

確認

IDトークンを取得します。

aws cognito-idp admin-initiate-auth \
--user-pool-id <YOUR USER POOL ID> \
--client-id <YOUR APP CLIENT ID>  \
--auth-flow ADMIN_NO_SRP_AUTH \
--auth-parameters USERNAME=<YOUR USERNAME>,PASSWORD=<YOUR PASSWORD>

REST API のテストツールは何を使っても良いと思いますが、Postman を使用して REST API を呼び出す - Amazon API Gateway を参考に、 Postman を使って見てみると下記のような結果となりました。

unauthed

authed

問題ないようですね。

以上です。

最後に

またしばらく SAM を触らない間に忘れないように雰囲気まとめておきました。

ついでに、どなたかのお役に立つことがあれば幸いです。