Flutter × FirebaseAuth 自作APIの認証について
現在Flutterアプリを開発中です。自前のサーバーを立ててAPIを作成するということが良くあると思います。その中でAPIに正規のユーザーしか受け付けないように認証を実装します。FirebaseでFlutterアプリの認証を行なっているのでAPIの認証もFirebaseに任せたいと思います。今回の構成図は以下の通りです。
JWTとは
Json Web Tokenの略で読み方はジョット。JSONデータ構造で表現したトークンの仕様です。
JWTは長い文字列になっていますが . によって3つに分割することができます。構成は<ヘッダー>.<ペイロード>.<署名>となってます。JWT実際にデコードしていってみます。色々検証してみたい方はこちら↓↓↓
ヘッダー
ヘッダーはJWTの署名検証を行うために必要な情報を格納しています。"typ"はJWTであることを示し、"alg"は署名アルゴリズムを示しています。公開鍵方式であり署名の作成に秘密鍵を利用。署名の検証に 公開鍵を利用。"kid"は鍵IDとなっています。
{
"alg": "RS256",
"kid": "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
"typ": "JWT"
}
ペイロード
ペイロードはやりとりに必要な属性情報(Claim)です。送信したい情報を格納しています。
{
"provider_id": "anonymous",
"iss": "https://securetoken.google.com/xxxxxxxxxxxxx",
"aud": "xxxxxxxxxxxxxxxxxxx",
"auth_time": 1234567890,
"user_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
"sub": "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
"iat": 1234567890,
"exp": 1234567890,
"firebase": {
"identities": {},
"sign_in_provider": "anonymous"
}
}
署名
署名は、<エンコードしたヘッダー>.<エンコードしたペイロード>を連結したものを入力値として"alg"の署名アルゴリズムで署名し、Base64urlエンコードすることにより作成されます。これをJWTで送られてきた署名と比較して一致すれば認証成功になります。
実践編
Firebase Admin SDKを導入する
サーバーサイド側(Node.js)にFirebase Admin SDKを導入します。メリットとしては比較的シンプルなコードでJWTを検証することができます。Firebaseでサポートされていない言語の場合でもサードパーティのJWTライブラリを使用して検証することもできます。
JWTを取得する
クライアント側で(Flutter)でJWTを取得します。
公式に以下のような記述があります↓↓↓
Firebase Authentication セッションは長期間有効です。ユーザーがログインするたびに、ユーザー認証情報が Firebase Authentication のバックエンドに送信され、Firebase ID トークン(JWT)および更新トークンと交換されます。Firebase ID トークンの有効期間は短く、1 時間で期限切れとなります。
String idToken = await FirebaseAuth.instance.currentUser!.getIdToken();
JWTを付与してリクエストを行う
クライアントがJWTをヘッダーに含めて送信します。
Authorization: Bearer <JWT>
JWTを検証する
サーバーサイド側(Node.js)でJWTの検証はFirebase Admin SDKによって簡単にできます。Firebaseが送信したidTokenか改ざんされていないidTokenかを検証します。
admin.auth().verifyIdToken(idToken).then((decodedToken) => {
// 認証成功
const uid = decodedToken.uid;
}).catch((error) => {
// 認証失敗
});
参考文献
・JWTについて
・Firebase公式
・その他参考
Discussion