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 がアタッチされていることが分かります。
Lambda
API Gateway がトリガーの Lambda が作成されています。そして、コンテナイメージがデプロイされていることが分かります。
動かしてみる
以下のドキュメントを参考に、Firebase Authenticate の ID トークンを取得しましょう。
curl や Postman を用いて、今回作成した API Gateway のエンドポイントにトークンを付与してリクエストを送ってみましょう。
トークンの付与は、HTTP リクエストヘッダーAuthorization
にBearer <トークン>
という形で付与します。
認証成功の場合
今回作成した Lambda のレスポンスが返却されました。JWT の Claim が取得できますので、user_id
やemail
を用いてユーザを特定した上で、業務ロジックを実行できそうですね。
{
"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 で構築してみてはいかがでしょうか。