GoogleアカウントでAWSサービスに接続する

#はじめに
こんにちは。Daddy's Officeの市川です。

私が10年以上開発を続けているWindowsPCを監視カメラシステムにする「LiveCapture3」。
先日、このWindowsアプリにクラウド経由で接続するスマホアプリ「LiveCapture3 Remote」をリリースしました。

クラウドサーバはAWSを使用してサーバレスで構築したので、アプリサインインは、Googleアカウントでサインインし、AWS Cognitoでユーザに対して適切なIAM権限を付与する形で実現しています。

# Googleアカウント認証の有効化

Googleアカウント認証をアプリで使用するには、まず、Googleアカウント認証を有効にする必要があります。

まず[FireBase Console][1]にログインし、（プロジェクトを作成していなければ作成して）左サイドメニューから「Authentication」を選択します。


Authenticationページの「ログイン方法」タブをクリックすると、ログインプロバイダ一覧が表示されますので、一覧から「Google」を選択して、有効にします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/382663/054b3bfa-6c4a-3b14-10c5-f35edcce8d9d.png)


![image.png](https://qiita-image-store.s3.amazonaws.com/0/382663/7b93563a-dceb-6be8-3ff5-a932b399af0d.png)

この段階では、アプリ情報の追加などはせずに、一旦保存します。

保存後、[Google Developer Console][2]にアクセスし、「認証情報」ページを開くと、OAuth2.0クライアントIDの一覧に、Web client用のIDが自動的に追加されているはずです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/382663/f8199759-876c-07f2-493f-fce85c70e8b6.png)

このWeb client用のID「xxxxxxxxxxx.apps.googleusercontent.com」は、AWSのIDプロバイダ設定で使用しますので、コピーしておいてください。


# IDプロバイダーの追加(AWS)

Googleアカウントで認証済みのアプリからのアクセスに対して、AWSサービスへアクセスするAWS認証情報を発行するために、CognitoのIDプールとGoogleアカウントを連携させます。

CognitoのIDプールには、Google+が選択肢として出てきますが、Google+はすぐに使えなくなりそうなので、別途、OpenIDのプロバイダを作成します。

IAMの「IDプロバイダー」を選択して、「プロバイダの作成」ボタンをクリックします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/382663/e06e86fe-1617-160f-49fa-b508acbdec09.png)

|項目|入力値|
|:-----------------|:-------------|
|プロバイダーのタイプ|OpenID Connect|
|プロバイダーのURL|accounts.google.com|
|対象者|Google Developer Console上に表示されたWeb client用のID「xxxxxxxxxxx.apps.googleusercontent.com」|


上記を入力して、IDプロバイダー(accounts.google.com)を作成します。

# Cognito IDプールの作成(AWS)

OpenIDのIDプロバイダを追加すると、CognitoのIDプール作成画面のOpenIDのタブに、今追加した「accounts.google.com」が選択肢として表示されますので、チェックを入れます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/382663/939b616b-2d32-3bcd-8df3-a0bdf4f32c19.png)

今回は認証されていないIDのアクセスは拒否しますので、そのままIDプールを作成します。

# AWS認証情報の権限設定

CognitoでIDプールを作成すると、２つのIAMロールが生成されます。

+ Cognito_xxxxxxAuth_Role (認証済みユーザ用のIAMロール）
+ Cognito_xxxxxxUnauth_Role（認証されていないユーザ用のIAMロール）

(xxxxxxは作成したIDプールの名称です)

先ほどのIDプール作成で、「認証されていないIDに対してのアクセスを有効にする」にチェックを入れると、認証していないユーザにもAWSサービスへのアクセスを許可することができます。

ただ、今回は使用しませんので、認証済みユーザ向けの「Cognito_xxxxxxxAuth_Role」の方に、必要なアクセス権を設定します。

# AndroidアプリをOAuth Clientとして追加

ここまでの設定で、Googleアカウント認証とAWSの連携はできました。
ただ、OAuthクライアントとして登録しているのはWeb clientだけです。

今回は、Androidアプリのサインイン処理で使用しますので、AndroidアプリをOAuthクライアントとして追加する必要があります。

### 1. フィンガープリントの取得

まず、利用するAndroidアプリのフィンガープリント(SHA-1)を取得します。
Debug版の場合は、ビルドしたマシン毎にローカルにデバッグ用のKeyStoreが作成されてアプリに署名されますので、そのKeyStoreを指定してKeyToolでフィンガープリントを表示させます。

（Release版は後程説明します）


#### Debug版KeyStoreの場所
|OS|場所|
|:----|:-----|
|Windows|C:\Users\<user>\.android\debug.keystore|
|MacやLinux|~/.android/debug.keystore|


#### フィンガープリントの取得
上記のKeyStoreを指定して、KeyToolを実行します。

```
keytool -v -list -keystore [keystore名]
```

表示されたSHA-1のフィンガープリントをコピーしておきます。

### 2. FireBaseプロジェクトにアプリを登録

[FireBase Console][1]を開き、「プロジェクトの設定」を選択します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/382663/d1756563-1faf-76df-02ad-0df546288ceb.png)


アプリをつ一つも追加していない場合は、下記のようにアプリ選択の画面が表示されます。


![image.png](https://qiita-image-store.s3.amazonaws.com/0/382663/7cd8f871-ec94-8108-ad4a-9ea9af8d549c.png)

今回はAndroidアプリを追加しますので、アンドロイドアイコンをクリックして表示されるアプリ登録画面上で、パッケージ名と、取得したフィンガープリント(SHA-1)を入力し、アプリを登録します。


アプリ登録後、[Google Developer Console][2]にアクセスし、「認証情報」ページを開きます。

そうすると、OAuth2.0クライアントIDの一覧に、Web client用のIDだけでなく、Android用のアプリクライアントIDが自動生成されているはずです。（「タイプ」がAndroidのキー）

これをコピーしておきます。


### 3. IDプロバイダーの対象者にAndroid用クライアントIDを追加（AWS)

AWSのIAMページで、先ほど追加した、accounts.google.comを選択し、対象者にAndroid用のクライアントIDを追加します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/382663/183de2d1-89ef-2ed1-1cae-f75c672bf034.png)

これで、AndroidアプリでGoogleアカウント認証を行い、AWSサービスにアクセスする準備ができました。


# 実装

Googleアカウントへのサインイン処理の実装方法については色々と情報がありますので、そちらを参照してください。
私は下記の記事を参考にさせていただきました。

[Google Sign-In][3]

Googleサインインが成功すると、GoogleSignInAccountが取得できます。
このアカウントオブジェクトのgetIdToken()メソッドで、OpenID Connectで使用するJWTトークンが取得できますので、それを使用して、AWSサービスにアクセスする際のCredentialオブジェクトを生成します。

```java
        CognitoCachingCredentialsProvider credentialsProvider 
                = new CognitoCachingCredentialsProvider(
                                mContext,            // context
                                COGNITO_IDP_ID,      // Cognito IDプールのID
                                Regions.AP_NORTHEAST_1);

        // mAccountは、サインイン成功で返却されたGoogleSignInAccountオブジェクト
        Map<String, String> logins = new HashMap();
        logins.put("accounts.google.com", mAccount.getIdToken());

        credentialsProvider.setLogins(logins);

```

このCredentialをAWS SDKの各サービス用クライアント生成時に使用することで、AWSサービスに設定された権限でアクセスが可能になります。

#注意事項

## 認証の有効期限

取得した認証トークンには有効期限があります。
Cognitoユーザプール経由で認証を行うと、トークンのリフレッシュはSDK内部で自動的に行ってくれるようなのですが、今回の方法では自分で実装する必要があります。

有効期限が切れると、AWS SDKから、下記のExceptionが発生します。

```java
com.amazonaws.services.cognitoidentity.model.NotAuthorizedException
```

これをCatchしたら、再ログイン処理を行ってから再試行する、という実装が必要です。


## Release版のフィンガープリント

Androidアプリ用のOAuthクライアントIDは、フィンガープリントが異なるRelease版は別途登録する必要があります。

登録方法はDebug版と同じなのですが、PlayStoreに「GooglePlayアプリ署名」を利用してアップロードしたアプリの場合、フィンガープリントはPlayStore側で付け替えられてしまいますので、Google Play Consoleからフィンガープリントを取得する必要があります。

[GooglePlayアプリ署名とは][4]

その場合、[Google Play Console][5]を開き、アプリの署名ページで表示されるSHA-1フィンガープリントを登録します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/382663/7950e144-1f77-1b21-d7e3-be8626bf5058.png)



#参考

以下のページを参考にさせていただきました。
[Google Sign-In][3]
[GooglePlayアプリ署名とは][4]




[1]:https://console.firebase.google.com/?hl=ja
[2]:https://console.developers.google.com/?hl=ja
[3]:https://qiita.com/Yuki_312/items/437f41aa7a37a9009c25
[4]:https://support.google.com/googleplay/android-developer/answer/7384423
[5]:https://developer.android.com/distribute/console