AndroidでFirebase Authenticationを利用してGoogle認証ログインする

# はじめに
Firebase Authenticationを設定する記事や、Google認証でログインする実装を書いた記事はたくさん見かけるのですが、結局、何を設定して実装すれば出来上がるのか通しで書いてある記事がなかなか見つけられませんでした。
様々な記事を参考にしてGoogle認証でログインするところまで実装することは出来たのですが、途中でエラーで躓いたり、色々な調査をしたりと、ちょっと大変でした。
そこで改めて今回の記事で、プロジェクトの設定から実装、そしてGoogle認証の実行が出来るところまでを通しで書いていきたいと思います。
# Firebase Authenticationとは
パスワード認証やOAuth2に対応したフェデレーションIDを使った認証をアプリケーションに組み込む事ができるサービスの一つです。このサービスを利用する事で、簡単に、そして無料でユーザー認証の機能を実現する事が出来てしまいます。
今回は、特にその中でもユーザー操作が簡単なフェデレーション認証の１つ、Google認証を利用したログインの実装について投稿したいと思います。
Androidアプリに組み込む場合、Androidユーザーであれば誰でも持っているGoogleアカウントを利用するため、幅広い用途で利用出来ると思います。
# この記事での完成イメージ
この投稿で説明する内容の完成イメージは以下です。
![LoginImage.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545703/8fc056db-375b-1717-d881-26166ffc38aa.png)
Googleログインボタンを押下し、Googleアカウントを選択する事でユーザー認証が完了します。

# １．Firebaseのプロジェクトを追加する
まずは、[Firebase プロジェクト](https://console.firebase.google.com/?hl=ja)の画面に行って新しいプロジェクトを作成します。

![CreateProject.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545703/02fb106d-2e49-9b7a-42a4-35e9e53058e4.png)

上記の「プロジェクトを追加」を選択します。
その後、プロジェクトの名前を決めたりと、簡単な4ステップがあるので指示通りにプロジェクトを作成します。
# ２．プロジェクトのAuthenticationを選択
プロジェクトを作成するとプロジェクトのコンソール画面に入りますので、左側のツールメニューからAuthenticationを選択して、「始める」を選択します。
![SelectAuthentication.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545703/523870ee-eb72-ab8c-c04e-698dca8da9dc.png)

# ３．ログイン方法の追加でGoogleを有効にする
以下の手順に沿ってGoogle認証を有効にしていきます。
## ３．１．プロバイダを選択
追加のプロバイダからGoogleを選択します。
![SelectProvider.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545703/c925eeed-e3ab-0b15-7c4e-bc68dbcb3bd0.png)
## ３．２．ログインプロバイダを有効にする
Googleのログインプロバイダを有効にします
![SetEnable.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545703/66dd5c6a-67de-00bd-a58d-1c13396cee33.png)
## ３．３．プロジェクトの設定をして保存
プロジェクトの公開名の設定とメールアドレスを設定して保存します。
プロジェクトの公開名はプロバイダを有効にした時点で勝ってに名前が設定されるので、基本的にはそのままでOKです。メールアドレスはプルダウンから自分のgmailのアドレスが選択出来ます。
![SetParameter.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545703/fe08e592-58bf-c95e-b481-9d5456118035.png)
以下のようにGoogleが有効になっていればOKです。
![SetComplete.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545703/7e00ab39-998a-0a23-639f-b3679ba8ef72.png)

# ４．AndroidStudioからAuthenticationの設定
Android Studioで新しいアプリのプロジェクトを作成してある前提で説明していきます。
Firebase Authenticationの設定を以下の手順に沿って行います。
基本的にはアシスタントの機能を利用すれば簡単に設定する事が出来ます。
## ４．１．Firebaseアシスタントを開く
Android StudioのTool→Firebaseを選択します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545703/70f888a4-674b-746f-8a2c-470a61f9e147.png" width=50%/>
## ４．２．Firebaseプロジェクトと接続する
以下のアシスタントの画面の「①Connect your app to Firebase」から「Connect to Firebase」を選択します。
![AsistantConnect.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545703/5bb52f25-47df-70ab-53b9-602c685a884f.png)
すると、FirebaseのWebサイトに飛ばされるので、最初に作成したプロジェクトを選択します。
![ConnectProject.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545703/e3f85fbd-62be-1c18-9405-55a53c36f2cf.png)
## ４．３．gradleにfirebase-authを追加する
アシスタントの「②Add the Firebase Authentication SDK to your app」の「Add the Firebase Authentication SDK to your app」を選択します。
![AsistantAddSDK.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545703/c7aee50d-e2c2-349f-e9e1-ee9efc15bbc8.png)
以下のようにbuild.gradleに追加するようダイアログが表示されるので、指示通りにgradleを修正して同期します。
![AddGradle.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545703/786964ea-c321-22aa-b7ee-b0b15a3f04a5.png)
ここで、一旦ビルドしておくことをお勧めします。ビルドする事でこの後の実装で必要とするリソースデータが生成されるからです。
## ４．４．フィンガープリントを設定する
アシスタントの「③Complete your set up in the Firebase Console」にあるように、Firebaseコンソール画面でフィンガープリントを設定する必要があります。
フィンガープリントはデバッグ用とリリース用の２つ設定しておくと良いです。
開発中はデバッグ用のフィンガープリントが必要になり、ストア用のSigned Buildしたアプリでは、リリース用のフィンガープリントが必要になります。
### ４．４．１．デバッグ用のフィンガープリントの生成
まずは、デバッグ用のフィンガープリントから。

Android StudioのTerminalを選択して、以下のコマンドを実行します。

```
keytool -list -v -keystore /Users/<ユーザー名>/.android/debug.keystore -alias androiddebugkey
```

すると以下のようにパスワードを求められるので、「android」と入力します。
![FingerPassword.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545703/3cb37efb-a4c4-e109-d97a-585187f52fc2.png)
実行結果は以下のようになります。

```
[01:52:47 kouichi Ponshu:501]$ keytool -list -v -keystore /Users/<ユーザー名>/.android/debug.keystore -alias androiddebugkey
キーストアのパスワードを入力してください:  
別名: androiddebugkey
作成日: 2018/02/10
エントリ・タイプ: PrivateKeyEntry
証明書チェーンの長さ: 1
証明書[1]:
所有者: C=US, O=Android, CN=Android Debug
発行者: C=US, O=Android, CN=Android Debug
シリアル番号: 1
有効期間の開始日: Sat Feb 10 23:51:37 JST 2018終了日: Mon Feb 03 23:51:37 JST 2048
証明書のフィンガプリント:
         SHA1: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
         SHA256: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
署名アルゴリズム名: SHA1withRSA (弱)
サブジェクト公開キー・アルゴリズム: 1024ビットRSAキー(弱)
バージョン: 1

Warning:
証明書はSHA1withRSA署名アルゴリズムを使用しており、これはセキュリティ・リスクとみなされます。このアルゴリズムは将来の更新で無効化されます。
証明書は1024ビットRSAキーを使用しており、これはセキュリティ・リスクとみなされます。このキー・サイズは将来の更新で無効化されます。
JKSキーストアは独自の形式を使用しています。"keytool -importkeystore -srckeystore /Users/<ユーザー名>/.android/debug.keystore -destkeystore /Users/<ユーザー名>/.android/debug.keystore -deststoretype pkcs12"を移行することをお薦めします。
```

この出力結果のSHA1の「XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX」をコピーしておきます。
### ４．４．２．フィンガープリントの登録
Firebaseコンソール画面に戻り、以下のようにプロジェクトの設定を選択します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545703/8eb49fc8-4003-dac2-3bb7-522f8bd0725c.png" width=50%/>
「フィンガープリントを追加」を選択して、先ほどコピーしたフィンガープリントをペーストします。
![AddFingerprint.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545703/ee112f08-9ee7-8c08-6af5-43695bb102d0.png)
### ４．４．３．リリース用のフィンガープリントの生成
リリース用のフィンガープリントを生成する場合には、まず、「Generated Signed Bundle / APK」を実行して、リリース用のアプリを生成する手順を実行しておきます。
![keyfile.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545703/e1493c6c-99e4-9a01-f4d0-1b50294a3ff6.png)
その過程で生成するkeyfile.jksを指定して以下のようにコマンドを実行します。

```
keytool -list -v -keystore /<Key store path>/keyfile.jks
```

パスワードが聞かれるので、Key store passwordで設定したパスワードを入力します。
すると、デバッグ用の時と同じようにフィンガープリントが生成されるので、同じようにして登録すれば完了です。
#５．Google認証によるログインの実装
基本的にはアシスタントの以下の手順に沿って実装していきます。
④Integrate Google Sign-in into your app
![AsistantIntegrate.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545703/177b25b4-b7a0-041f-12f9-76771715f604.png)
⑤Get a shared instance and initialize
![AsistantInitialize.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545703/88968e13-b2da-e218-e494-ae79a01257e4.png)
⑥Use a Firebase credential to authenticate with Firebase
![AsistantAuthenticate.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545703/ef02316d-ca47-3f99-b67a-d57cef5a5636.png)
上記の他にも権限の設定や画面レイアウトの実装などもあるので、順を追って説明していきます。
## ５．１．AndroidMAnifest.xmlの修正
ネットワークアクセスが必要なため
・ACCESS_NETWORK_STATE
・INTERNET
の権限を付与します。

```AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.highcom.ponshu">

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />

    <application
```

## ５．２．画面レイアウトにGoogleログインボタンを追加
以下のように、画面にGoogleログインボタンを設定します。

```activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context=".LoginActivity">

    <com.google.android.gms.common.SignInButton
        android:id="@+id/google_sign_in_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
```

## ５．３．Google認証でのログインをアクティビティに実装
画面に設置したGoogleログインボタンを押下してGoogle認証によるログインが出来るように、アクティビティを以下のように実装します。

```LoginActivity.java
public class LoginActivity extends AppCompatActivity {
    private FirebaseAuth mAuth;
    private GoogleSignInClient mGoogleSignInClient;
    private static final int RC_SIGN_IN = 9001;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        mAuth = FirebaseAuth.getInstance();
        mAuth.setLanguageCode("ja");

        GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                .requestIdToken(getString(R.string.default_web_client_id)).requestEmail().build();
        mGoogleSignInClient = GoogleSignIn.getClient(this, gso);

        SignInButton signInButton = findViewById(R.id.google_sign_in_button);
        signInButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                signIn();
            }
        });
    }

    @Override
    protected void onStart() {
        super.onStart();

        FirebaseUser currentUser = mAuth.getCurrentUser();
    }

    private void signIn() {
        Intent signInIntent = mGoogleSignInClient.getSignInIntent();
        startActivityForResult(signInIntent, RC_SIGN_IN);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        // Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...);
        if (requestCode == RC_SIGN_IN) {
            Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
            try {
                // Google Sign In was successful, authenticate with Firebase
                GoogleSignInAccount account = task.getResult(ApiException.class);
                Log.d("LOGIN", "firebaseAuthWithGoogle:" + account.getId());
                firebaseAuthWithGoogle(account.getIdToken());
            } catch (ApiException e) {
                // Google Sign In failed, update UI appropriately
                Log.w("LOGIN", "Google sign in failed", e);
            }
        }
    }

    private void firebaseAuthWithGoogle(String idToken) {
        AuthCredential credential = GoogleAuthProvider.getCredential(idToken, null);
        mAuth.signInWithCredential(credential)
                .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        if (task.isSuccessful()) {
                            // Sign in success, update UI with the signed-in user's information
                            Log.d("LOGIN", "signInWithCredential:success");
                            FirebaseUser user = mAuth.getCurrentUser();
                            Intent intent = new Intent(LoginActivity.this, MainActivity.class);
                            startActivity(intent);
                        } else {
                            // If sign in fails, display a message to the user.
                            Log.w("LOGIN", "signInWithCredential:failure", task.getException());
                            Toast.makeText(LoginActivity.this, "Authentication failed.", Toast.LENGTH_SHORT).show();
                        }
                    }
                });
    }
}
```

# ６．ビルドして実行
以上でプロジェクトの設定から実装までは完了です！
あとは、ビルドして実行してみましょう。Googleログインによる認証が出来ていると思います。
もし、実行して以下のようなApiException:10が発生した場合

```
com.google.android.gms.common.api.ApiException: 10:
```

DEVELOPER_ERRORです。フィンガープリントの設定がうまく出来ていない可能性があるので、改めてフィンガープリントの設定に戻ってやり直してみると良いと思います。
私の場合はこれにハマりました。。。
# さいごに
本来であれば、ユーザー認証を行うためにば、認証サーバーを立てたりユーザーデータに対するセキュリティなど、気をつけなければならない事がたくさんあります。
ですが、Firebase Authenticationを利用する事でそのような煩わしさもなく、ユーザー認証機能が実現出来てしまうのですから凄いです。
そこで、**Qiita10周年記念イベントの10年後のために今勉強しておきたい技術**という事で今回の記事を投稿してみようと思いました。