【React Native】ExpoでFirebase AuthenticationのSNS認証を利用する方法

# まえがき
**CRNA(create-react-native-app)**で作成したアプリケーションで、**Firebase AuthenticationのSNS認証**を行う方法をまとめます。
→こちらで紹介しているGoogleの認証方法はExpo側で廃止予定なようです。。。

※_GoogleアカウントとFacebookアカウントでのSNS認証を解説します。_

# 準備

```
npm install create-react-native-app -g
create-react-native-app firebaseAuthSample
cd firebaseAuthSample
npm install firebase --save
```

# Facebook認証

[Facebook - Expo Documentation](https://docs.expo.io/versions/latest/sdk/facebook)に実装の仕方が書かれています。

流れとして、Expo Client AppでFacebook認証を利用できるよう設定し、Expo APIで取得できるトークンでクレデンシャルを作成し、作成したクレデンシャルでログインを行います。

#### 1. Facebook for DeveloperでIOSのBundle IDとAndroidのKey Hashesを登録する

[Facebook for Developers](https://developers.facebook.com/?locale=ja_JP)でアプリを作成し、設定のベーシックから**IOSのBundle ID**と**AndroidのKey Hashes**を登録しましょう。

<img src="https://docs.expo.io/static/images/generated/v32.0.0/sdk/facebook-app-settings.png"/>

IOSのBundle IDは`host.exp.Exponent`、AndroidのKey Hashesは`rRW++LUjmZZ+58EbN5DVhGAnkX4=`です。

**アプリID**はあとで使うので、メモしておきましょう。

#### 2. Firebase consoleでIOSアプリとAndroidアプリを登録する

[Firebase console](https://console.firebase.google.com/) で「＋アプリを追加」からIOSアプリとAndroidアプリを追加し、**IOSアプリのバンドルID**と**Androidアプリのパッケージ名**に`host.exp.exponent`を登録しましょう。

<img width="984" alt="スクリーンショット 2019-01-16 1.09.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/189146/55ba1cfb-d622-1858-1b1d-ff7dbd29a1e4.png">

#### 3. Facebook認証を実装する

どこからでもすぐ呼び出せる用、認証処理を関数化して実装します。
Expo APIでトークンを取得、Firebase APIでトークンからクレデンシャルを取得、Firebase APIでクレデンシャルを利用してFacebook認証をさせます。

```js
import * as firebase from 'firebase'
import * as Expo from 'expo'

const FACEBOOK_APPID = '<app id>'

const config = {
  apiKey: '<api key>',
  authDomain: '<domain>',
  databaseURL: '<database url>',
  projectId: '<project id>',
  storageBucket: '<sotarge bucket>',
  messagingSenderId: '<id>'
}

firebase.initializeApp(config)

export const authFacebook = async () => {
  try {
    const { type, token } = await Expo.Facebook.logInWithReadPermissionsAsync(
      FACEBOOK_APPID,
      { permissions: ['public_profile'] }
    )

    if (type === 'success') {
      const credential = firebase.auth.FacebookAuthProvider.credential(token)
      return firebase.auth().signInAndRetrieveDataWithCredential(credential).catch((error) => console.log(error))
    }
    else {
      return { cancelled: true }
    }
  }
  catch (e) {
    return { error: true }
  }
}
```

Expo APIの**Expo.Facebook.loginWithReadPermissionsAsync(appId, options)**で先ほどメモしたアプリIDを使用して、「token」と「type」を取得します。

※「type」には認証結果が入ります。
 
```js
const { type, token } = await Expo.Facebook.logInWithReadPermissionsAsync(
  FACEBOOK_APPID,
  { permissions: ['public_profile'] }
)
```

「type」で認証結果をチェックして、Firebase APIの**firebase.auth.FacebookAuthProvider.credential(token)**で「token」を使用して、「credential」を取得します。

Firebase APIの**firebase.auth().signInAndRetrieveDataWithCredential(credential)**で「credential」を使用して、Facebook認証処理を走らせます。

```js
if (type === 'success') {
  const credential = firebase.auth.FacebookAuthProvider.credential(token)
  return firebase.auth().signInAndRetrieveDataWithCredential(credential).catch((error) => console.log(error))
}
else {
  return { cancelled: true }
}
```


# Google認証

[Google - Expo Documentation](https://docs.expo.io/versions/latest/sdk/google)に実装の仕方が書かれています。

流れとして、Expo Client AppでGoogle認証を利用できるよう設定し、Expo APIで取得できるトークンでクレデンシャルを作成し、作成したクレデンシャルでログインを行います。

※_Facebook認証の時と同じ手順です。_

#### 1. Google Developer ConsoleでIOSのバンドルIDとAndroidのパッケージ名を登録する

[Google Developer Console](https://console.developers.google.com/apis/credentials)で「認証情報を作成」→「OAuth クライアント ID」にて**IOSのバンドルID**と**Androidのパッケージ名**を登録する。

※_IOS用とAndroid用にOAuth クライアント IDを2つ作成してください。_

<img width="654" alt="スクリーンショット 2019-01-16 1.30.43.png" src="https://qiita-image-store.s3.amazonaws.com/0/189146/3865f475-ce1d-f280-ec2c-8b80398167b5.png">

IOS(OAuth クライアント ID)のバンドルIDとAndroid(OAuth クライアント ID)のパッケージ名は`host.exp.exponent`です。

また、Android(OAuth クライアント ID)には署名証明書フィンガープリントの項目があるので、ターミナルで`openssl rand -base64 32 | openssl sha1 -c`を実行し、表示された文字を入力してください。

IOSとAndroidの**クライアントID**はあとで使うので、メモしておきましょう。

※_署名証明書フィンガープリントは`A1:B2:C3...`のような形式です。_

#### 2. Firebase consoleでIOSアプリとAndroidアプリを登録する

こちらはFacebook認証の**2. Firebase consoleでIOSアプリとAndroidアプリを登録する**と全く同じなので省略します。

※Facebook認証実装の際すでにアプリを登録しているのであれば、スキップして大丈夫です。

#### 3. Google認証を実装する

どこからでもすぐ呼び出せる用、認証処理を関数化して実装します。
Expo APIでトークンを取得、Firebase APIでトークンからクレデンシャルを取得、Firebase APIでクレデンシャルを利用してGoogle認証をさせます。

※Facebook認証の時と仕組みは変わりません。

```js
import * as firebase from 'firebase'
import * as Expo from 'expo'

const GOOGLE_IOS_CLIENTID = '<google ios client id>'
const GOOGLE_ANDROID_CLIENTID = '<google android client id>'

const config = {
  apiKey: '<api key>',
  authDomain: '<domain>',
  databaseURL: '<database url>',
  projectId: '<project id>',
  storageBucket: '<sotarge bucket>',
  messagingSenderId: '<id>'
}

firebase.initializeApp(config)

export const authGoogle = async () => {
  try {
    const { type, idToken, accessToken } = await Expo.Google.logInAsync({
      behavior: 'web',
      iosClientId: GOOGLE_IOS_CLIENTID,
      androidClientId: GOOGLE_ANDROID_CLIENTID,
      scopes: ['profile', 'email'],
    })

    if (type === "success") {
      const credential = firebase.auth.GoogleAuthProvider.credential(idToken, accessToken)
      return firebase.auth().signInAndRetrieveDataWithCredential(credential).catch((error) => console.log(error))
    }
    else {
      return { cancelled: true }
    }
  }
  catch (e) {
    return { error: true }
  }
}
```

Expo APIの**Expo.Google.logInAsync(options)**で先ほどメモしたIOSのクライアントIDとAndroidのクライアントIDを使用して、「idToken」と「accessToken」と「type」を取得します。

※「type」には認証結果が入ります。

```js
const { type, idToken, accessToken } = await Expo.Google.logInAsync({
  behavior: 'web',
  iosClientId: GOOGLE_IOS_CLIENTID,
  androidClientId: GOOGLE_ANDROID_CLIENTID,
  scopes: ['profile', 'email'],
})
```

「type」で認証結果をチェックして、Firebase APIの**firebase.auth.GoogleAuthProvider.credential(idToken, accessToken)**で「idToken」と「accessToken」を使用して、「credential」を取得します。

Firebase APIの**firebase.auth().signInAndRetrieveDataWithCredential(credential)**で「credential」を使用して、Google認証処理を走らせます。

```js
if (type === "success") {
  const credential = firebase.auth.GoogleAuthProvider.credential(idToken, accessToken)
  return firebase.auth().signInAndRetrieveDataWithCredential(credential).catch((error) => console.log(error))
}
else {
  return { cancelled: true }
}
```

# まとめ

ExpoでFirebase AuthenticationのSNS認証を実装するときは、Expo APIで各プロバイダーのトークンを取得し、Firebase APIでトークンからクレデンシャルを取得して、クレデンシャルにて認証させれば実装できるみたいです。