Firebaseのapiなどに関する基本的なことまとめ

# Firebaseとは
Firebaseは、モバイルおよびウェブアプリケーションの開発を簡素化するためのクラウドサービスおよびバックエンドプラットフォームです。Googleが提供しており、開発者がアプリケーションを迅速に構築、管理、拡張できるように、豊富なツールとサービスを提供しています。

主な機能
1. **Authentication（認証)**
1. Firestore（データベース）
1. Firebase Cloud Messaging（FCM）
1. Firebase Storage（ファイルストレージ）
1. Firebase Analytics（分析）
1. Firebase Cloud Functions（サーバーレス関数）
1. Firebase Hosting（ホスティング）

今回は、Authenticationの紹介です。

## Firebaseの利用に関して

REST APIを直接叩く方法は、低レベルのコントロールが必要な場合や、特定のプラットフォームに依存せず、柔軟にカスタマイズしたい場合に適しています。しかし、手動でリクエストを作成するため、開発工数やセキュリティ面でのリスクが増えます。
([Firebase Auth Rest API reference](https://firebase.google.com/docs/reference/rest/auth?hl=ja))

SDKを使う方法は、開発の効率化やセキュリティの強化を重視する場合に適しています。SDKを使うことで、API呼び出しが簡素化され、内部で行われる認証やトークン管理などが自動化されますが、プラットフォームに依存することと、柔軟性に制限があることに注意が必要です。
([opensource](https://firebaseopensource.com/))

次の[Firebaseが提供しているAPI](https://qiita.com/drafts#firebase%E3%81%8C%E6%8F%90%E4%BE%9B%E3%81%97%E3%81%A6%E3%81%84%E3%82%8Bapi)では、REST APIを叩く方法を紹介しています。

# Firebaseが提供しているAPI

## refreshTokenからidToken(アクセストークン)の取得
https://firebase.google.com/docs/reference/rest/auth?hl=ja#section-refresh-token

- SDKでの関数名：-
- Content-Type: application/x-www-form-urlencoded
- エンドポイント：
`POST https://securetoken.googleapis.com/v1/token?key=[API_KEY]`
- リクエスト本文ペイロード

    |プロパティ名	|型	|説明|
    |-|-|-|
    |grant_type	|文字列	|更新トークンの付与タイプ。常に「refresh_token」です。|
    |refresh_token	|文字列	|Firebase Auth の更新トークン。|

- レスポンス本文ペイロード

    |プロパティ名	|型|	説明|
    |-|-|-|
    |expires_in	|文字列|	ID トークンの有効期間（秒）。|
    |token_type	|文字列|	更新トークンのタイプ。常に「Bearer」です。|
    |refresh_token	|文字列|	リクエストで指定された Firebase Auth の更新トークン、または新しい更新トークン。|
    |id_token	|文字列|	Firebase Auth ID トークン。|
    |user_id	|文字列|	提供された ID トークンに対応する uid。|
    |project_id	|文字列|	Firebase プロジェクト ID。|


## email + パスワードで登録
https://firebase.google.com/docs/reference/rest/auth?hl=ja#section-create-email-password

- SDKでの関数名：**signupNewUser**
- Content-Type: application/json
- エンドポイント： 
`POST https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=[API_KEY]` 
- リクエスト本文ペイロード

    |プロパティ名	|型	|説明|
    |---|---|---|
    |メール	|文字列	|作成するユーザーのメールアドレス。|
    |パスワード	|文字列	|作成するユーザーのパスワード。|
    |returnSecureToken	|ブール値|	ID と更新トークンを返すかどうか。常に true を指定します|
- レスポンスのペイロード

    |プロパティ名	|型	|説明|
    |-|-|-|
    |idToken	|文字列	|新しく作成されたユーザーの Firebase Auth ID トークン。|
    |メール	|文字列	|新しく作成されたユーザーのメールアドレス。|
    |refreshToken	|文字列	|新しく作成されたユーザーの Firebase Auth 更新トークン。|
    |expiresIn	|文字列	|ID トークンの有効期間（秒）。|
    |localId	|文字列	|新しく作成されたユーザーの uid。|

## email + パスワードでログイン
[リンク](https://firebase.google.com/docs/reference/rest/auth?hl=ja#section-sign-in-email-password)
- SDKでの関数名：**verifyPassword**
- Content-Type: application/json
- エンドポイント：
`POST https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=[API_KEY]` 
- リクエスト本文ペイロード

    |プロパティ名	|型	|説明|
    |-|-|-|
    |メール	|文字列	|ユーザーがログインに使用するメールアドレス。|
    |パスワード	|文字列	|アカウントのパスワード。|
    |returnSecureToken	|ブール値	|ID と更新トークンを返すかどうか。常に true を指定します|

- レスポンス本文ペイロード

    |プロパティ名	|型	|説明|
    |-|-|-|
    |idToken	|文字列	|認証されたユーザーの Firebase Auth ID トークン。|
    |メール	|文字列	|認証されたユーザーのメールアドレス。|
    |refreshToken	|文字列	|認証されたユーザーの Firebase Auth の更新トークン。|
    |expiresIn	|文字列	|ID トークンの有効期間（秒）。|
    |localId	|文字列	|認証されたユーザーの uid。|
    |registered	|ブール値	|メールアドレスが既存アカウントのものかどうか。|



## Gmail の OAuth で登録
登録はログインと同じAPIなので、開発者側が意識する必要はありません。
ログインと登録で処理を分けたい場合には、FirebaseのgetAccountInfo APIを使用して、特定のidTokenを持つユーザーがFirebaseに既に存在するかを確認できます。
この方法で、指定したGoogleアカウントが新規か既存かをプログラム的に判断することができます。
~~??(そもそもidtokenが取得できないのでは?)~~(ローカルストレージのrefreshTokenを利用してidTokenが取得可能です。)

## Gmail の OAuth でログイン (一番難しいです)
OAuthにおいて、Googleが提供する認証の範囲と、自分たちのアプリで実装すべき部分を明確にする必要があります。

[OAuthに関する他のqiita](https://qiita.com/kato10315/items/cc40b60e211056094846) を参考にすると、signInWithIdpエンドポイントにアクセストークンを送信する際、リダイレクトURIを指定する理由が疑問に思えます。しかし、これは認可コードの取得目的ではなく、セキュリティ上の検証のために必要とされています。

[リンク](https://firebase.google.com/docs/reference/rest/auth?hl=ja#section-sign-in-with-oauth-credential)
- SDKでの関数名：**verifyAssertion**
- Content-Type: application/json
- エンドポイント：
`POST https://identitytoolkit.googleapis.com/v1/accounts:signInWithIdp?key=[API_KEY]`
- リクエスト本文ペイロード

    |プロパティ名	|型	|説明|
    |-|-|-|
    |requestUri	|文字列	|IDP がユーザーをリダイレクトする URI。|
    |postBody	|文字列	|認証情報を発行する OAuth 認証情報（ID トークンまたはアクセス トークン）とプロバイダ ID が含まれます。|
    |returnSecureToken	|ブール値	|ID と更新トークンを返すかどうか。常に true を指定します。|
    |returnIdpCredential	|ブール値	|FEDERATED_USER_ID_ALREADY_LINKED と EMAIL_EXISTS エラーで OAuth 認証情報を強制的に返すかどうか。|

- レスポンス本文ペイロード

    |プロパティ名|	型|	説明|
    |-|-|-|
    |federatedId|	文字列|	一意の ID により IdP アカウントが識別されます。|
    |providerId|	文字列|	リンクされたプロバイダ ID（Google プロバイダの「google.com」など）。|
    |localId	|文字列|	認証されたユーザーの uid。|
    |emailVerified	|ブール値	|ログイン メールアドレスが確認済みかどうか。|
    |メール	|文字列|	アカウントのメールアドレス。|
    |oauthIdToken	|文字列|	OIDC ID トークン（使用可能な場合）。|
    |oauthAccessToken	|文字列|	OAuth アクセス トークン（使用可能な場合）。|
    |oauthTokenSecret	|文字列|	OAuth 1.0 トークン シークレット（使用可能な場合）。|
    |rawUserInfo	|文字列|	指定された OAuth 認証情報に対応するすべての IdP データを含む、文字列化された JSON レスポンス。|
    |firstName	|文字列|	アカウントの名。|
    |lastName	|文字列|	アカウントの姓。|
    |fullName	|文字列|	アカウントの氏名。|
    |displayName	|文字列|	アカウントの表示名。|
    |photoUrl	|文字列|	アカウントの写真の URL。|
    |idToken	|文字列|	認証されたユーザーの Firebase Auth ID トークン。|
    |refreshToken	|文字列|	認証されたユーザーの Firebase Auth の更新トークン。|
    |expiresIn	|文字列|	ID トークンの有効期間（秒）。|
    |needConfirmation	|ブール値|	同じ認証情報を持つ別のアカウントがすでに存在するかどうか。ユーザーは、元のアカウントにログインして、現在の認証情報をリンクする必要があります。|

## email + パスワード で登録後に OAuthで登録(リンク)
https://firebase.google.com/docs/reference/rest/auth?hl=ja#section-link-with-oauth-credential

前述のGmailのOAuthでのログインと比較してリクエストのパラメータが一つ違うだけである。
- SDKでの関数名：**verifyAssertion**
- エンドポイント：
`POST https://identitytoolkit.googleapis.com/v1/accounts:signInWithIdp?key=[API_KEY]`
- リクエスト本文ペイロード

    |プロパティ名	|型|	説明|
    |-|-|-|
    |idToken	|文字列|	認証情報をリンクするアカウントの Firebase ID トークン。|
    |requestUri	|文字列|	IDP がユーザーをリダイレクトする URI。|
    |postBody	|文字列|	認証情報を発行する OAuth 認証情報（ID トークンまたはアクセス トークン）とプロバイダ ID が含まれます。|
    |returnSecureToken	|ブール値|	ID と更新トークンを返すかどうか。常に true を指定します。|
    |returnIdpCredential	|ブール値|	FEDERATED_USER_ID_ALREADY_LINKED と EMAIL_EXISTS エラーで OAuth 認証情報を強制的に返すかどうか。|

- レスポンス本文ペイロード

    |プロパティ名	|型|	説明|
    |-|-|-|
    |federatedId	|文字列|	一意の ID により IdP アカウントが識別されます。|
    |providerId	|文字列|	リンクされたプロバイダ ID（Google プロバイダの「google.com」など）。|
    |localId	|文字列|	認証されたユーザーの uid。|
    |emailVerified	|ブール値|	ログイン メールアドレスが確認済みかどうか。|
    |メール	|文字列|	アカウントのメールアドレス。|
    |oauthIdToken	|文字列|	OIDC ID トークン（使用可能な場合）。|
    |oauthAccessToken	|文字列|	OAuth アクセス トークン（使用可能な場合）。|
    |oauthTokenSecret	|文字列|	OAuth 1.0 トークン シークレット（使用可能な場合）。|
    |rawUserInfo	|文字列|	指定された OAuth 認証情報に対応するすべての IdP データを含む、文字列化された JSON レスポンス。|
    |firstName	|文字列|	アカウントの名。|
    |lastName	|文字列|	アカウントの姓。|
    |fullName	|文字列|	アカウントの氏名。|
    |displayName	|文字列|	アカウントの表示名。|
    |photoUrl	|文字列|	アカウントの写真の URL。|
    |idToken	|文字列|	認証されたユーザーの Firebase Auth ID トークン。|
    |refreshToken	|文字列|	認証されたユーザーの Firebase Auth の更新トークン。|
    |expiresIn	|文字列|	ID トークンの有効期間（秒）。|

## アカウント削除
https://firebase.google.com/docs/reference/rest/auth?hl=ja#section-delete-account

- SDKにおける関数名：**deleteAccount**
- エンドポイント：
    `POST https://identitytoolkit.googleapis.com/v1/accounts:delete?key=[API_KEY]`
- リクエスト本文ペイロード

    |プロパティ名	|型|	説明|
    |-|-|-|
    |idToken	|文字列|	削除するユーザーの Firebase ID トークン。|

- レスポンスのペイロード

    |プロパティ名	|型	|説明|
    |-|-|-|

## ユーザーデータの取得
https://firebase.google.com/docs/reference/rest/auth?hl=ja#section-get-account-info

- SDKにおける関数名：**getAccountInfo**
- エンドポイント：
    `POST https://identitytoolkit.googleapis.com/v1/accounts:lookup?key=[API_KEY]`
- リクエスト本文ペイロード
    
    |プロパティ名	|型	|説明|
    |-|-|-|
    |idToken	|文字列	|アカウントの Firebase ID トークン。|

- レスポンスのペイロード

    |プロパティ名	|型	|説明|
    |-|-|-|
    |ユーザー	|JSON オブジェクトのリスト	|指定された Firebase ID トークンに関連付けられているアカウント。詳しくは以下をご覧ください。|
    
    省略します。

# Firebaseが提供しているAPIに必要な情報の取得

## APIキー の取得
1. Firebase プロジェクト作成時に 自動で API キーが発行される
1. Firebase コンソールの 「プロジェクトの設定」→「全般」 で取得可能
1. API キーは IP制限や Firebase Authentication ルールで保護する のが推奨

## idToken
Firebaseの idToken は、以下のいずれかの方法で取得：

1. Google OAuth を通じて signInWithIdp から取得
1. Firebase の signInWithPassword API でログインして取得
1. すでに refreshToken を持っている場合、securetoken.googleapis.com で idToken を更新

この idToken を Firebase の各種 API (getAccountInfo など) に渡すことで、認証が完了する。

idTokenの管理は、

1. ✅ Firebase SDK を使う場合
    - 自動的にリフレッシュトークンを管理 するため、開発者がリフレッシュ処理を実装する必要はない。
    - onAuthStateChanged() でユーザーのログイン状態を監視。
1. ✅ REST API を使う場合
    - idToken をローカルストレージに保存
    - 期限切れ時に refreshToken を使って新しい idToken を取得
    - 必要なら バックエンドと連携して idToken の更新処理を実装

# Firebaseが提供しているAPIにより得られる情報の利用方法

## idToken
アクセストークンと同じ使い方です。
リクエストする際にヘッダーに付与します。

### バックエンド側が正しいidTokenかを確認する
https://firebase.google.com/docs/auth/admin/verify-id-tokens?hl=ja#verify_id_tokens_using_the_firebase_admin_sdk

Firebase では、バックエンド側でも idToken の検証方法として SDK を利用する方法 と JWT を手動で検証する方法 の 2 種類があります。
ここでは、Firebase Admin SDK を使用する方法 を紹介します。
```python
    # トークンの検証
    decoded_token = auth.verify_id_token(id_token)
    uid = decoded_token['uid']
```
verify_id_token() を使用すると、署名の検証・有効期限の確認・発行元のチェック を自動で行い、
正当なトークンであれば uid を取得できます。

※ JWT を手動で検証する方法については、ここでは省略します。

## refreshToken
idTokenが期限切れの際に、再発行するために必要です。

## localId (uid)
ユーザーを一意に識別するために必要です。

# まとめ
Firebaseの簡単な紹介を行った。
Firebaseの公開しているREST APIとラッパーのSDKが存在するが、REST APIの一部を紹介した。
REST APIで出現するリクエストパラメータの取得方法、レスポンスパラメータの利用方法を紹介した。