【aws-sdk-ios】cognitoユーザープールへのFacebookログインを実装する


# 何がやりたいか

AWSの提供する認証基盤の**Cognito ユーザープール**へのソーシャルログインを実現します。

# どんな人向けか
- Cognitoユーザープールを利用していてFacebookログインなどのソーシャルログインを実装しようとしている人
- AWS Cognitoを用いたソーシャルログイン実装を検討している人

# 前提

以下を前提条件とします。

- AWSアカウントやCognitoの設定などAWS側の環境構築が完了していること
- AmplifySDKをクライアントにインストール済みであること
- アプリからUserPoolにID/Passwordでのログインができる状態になっていること
- CognitoではIDプールではなくユーザープールを使用していること
- AmplifyやCognitoの詳細な説明は省略します

# 大事なこと
通常ソーシャルログインを実装しようとしたら、
例えばFacebookであればFacebookのSDKのメソッドを利用し、
認証後のコールバックなどをゴニョゴニョして、、、
みたいな形になるかと思いますが、Cognitoを使えばそのへんもうまいことやってくれます。

しかし、Cognitoで**ユーザープール**を使っており、
そこにamplifySDKの提供する機能でFacebookなどのソーシャルログインを利用したい場合、
選択肢は**HostedUI一択となります。**

HostedUIとはなにかというと、Amplifyの提供しているサインイン・サインアップの機能で、
画像のようなUIも含めてAWS側がまるっと提供してくれています。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287671/2baedc79-1274-c89c-6ead-9f14220b6252.jpeg" width="30%">

Webで提供しているので全くネイティブっぽくない感じのUIになっており、
様々なissueでネイティブの機能でユーザープールにサインインできるような対応を求める声が挙がっていますが、
現状これ以外の選択肢はなさそうです。（コンソール上から表示するUIのカスタムはできるようですが）
色々調べてみると`federatedSignIn`などHostedUI以外の選択肢がありそうに見えてきますが、錯覚です。
記事を書いている2021/4月時点ではHostedUI以外の選択肢はないといくつものissueで回答されています。

・[参照したissueの一例](https://github.com/aws-amplify/aws-sdk-ios/issues/993)

# コンソールでの設定

## Facebook側の設定

### Facebook for Developerへの登録

Facebook for Developerにアプリを登録し、Facebookログインを追加します。

### 設定 > ベーシック

記載されているアプリID,アプリシークレットを控えておきます。
また、以下の手順でFacebookログインを使用するプラットフォームを選択します。
通常FacebookSDKの機能でログインする場合は「iOS」を選択しますが、
HostedUIの場合はプラットフォームとしてウェブサイトを選択する必要がある模様です。

1. 画面下部のボタンからプラットフォームを追加
1. プラットフォームを選択 から ウェブサイトを選択
1. サイトURLに`https://<your-user-pool-domain>/oauth2/idpresponse`を記載（後述）

![facebook_add_platform.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287671/8dfa46d6-b024-171b-53d6-81eebd2379b4.jpeg)

### Facebookログイン > 設定

こちらの「有効なOAuthリダイレクトURI」にもプラットフォーム欄のサイトURLに記載した
`https://<your-user-pool-domain>/oauth2/idpresponse`を記載します（後述）

## Cognito側の設定

### 全般設定 > アプリクライアント

モバイルアプリから使用する場合は`アプリクライアントID`と`アプリクライアントのシークレット`の設定が必要です。
なければ「別のアプリクライアントから追加」で追加します。
こちらのIDとシークレットは控えておき後で使用します。

![Untitled1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287671/090024b6-7877-4cfe-f50b-c59d2da1c445.jpeg)

### フェデレーション > IDプロバイダー

1. Facebookを選択
1. 控えておいたアプリID，アプリシークレットを入力
1. 承認スコープにpublic_profile, emailを記載
1. Facebookの更新を押下

### アプリの統合 > アプリクライアントの設定

アプリシークレットの設定してあるクライアントに対して下記の設定を実施します。

1. 有効なIDプロバイダにFacebookが追加されているはずなのでチェックを入れる
1. サインインとサインアウトのURLにアプリのスキーム（`myapp://`）を記載する
1. 許可されているOAuthフローに「Authorization code grant」をチェック
1. 許可されているOAuthスコープに「phone」「email」「openid」「aws.cognito.signin.user.admin」「profile」をチェック

### アプリの統合 > ドメイン名

Amazon Cognito ドメイン項目に新たなドメインを追加する。
この`hoge.auth.region-infomation.amazoncognito.com`が
上記の`<your-user-pool-domain>`にあたる文字列となる。

# アプリ実装

## awsconfigration.json（構成ファイル）

awsconfigration.jsonに以下の記述を追加する。

```awsconfigration.json
    "Auth": {
        "Default": {
            "OAuth": {
                "WebDomain": "<your-user-pool-domain>",
                "AppClientId": "<cognito-app-client-id>",
                "AppClientSecret": "<cognito-app-secret>",
                "SignInRedirectURI": "myapp://",
                "SignOutRedirectURI": "myapp://",
                "Scopes": ["openid", "email","aws.cognito.signin.user.admin","profile","phone"]
            }
        }
    },...
```

## ViewController

サインインする画面で以下を実装


```Swift:ViewController.swift
let hostedUIOptions = HostedUIOptions(scopes: ["openid", "email", "profile", "aws.cognito.signin.user.admin"])

// Present the Hosted UI sign in.
AWSMobileClient.default().showSignIn(navigationController: self.navigationController!, hostedUIOptions: hostedUIOptions) { (userState, error) in
    if let error = error as? AWSMobileClientError {
        print(error.localizedDescription)
    }
    if let userState = userState {
        // サインインが完了するとサインイン後のステータスが返却される
        print("Status: \(userState.rawValue)")
    }
}
```

ここまで実装して、無事添付画像のようなサインイン画面が出れば一旦完了です。


実際に動かしてみるとHostedUIによって

- サインイン
- サインアップ
- パスワード再発行
- 二段階認証のフロー
- ソーシャルログイン

など認証に関する機能が一通り備わっており、
UI的な自由度がないとはいえこのコード量でこれだけの機能を提供してもらえるのはかなり嬉しいです。

# 終わりに

色々試行錯誤しながら行き着いた現状動いている設定・実装になります。
今回のようなシチュエーションにおける日本語の記事はほとんどないので、どなたかの手助けになれば幸いです。


# 参考資料
- [Amplify Docs - Hosted UI](https://docs.amplify.aws/sdk/auth/hosted-ui/q/platform/ios#automated-setup-with-cli)
- [ユーザープールへのソーシャル ID プロバイダーの追加](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-pools-social-idp.html)