Firebase AuthenticationでFacebookログインを実装するために必要な手順

## はじめに

https://firebase.google.com/docs/auth/web/facebook-login?hl=ja

に方法が書かれているので何もブログ記事にしなくても…と思ったが、Facebookの方でOpenID Connect（OAuth2.0）のクライアントを作成する部分で少し躓いたというか、わかりにくい部分があったので自身の備忘録の意味も込めてメモ記事を残しておこうと思う。

## FacebookでOpenID Connect（OAuth2.0）のクライアント（client_id, client_secret）を払い出す

前提としてFacebookアカウントを持っている状態で、 https://developers.facebook.com/ にアクセスして右上の`ログイン`をクリック。

以下のログインの画面になるのでそこからログイン。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/d7e124a4-c695-ee4e-e296-44e86fb325f3.png)

ログインしたら`アプリを作成`からアプリを作成する。
| 手順 |  |
|---|:-:|
| アプリにリンクするビジネスポートフォリオを選択 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/a190ee19-3091-d310-78fb-b66c6d09e59e.png) |
| 必要な機能を選択 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/91016b7d-2ae5-a77b-3b1f-0c82063194ce.png) |
| アプリ名等を設定 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/0fa3bc02-966e-bcc3-c80d-c1e37f757ffa.png) |

アプリの作成をした後は、アプリのダッシュボードの左のメニューから`アプリの設定 > ベーシック`を選択して、作成したアプリ（クライアント）の情報を確認する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/39b96572-d699-a823-4685-e1237f63d673.png)

上記のスクショの`アプリID`と`app secret`がOpenID Connect（OAuth2.0）のclient_idとclient_secretなので、この値をメモしておく。

メモした値をFirebase Authenticationの設定画面で入力する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/412caedc-3cdd-23c8-856c-b754735eaba5.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/a7a3e046-88b2-b5e9-b51a-887d684b01c2.png)

ここまでで一旦、クライアントを払い出す作業は完了になる。これで設定終わり！というわけではなく、リダイレクトURIの設定などが残っている。それについては次の項で見ていく。

## リダイレクトURIの設定と利用者にリクエストできるアクセス許可の設定

Firebase AuthenticationはOpenID Connect（OAuth2.0）の認可コードフロー（Authorization Code Flow）でそのscopeに`openid`, `profile`などを指定してIDトークンをOIDCプロバイダーから返してもらうことで認証を行っている（[おまけ](#おまけ)で見るように、Facebookログインは少し事情が違うようだが…）。

そのための設定が必要で、以下のスクショの`ユースケース`から追加の設定を行う必要がある。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/980e585c-504f-458f-112c-e178330610f1.png)

まず、Facebookでログインの際にユーザーに対しアクセス許可を要求できるものは限られており、[アプリレビュー](https://developers.facebook.com/docs/facebook-login/overview#review)の項に書かれている通り。ただ今回は認証目的なので、アプリレビュー不要で利用できる`public profile`と`email`のみを利用する。

:::note info
当初、アプリレビューが必要か否かでよくわからなかった部分があった。
結論から言うとFirebase Authenticationでの認証用にFacebookログインを利用する場合は、scopeには`public_profile`と`email`のみ指定されるので、アプリレビューは不要。
:::

`ユースケース`の`カスタマイズ`をクリックすると、アクセス許可の編集ができる画面になるので、ここで`email`を追加する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/f9a83b1b-2738-b0c1-88ff-4c01ba0e86f6.png)

続いてリダイレクトURIの設定を行う。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/17941c1d-37c7-0953-e8d2-f2ec5bd500ce.png)

`有効なOAuthリダイレクトURI`にFirebaseのデフォルトのドメインのパス（以下のようなパス）を追加する。もし、カスタムドメインを利用する場合は、そのカスタムドメインのパスも追加する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/0aba60b0-f467-b97d-880e-52779d4cf8ab.png)

ここまでの設定ができれば、Firebase AuthenticationでFacebookログインが利用できるようになる。

## まとめとして

今回はFirebase AuthenticationでFacebookログインを利用するために必要な、Facebook側の設定についてみてきた。アプリレビューの必要性で混乱したが、最終的には不要という事が確認できたのでよかった（アプリレビューはビジネス情報が必要で個人とかだとどうするのだろうか…と思っていたので）。

## おまけ

Firebase Authenticationの認可リクエストのscopeを実際のリクエストのスナップから確認してみると、以下のようになっていることが確認できた。

### Googleログイン

```
https://accounts.google.com/o/oauth2/auth/oauthchooseaccount?response_type=code&client_id=*****&redirect_uri=https://*****.com/__auth/handler&state=*****&scope=openid https://www.googleapis.com/auth/userinfo.email profile&prompt=login&context_uri=https://*****.com&service=lso&o2v=1&ddm=0&flowName=GeneralOAuthFlow
```

- response_type=code
認可コードフロー（Authorization Code Flow）であることを指定している
（詳細は[1. response_type=code](https://qiita.com/TakahikoKawasaki/items/4ee9b55db9f7ef352b47#1-response_typecode)などを参照）

- `scope=openid https://www.googleapis.com/auth/userinfo.email profile`
`openid`のスコープを含むのでIDトークンを要求しており、かつ、[5.4.  Requesting Claims using Scope Values](https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims)のprofileをしていることで、IDトークンには[5.1.  Standard Claims](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims)に書かれている`name`などのクレームが含まれることになる。これを利用してログイン時にプロフィール情報をFirebase Authenticationを利用して取得できている。
※`https://www.googleapis.com/auth/userinfo.email`はOIDCの[5.4.  Requesting Claims using Scope Values](https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims)にはないが、Google側で独自に定義していると思われる（なんとなくメールアドレスを取得するものとはわかるが、OIDCの仕様にある`email`ではだめだったのだろうか…。https://accounts.google.com/.well-known/openid-configuration を見ると、`scopes_supported`に`email`もあるようなので謎だなと思った。）

### Facebookログイン

```
https://www.facebook.com/login.php?skip_api_login=1&api_key=*****&kid_directed_site=0&app_id=*****&signed_next=1&next=https://www.facebook.com/v8.0/dialog/oauth?response_type=code,granted_scopes&client_id=*****&redirect_uri=https://*****.com/__auth/handler&state=*****&scope=public_profile,email&prompt=login&context_uri=https://oniku-map.com&ret=login&fbapp_pres=0&logger_id=*****&tp=unspecified&cancel_url=https://*****.com/__auth/handler?error=access_denied&error_code=200&error_description=Permissions+error&error_reason=user_denied&state=*****
```

- response_type=code,granted_scopes
`code`なので認可コードフロー（Authorization Code Flow）であることを指定している
`granted_scopes`は何か？だが、まずOIDCにはこの値は少なくとも仕様にない（[3.  Authentication](https://openid.net/specs/openid-connect-core-1_0.html#Authentication)や[OpenID Connect 全フロー解説](https://qiita.com/TakahikoKawasaki/items/4ee9b55db9f7ef352b47)を参照）。Facebookのドキュメントを読むと、[Logging People In](https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow/?locale=en_US#login)に以下のように書かれていた。つまりは、ユーザがアプリに付与した全ての権限をカンマ区切りで返させるためのものみたいが、これはOIDCとしてはありなのかはよくわからなかった。
> granted_scopes. Returns a comma-separated list of all Permissions granted to the app by the user at the time of login. Can be combined with other response_type values. When combined with token, response data is included as a URL fragment, otherwise included as a URL parameter.

- scope=public_profile,email
`openid`のスコープを含まないので、IDトークンが返されることはない。ので、アクセストークンを使って[Graph API User](https://developers.facebook.com/docs/graph-api/reference/user/?locale=en_US)を叩いてユーザー情報を取得するという事になると思われる。
OIDCなのに（と私は理解しているがFacebookログインはOIDCではない？！のかも）IDトークンを発行しないのはなぜ？という感じはするが、一応ユーザー情報は取得できるのでFirebase側でうまくやっていると思われる（[IDの確認](https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow/?locale=ja_JP#confirm)にもaccess_tokenはあるがid_tokenはない）

※追記：[OIDC Code Flow with PKCE for Manually Built Facebook Login Flows](https://developers.facebook.com/docs/facebook-login/guides/advanced/oidc-token?locale=ja_JP)というページを見つけたが、これがいわゆるOIDCであり、Firebaseにおいて利用しているものはOIDCではなく、アクセストークンと利用してAPIを叩きユーザー情報を取得して認証を行っている可能性が高いと思われる。

※OIDCプロバイダーによって微妙に違う？のか、Facebookの方は実装違反？があるのではないかと思われる

- response_typeの区切り文字
半角スペースだと思うが、カンマ区切りになっている…

- scopeの区切り文字
半角スペースだと思うが、カンマ区切りになっている…（詳細は[2. スコープ名の区切文字の間違い](https://qiita.com/TakahikoKawasaki/items/efbbd2c5875577c911a3#2-%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%97%E5%90%8D%E3%81%AE%E5%8C%BA%E5%88%87%E6%96%87%E5%AD%97%E3%81%AE%E9%96%93%E9%81%95%E3%81%84)を参照）

※ちなみに、https://www.facebook.com/.well-known/openid-configuration を確認すると、以下のように`response_type`には設定できる値に`code`は含まれていなかった…。Facebookログインには謎が深まるばかり…。
```
  "response_types_supported": [
    "id_token",
    "token id_token"
  ],
```

## 参考

- [Facebookログイン](https://developers.facebook.com/docs/facebook-login)