🐡

SAM で Lambda + APIGW (HTTP API) と Firebase Auth

2023/08/15に公開

TL;DR

  • SAMを使うと簡単にサーバーレスでAPIを構築できる
    • API Gateway + Lambda を簡単に構築できる
  • API Gateway の HTTP API を使えば、JWT認証ができる
    • OIDCのIDトークンによる認証ができる
  • JWT認証でFirebase Authのトークンを検証できる

SAMとは

  • AWS サーバーレスアプリケーションモデル
  • SAM CLI を使って簡単にサーバーレスのアプリケーションを構築できる
  • CloudFormationテンプレートベースのSAMテンプレートで構成を記述
  • 開発用にローカルでLambda実行やAPIサーバーの起動も容易

API Gateway HTTP APIとは

SAM + ApiGW (HTTP API) でJWT認証する

  • AWS CLI/SAM CLIのインストールから、Firebase Authのトークン検証を行うエンドポイントを作るまで

AWS CLIインストール

curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

SAM CLIインストール

curl -L https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-x86_64.zip -o aws-sam-cli-linux-x86_64.zip
unzip aws-sam-cli-linux-x86_64.zip -d sam-installation
sudo ./sam-installation/install

SAMアプリケーションを作る

  • sam init コマンドを使ってアプリケーションの初期化を行う
    • ランタイムは Go を使う
    • AWS Quick Start Templates (Hello World Example) を利用
    • x-rayとかcloudwatch insightsとかはなし
    • プロジェクト名は適当に sam-ple とした
sam init --runtime go1.x
Which template source would you like to use?
        1 - AWS Quick Start Templates
        2 - Custom Template Location
Choice: 1

Choose an AWS Quick Start application template
        1 - Hello World Example
        2 - Infrastructure event management
        3 - Multi-step workflow
Template: 1

Based on your selections, the only Package type available is Zip.
We will proceed to selecting the Package type as Zip.

Based on your selections, the only dependency manager available is mod.
We will proceed copying the template using mod.

Would you like to enable X-Ray tracing on the function(s) in your application?  [y/N]: n

Would you like to enable monitoring using CloudWatch Application Insights?
For more info, please view https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwat
ch-application-insights.html [y/N]: n

Project name [sam-app]: sam-ple

    -----------------------
    Generating application:
    -----------------------
    Name: sam-ple
    Runtime: go1.x
    Architectures: x86_64
    Dependency Manager: mod
    Application Template: hello-world
    Output Directory: .
    Configuration file: sam-ple/samconfig.toml
    
    Next steps can be found in the README file at sam-ple/README.md
        

Commands you can use next
=========================
[*] Create pipeline: cd sam-ple && sam pipeline init --bootstrap
[*] Validate SAM template: cd sam-ple && sam validate
[*] Test Function in the Cloud: cd sam-ple && sam sync --stack-name {stack-name} --watch

  • 以下コマンドでAPIサーバーをローカルで起動し、動作確認
    • ログは長いので省略
    • Hello, 127.0.0.1! が返ってくる
cd sam-ple
sam build
sam local start-api
curl localhost:3000/hello

デプロイ

  • aws cli の認証情報を設定
    • 例えば、aws configure でAPIキー入れる
  • sam deploy --guided
    • ガイドにそって進めていけばデプロイできる
  • https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/ にブラウザでアクセス
    • このURLは sam deploy のログに出力されている
    • Hello, {IP Address}! が表示される

カスタマイズする

  • 以下2つのカスタマイズをする
    • API Gateway の種類を REST API から HTTP API に変える
    • JWT認証でFirebase Authのトークンを検証できるようにする
  • template.yaml を編集
@@ -11,7 +11,27 @@ Globals:
     Timeout: 5
     MemorySize: 128
 
+Parameters:
+  JwtIssuer:
+    Type: String
+  JwtAudience:
+    Type: String
+
 Resources:
+  MyApi:
+    Type: AWS::Serverless::HttpApi
+    Properties:
+      StageName: Prod
+      Auth:
+        Authorizers:
+          OAuth2Authorizer:
+            IdentitySource: $request.header.Authorization
+            JwtConfiguration:
+              Issuer: !Ref JwtIssuer
+              Audience:
+                - !Ref JwtAudience
+        DefaultAuthorizer: OAuth2Authorizer
+
   HelloWorldFunction:
     Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
     Properties:
@@ -22,8 +42,9 @@ Resources:
         - x86_64
       Events:
         CatchAll:
-          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
+          Type: HttpApi # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
           Properties:
+            ApiId: !Ref MyApi
             Path: /hello
             Method: GET
       Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object
@@ -36,7 +57,7 @@ Outputs:
   # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
   HelloWorldAPI:
     Description: "API Gateway endpoint URL for Prod environment for First Function"
-    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
+    Value: !Sub "https://${MyApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
   HelloWorldFunction:
     Description: "First Lambda Function ARN"
     Value: !GetAtt HelloWorldFunction.Arn

  • 解説その1 API Gateway の種類を REST API から HTTP API に変える
-          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
+          Type: HttpApi # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
  • 解説その2 JWT認証でFirebase Authのトークンを検証できるようにする
    • API Gatewayを個別に定義し、JWT認証を指定する
      • トークンは Authorization ヘッダに乗せる
      • OAuth2Authorizer を参考にしたので、OAuth2Authorizerになっている
+Parameters:
+  JwtIssuer:
+    Type: String
+  JwtAudience:
+    Type: String
+
 Resources:
+  MyApi:
+    Type: AWS::Serverless::HttpApi
+    Properties:
+      StageName: Prod
+      Auth:
+        Authorizers:
+          OAuth2Authorizer:
+            IdentitySource: $request.header.Authorization
+            JwtConfiguration:
+              Issuer: !Ref JwtIssuer
+              Audience:
+                - !Ref JwtAudience
+        DefaultAuthorizer: OAuth2Authorizer
+
    • ↑で定義した MyApi を指定するように変更
+            ApiId: !Ref MyApi
-    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
+    Value: !Sub "https://${MyApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
   HelloWorldFunction:
  • samconfig.yaml にパラメータを追加する
    • FirebaseのProject IDが必要
    • Firebaseにプロジェクト作ってIDを取得する
    • Firebase Authのセットアップもしておく
    • (Firebaseのセットアップについては深堀しないので、他の記事とかを検索してください)
    • (Firebase Auth以外を使う場合はここを変える)
[default.deploy.parameters]
(省略)
parameter_overrides = [
  "JwtIssuer=https://securetoken.google.com/{firebase-project-id}",
  "JwtAudience={firebase-project-id}"
]

再ビルド&デプロイ

  • sam build
  • sam deploy
  • https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello にブラウザでアクセス
    • {"message":"Unauthorized"} が返ってくる (ステータス401)
    • 最後の / (スラッシュ) があると、Not Found(404) が返ってくるかも
  • Firebase Authのトークンを取得
    • Firebase Authのトークンをどうにかして登録する
    • メールアドレス&パスワード認証が有効な場合は、↓を参考にcurlで取得できる
  • トークンをヘッダーに入れてリクエストしてみる
    • curlが簡単なので、ここではcurlでリクエストしてみる
    • Hello, {IP Address}! or Hello, world! が返ってくる
curl -H 'Authorization: Bearer {token}' https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello

後片付け

  • 使い終わったら、忘れずに片付ける
sam delete

Discussion