Cognito User Poolsのサインアップ・サインイン画面をiOSで試してみた

Coginit User Poolsにフェデレーション等の新機能が追加されましたが
新機能のサインアップ・サインイン画面をiOSから試してみました。
https://aws.amazon.com/jp/about-aws/whats-new/2017/08/amazon-cognito-launches-general-availability-of-a-built-in-customizable-user-experience-for-sign-in-oauth-2-0-support-and-federation-with-facebook-login-with-amazon-google-and-saml-providers-for-user-pools/

# 環境
* xcode
* aws sdk

# 作業概要
iOSからCognitoでサインアップ（サインイン）後、払い出されたCredentialの確認のためPollyに喋らせてみたいと思います。

1. UserPoolを作成する
2. Identity Poolを作成する
3. xcodeでコードを書く
4. Polly実行用にIAM Roleを設定する
5. Simulatorで実行する

# 1. UserPoolを作成する
1. AWSマネジメントコンソールにログインして、Cognito User Poolsを選択する
2. Pool名(MyPool)を指定して、User Poolを作成する(全てデフォルト設定)
3. App Clientを追加する
    1. [General settings]-[App Clients]を選択する
    2. Client名(MyApp)を指定して、Clientを追加する(全てデフォルト設定)
4. App Integrationを設定する
    1. App client settingsを以下のように指定
    <img width="1192" alt="AppClientSettings.png" src="https://qiita-image-store.s3.amazonaws.com/0/192584/050f4e9d-f4cf-abb3-d724-3ca39d096a57.png">
    2. Domain Nameを以下のように指定
    <img width="1213" alt="DomainName.png" src="https://qiita-image-store.s3.amazonaws.com/0/192584/c93fc755-69a2-de5c-a6b3-262a818b9a1f.png">

<img width="989" alt="attention.png" src="https://qiita-image-store.s3.amazonaws.com/0/192584/1a83fe2a-4200-39f7-1b92-bfc4a9c52b70.png">

# 2. Identity Poolを作成する
1. Federated Identitiesを選択し、User Pools画面から切替
2. Identity Pool name(MyAppIdentity)とUser Pool IDとApp client idを指定してIdentity Poolを作成
   <img width="1383" alt="IdentityPool.png" src="https://qiita-image-store.s3.amazonaws.com/0/192584/0a9ee588-8613-5b18-55a1-8fb1398929fb.png">

# 3. xcodeでコードを書く
参考URL
https://github.com/awslabs/aws-sdk-ios-samples/tree/master/CognitoAuth-Sample
http://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-integrating-user-pools-with-identity-pools.html

```Info.plist(抜粋)
<key>NSAppTransportSecurity</key>
	<dict>
		<key>NSExceptionDomains</key>
		<dict>
			<key>amazonaws.com</key>
			<dict>
				<key>NSThirdPartyExceptionMinimumTLSVersion</key>
				<string>TLSv1.0</string>
				<key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
				<false/>
				<key>NSIncludesSubdomains</key>
				<true/>
			</dict>
			<key>amazonaws.com.cn</key>
			<dict>
				<key>NSThirdPartyExceptionMinimumTLSVersion</key>
				<string>TLSv1.0</string>
				<key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
				<false/>
				<key>NSIncludesSubdomains</key>
				<true/>
			</dict>
		</dict>
	</dict>
    <key>AWS</key>
    <dict>
        <key>CognitoUserPool</key>
        <dict>
            <key>Default</key>
            <dict>
                <key>CognitoUserPoolAppClientId</key>
                <string>****************************</string>
                <key>CognitoUserPoolAppClientSecret</key>
                <string>****************************</string>
                <key>CognitoAuthWebDomain</key>
                <string>https://myapp.auth.us-east-1.amazoncognito.com</string>
                <key>CognitoAuthSignInRedirectUri</key>
                <string>myapp://top</string>
                <key>CognitoAuthSignOutRedirectUri</key>
                <string>myapp://top</string>
                <key>CognitoAuthScopes</key>
                <array>
                    <string>openid</string>
                </array>
            </dict>
        </dict>
    </dict>
	<key>CFBundleURLTypes</key>
	<array>
		<dict>
			<key>CFBundleURLSchemes</key>
			<array>
				<string>myapp</string>
			</array>
			<key>CFBundleURLName</key>
			<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
		</dict>
	</array>
```

```AppDelegate.swift(抜粋)
    func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
        return AWSCognitoAuth.default().application(app, open: url, options: options)
    }

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        let clientId = "App Client IDを指定"
        let clientSecret = "App Client　Secretを指定"
        let poolId = "User Pool IDを指定"
        let identityPoolId = "Identity Pool IDを指定"
        
        let serviceConfiguration = AWSServiceConfiguration(
            region: .USEast1, // リージョンを指定(ここではバージニア)
            credentialsProvider: nil)
        
        let userPoolConfiguration = AWSCognitoIdentityUserPoolConfiguration(
            clientId: clientId,
            clientSecret: clientSecret,
            poolId: poolId)
        
        AWSCognitoIdentityUserPool.register(
            with: serviceConfiguration,
            userPoolConfiguration: userPoolConfiguration,
            forKey: "UserPool")
        
        let pool = AWSCognitoIdentityUserPool(forKey: "UserPool")
        
        let credentialsProvider = AWSCognitoCredentialsProvider(
            regionType: .USEast1, // リージョンを指定(ここではバージニア)
            identityPoolId: identityPoolId,
            identityProviderManager:pool)
        
        let configuration = AWSServiceConfiguration(
            region: .USEast1,  // リージョンを指定(ここではバージニア)
            credentialsProvider: credentialsProvider
        )
        AWSServiceManager.default().defaultServiceConfiguration = configuration
        
        return true
    }
```

ログインボタンとログアウトボタンを配置する

```ViewController.swift
import UIKit
import AWSCognitoAuth
import AWSCognitoIdentityProvider
import AVFoundation
import AWSPolly

class ViewController: UIViewController, AWSCognitoAuthDelegate {
    var audioPlayer = AVPlayer()
    let cognitoAuth = AWSCognitoAuth.default()

    override func viewDidLoad() {
        super.viewDidLoad()
        cognitoAuth.delegate = self as AWSCognitoAuthDelegate
    }

    func getViewController() -> UIViewController {
        return self
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    @IBAction func LoginButtonAction(_ sender: Any) {
        cognitoAuth.getSession(self)  { (session:AWSCognitoAuthUserSession?, error:Error?) in
            if(error != nil) {
                print((error! as NSError).userInfo["error"] as? String)
            }else {
                let message = "<speak>Hello World!</speak>"
                self.talkmessage(message: message)
                print("login")
            }
        }
    }
    
    @IBAction func LogoutButtonAction(_ sender: Any) {
        cognitoAuth.signOut { (error:Error?) in
            if(error != nil) {
                print((error! as NSError).userInfo["error"] as? String)
            }else {
                print("logout")
            }
        }
    }
    
    func talkmessage(message:String) {
        let input = AWSPollySynthesizeSpeechURLBuilderRequest()
        
        input.textType = AWSPollyTextType.ssml
        input.text = message
        input.outputFormat = AWSPollyOutputFormat.mp3
        input.voiceId = AWSPollyVoiceId.mizuki
        
        let builder = AWSPollySynthesizeSpeechURLBuilder.default().getPreSignedURL(input)
        
        builder.continueOnSuccessWith(block: { (awsTask: AWSTask<NSURL>) -> Any? in
            let url = awsTask.result!
            self.audioPlayer.replaceCurrentItem(with: AVPlayerItem(url: url as URL))
            self.audioPlayer.play()
            
            return nil
        })
    }
}
```
# 4. Polly実行用にIAM Roleを設定する
1. AWSマネジメントコンソールにログインして、IAM - Rolesを選択する
2. 認証されたユーザー用ロール(ここではCognito_MyAppIdentityAuth_Role)に、AmazonPollyFullAccessポリシーをアタッチする

# 5. Simulatorで実行する
Loginボタンをクリックする
<img width="373" alt="top.png" src="https://qiita-image-store.s3.amazonaws.com/0/192584/42101d96-a23d-11de-68b0-6524331f9695.png">

# サインアップ画面とサインイン画面
1. サインアップ画面
<img width="374" alt="signup.png" src="https://qiita-image-store.s3.amazonaws.com/0/192584/07cd30e6-ff15-9532-0f99-f301329a2b42.png">

2. パスワード入力中の画面
<img width="374" alt="signup_pass.png" src="https://qiita-image-store.s3.amazonaws.com/0/192584/3f586466-0d52-b5d8-30f8-3af48cd3f191.png">

3. Verify画面
<img width="373" alt="verify.png" src="https://qiita-image-store.s3.amazonaws.com/0/192584/d237447a-672e-3ad3-1eb2-d4bcfa4fb93f.png">

4. サインイン画面
<img width="374" alt="signin.png" src="https://qiita-image-store.s3.amazonaws.com/0/192584/e1fd19e2-89dc-e888-eb83-be04be37db1b.png">

5. サインイン画面(背景色変更・ロゴ表示)
<img width="376" alt="modify.png" src="https://qiita-image-store.s3.amazonaws.com/0/192584/d21aafd1-4c6c-c630-cd1a-7e7c8b150d09.png">

# 雑感
今回の機能拡張で、Cognitoは一段と使いやすくなったと思います。