Firebase AuthenticateをAWS API Gateway・Lambdaで利用する

2021/12/037 min read

Arumon Advent Calendar 2021 の 3 日目の記事です。

はじめに

Firebase とは、Google 社が提供している、アプリを素早く構築できる mBaaS のサービスです。 非常に簡単に Web アプリやスマホアプリなどのフロントアプリを作成することができます。

主に AWS で構築しているサービスの場合でも、上記の利点から Firebase でフロントアプリ(キャンペーンアプリやランディングサイトなど)を構築したくなるかもしれません。

そこで本記事では、Firebase で構築されたフロントエンド と AWS で構築されたバックエンド との連携における、認証・認可について取り上げたいと思います。

全体のイメージ

フロントアプリは Firebase のサービスを利用するため、認証サービスとして Firebase Authenticate を利用しています。ある特定の機能で AWS で構築されているバックエンドサービスに接続することを例に考えます。

一連の流れは以下のとおりです。

  • フロントアプリは Firebase Authenticate の SDK を利用して Firebase ID トークン(Token)を取得する
  • フロントアプリは APIGateway のエンドポイントへ Firebase ID トークン(Token)を付与したリクエストを送付する
  • API Gateway はリクエストの Firebase ID トークン(Token)を JWT Authorizer を用いて検証する
  • 検証 OK の場合、バックエンドサービスへ連携する

ハンズオン

AWS Serverless Application Model (AWS SAM) を利用して、検証に使用する API Gateway と Lambda をぱぱっと構築してみます。

AWS CLI および AWS SAM CLI が必要ですので、上記デベロッパーガイドを参考にインストールしましょう。

SAM アプリケーションの作成

SAM アプリケーションの初期化

まずは、以下のコマンドで SAM のサンプルアプリケーションである Hello World アプリケーションをセットアップしましょう。

アーティファクトとしてコンテナイメージを選べるようなので(知らなかった)、今回はこちらを選んでみました。なお、コンテナイメージのビルドには Docker が必要ですので、よく分からない人は素直に Zip を選んだほうが良いかも知れません。

$ sam init
Which template source would you like to use?
        1 - AWS Quick Start Templates
        2 - Custom Template Location
Choice: 1

What package type would you like to use?
        1 - Zip (artifact is a zip uploaded to S3)
        2 - Image (artifact is an image uploaded to an ECR image repository)
Package type: 2

Which base image would you like to use?
        1 - amazon/nodejs14.x-base
Base image: 1

Project name [sam-app]: firebase-auth-example

SAM アプリケーションの実装

Hello World アプリケーションを以下を参考に書き換えます。

SAM Template

作成するリソースは「API Gateway」と「Lambda」の 2 つです。

API Gateway は JWT Authorizer をアタッチします。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  firebase-auth-example

  Sample SAM Template for firebase-auth-example

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 3

Parameters:
  FirebaseProjectId:
    Type: String

Resources:
  AuthGatewayHttpApi:
    Type: AWS::Serverless::HttpApi
    Properties:
      Auth:
        Authorizers:
          FirebaseAuthorizer:
            IdentitySource: $request.header.Authorization
            JwtConfiguration:
              audience:
                - !Ref FirebaseProjectId
              issuer: !Sub https://securetoken.google.com/${FirebaseProjectId}
        DefaultAuthorizer: FirebaseAuthorizer
      StageName: "Prod"

  AuthFunction:
    Type: AWS::Serverless::Function
    Properties:
      PackageType: Image
      Architectures:
        - x86_64
      Events:
        Gateway:
          Type: HttpApi
          Properties:
            ApiId: !Ref AuthGatewayHttpApi
            Path: /hello
            Method: get
    Metadata:
      DockerTag: nodejs14.x-v1
      DockerContext: ./function
      Dockerfile: Dockerfile

Outputs:
  API:
    Description: "API Gateway endpoint URL for Prod stage"
    Value: !Sub "https://${AuthGatewayHttpApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/"

バックエンドの Lambda

受け取った JWT を出力する簡単な API です。

exports.lambdaHandler = async (event, context) => {
  try {
    const { jwt } = event.requestContext.authorizer;
    const { email } = jwt.claims;
    return {
      statusCode: 200,
      body: JSON.stringify({
        jwt: jwt,
        email: email,
      }),
    };
  } catch (err) {
    console.log(err);
    return {
      statusCode: 400,
      body: JSON.stringify({
        error: 'Something wrong.',
      }),
    };
  }
};

以下のコマンドで build&deploy しましょう。

$ sam build
$ sam deploy --guided

作成されたリソース

API Gateway

API Gateway が作成され、JWT Authorizer がアタッチされていることが分かります。

apigw jwt authorizer

Lambda

API Gateway がトリガーの Lambda が作成されています。そして、コンテナイメージがデプロイされていることが分かります。

lambda function

動かしてみる

以下のドキュメントを参考に、Firebase Authenticate の ID トークンを取得しましょう。

curl や Postman を用いて、今回作成した API Gateway のエンドポイントにトークンを付与してリクエストを送ってみましょう。

トークンの付与は、HTTP リクエストヘッダーAuthorizationBearer <トークン>という形で付与します。

postman

認証成功の場合

今回作成した Lambda のレスポンスが返却されました。JWT の Claim が取得できますので、user_idemailを用いてユーザを特定した上で、業務ロジックを実行できそうですね。

{
  "jwt": {
    "claims": {
      "aud": "projectId",
      "auth_time": "1638527830",
      "email": "test@example.com",
      "email_verified": "true",
      "exp": "1638543878",
      "firebase": "map[identities:map[email:[test@example.com]] sign_in_provider:password]",
      "iat": "1638540278",
      "iss": "https://securetoken.google.com/projectId",
      "sub": "userId",
      "user_id": "userid"
    },
    "scopes": null
  },
  "email": "test@example.com"
}

認証失敗の場合

リクエストヘッダーに Authorization が設定されていない場合や、トークンが無効の場合はUnauthorizedが返却されます。

{
  "message": "Unauthorized"
}

レスポンスヘッダーに補足情報が付加されている場合があります。こちらを参考にトラブルシュートするといいでしょう。

www-authenticate →Bearer scope="" error="invalid_token" error_description="the token has expired"

まとめ

API Gateway の JWT Authorizer を利用して Firebase ID トークンを検証することができ、バックエンドサービスが Firebase のユーザを特定することができました。(認証)

そして、カスタムクレームを活用することでリソースアクセスの権限制御を行うことができました。(認可)

主に AWS で構築しているサービスだとしても、Firebase の利点を活かせる部分があれば、是非 Firebase で構築してみてはいかがでしょうか。

Avatar of Author

わたるくん

I ♥ Firebase / 目指せフルスタックエンジニア!