はじめに
AWSには認証・認可のサービスとしてAmazon Cognitoというものが存在します。ややこしいのですが、認証のためのコンポーネントがAmazon Cognito user pools(以下、user pool)で認可のためのコンポーネントがAmazon Cognito identity pools (以下、identity pool)です。ちなみにidentity poolのほうはfederated identityと表記されている場合もあります。
そのうち、今回はidentity poolの話です。
identity poolは認証機構は持たず、大雑把にいうと任意のログインプロバイダで認証されたユーザに対してIAMロールが設定されたidを紐付けた上でテンポラリのAWSクレデンシャルを提供するといったサービスです。
この任意のログインプロバイダとしてFacebook、Twitterなどがあるんですが、それらに加えてOpenID Connect(以下、OIDC)というオープンな標準に準拠したログインプロバイダであれば利用できることになってます、一応。
今回はこのログインプロバイダとしてFirebase Authenticationを使ってCognitoと連携していきたいと思います。
IdPの設定
Amazon Cognito identity poolsでログインプロバイダとしてOIDCのプロバイダを利用するには先にIAM側でIdentity Providerとして登録しておく必要があります。Cognito側ではその登録済のIdentity Providerを選択するという流れになります。
というわけでまず、IAMで登録します。IAMで登録しようとするとこんな画面で情報入力が求められます。
だがしかし、Firebase Authenticationを使う場合に何をどう入力したらいいのかわかりません。Firebaseのドキュメントにもこのあたりあまり深く書かれてないのです。
が、ググりまくったのと試行錯誤の結果わかりました。以下のように入力すれば大丈夫です。
Provider URL: https://securetoken.google.com/
ポイントはAudienceにFirebase Client IDを入れることです。これで保存したら完了です。
あとは、Amazon Cognitoのidentity poolの編集画面で、Authenticate ProvidersでOpenIDを選択し Enable identity provider
のチェックを入れるだけです。
アプリ側を実装する
AWSの設定はぶっちゃけ大したことないです。問題はアプリ側。今回はJSでやります。SDKなんですがAmplifyではなくAWS SDKを直接使ってます。JSのSDKはv2とv3があってネットに転がってるサンプルはv2のものも多いのですが、今回はv3を使っています。なお、FirebaseのSDKもv8とv9ありますが今回はv9をインストールした上でv8互換の書き方になってます。
Firebase Authを使う場合(というかプロバイダの場合も)、大まかな流れとしては以下になります。
- Firebase Authで認証する
- Firebase Authからログイン中のユーザのIDトークンを取得
- Firebase Authから取得したトークンをCognitoのIDに紐づけて取得
- クレデンシャルを取得
- 取得したクレデンシャルを使ってAWSのリソースを操作
- (Firebase Authでサインアウト)
1は普通にFirebase Authで認証するのと変わりないです。よくあるメールアドレスとパスワードならログイン画面を用意してsignInWithEmailAndPassword
を実行する感じです。
3, 4がちょっと面倒な手続きですがそれさえ終われば、5は普通にクレデンシャル(Access KeyとSecret Access Key)を使ってAWSのAPIを呼び出すだけです。ここも変わりません。
というわけで関連するソースコードのサンプルだけ以下に。ここでは既存のFirebase Authのプロジェクトを使ったのとログイン画面を用意するのが面倒だったのでメールアドレスとパスワードをベタ書きして認証処理に渡していますが、普通はこんなことしてはいけません。また認証にメールアドレスとパスワード以外を使う場合は適宜読み替えてください。このあたりはFirebaseのドキュメントを参考にしてください。
また、今回はあくまでも認証認可周りだけのサンプルなのでまともにreturnもしていません。reactのApp.tsxのApp
内に'signIn'という関数をとりあえずその中で全部やってますが実際にはもうちょっとまともに考えたほうがいいと思います。
あと、このサンプルではS3を使うことを想定してますがその他のAWSのサービスはどれも同じ流れだと思います。クライアントがS3かそうじゃないかだけ。
事前に関連パッケージをyarn
もしくはnpm
でインストールしておいてください。このあたりを入れておきます。
@aws-sdk/client-cognito-identity @aws-sdk/client-s3 @aws-sdk/credential-provider-cognito-identity firebase
TypeScriptの場合は追加で以下も入れておきます。
@aws-sdk/types @types/node
ソースコードはこんな感じです。一応Reactで書いてます。
import React from 'react'; import {S3Client} from '@aws-sdk/client-s3'; import { CognitoIdentityClient, GetIdCommand, } from '@aws-sdk/client-cognito-identity'; import {fromCognitoIdentity} from '@aws-sdk/credential-provider-cognito-identity'; import 'react-native-get-random-values'; import 'react-native-url-polyfill/auto'; import firebase from 'firebase/compat/app'; import 'firebase/compat/auth'; const firebaseConfig = { apiKey: 'xxxxxxxxxxxxxxxxxxxxxxxxx', authDomain: 'xxxxxxxxxxxxxxxxx.firebaseapp.com', databaseURL: 'https://xxxxxxxxxxxxxxxxx.firebaseio.com', projectId: 'xxxxxxxxxxxxxxxxx', storageBucket: 'xxxxxxxxxxxxxxxxx.appspot.com', messagingSenderId: '1234567812345', appId: '1:1234567812345:xxx:abcderghijklmnopqr12345', measurementId: 'G-XXXXXXXXX', }; firebase.initializeApp(firebaseConfig); const App = () => { const region = 'ap-northeast-1'; const poolId = 'ap-northeast-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'; const signIn = async () => { const auth = firebase.auth(); let token; try { const user = await auth.signInWithEmailAndPassword( 'ユーザID', 'パスワード', ); } catch (error) { console.log({error}); } token = await firebase.auth().currentUser?.getIdToken(); const getIdParmeters = { IdentityPoolId: poolId, Logins: { 'securetoken.google.com/xxxxxxxxxxxxxxxxx': token!, }, }; const cognitoClient = new CognitoIdentityClient({region: region}); const res = await cognitoClient.send(new GetIdCommand(getIdParmeters)); const s3client = new S3Client({ region: region, credentials: fromCognitoIdentity({ client: cognitoClient, identityId: res.IdentityId!, logins: { 'securetoken.google.com/xxxxxxxxxxxxxxxxx': token!, }, }), }); await firebase.auth().signOut(); }; return <></>; }; export default App;
一応最後にサインアウトするのをお忘れなく。
まとめ
というわけであまり情報がないなかで試したものの無事にCognitoの外部プロバイダとしてFirebaseを利用することができました。いろんな事情でこのあたりは情報が少ないんだと思うのですがまあ少しでも役に立てば幸い。
そもそもFirebase AuthをOIDCプロバイダとして使えるっていうのがドキュメントでは少し曖昧な感じだったのですがいけそうです。
今回は試してないけれど、きっとUser PoolのFederation先としてFirebaseを連携することも同じような感じで可能なんだと思う。でもこれはあまりやる意味がないよね、きっと。