CognitoをSwiftで使ってFacebookログイン

Amazon Cognitoは、モバイルアプリにおけるユーザ認証の仕組みを提供してくれるサービスです。
最近は家でSwiftを書いていて、認証機能を作ろうと思っていたところ、クラスメソッドさんのブログを発見しました。
[Amazon Cognito でモバイルユーザー認証！](http://dev.classmethod.jp/smartphone/amazon-cognito-ios-auth/)

この記事はObjective-Cの例が取り上げられていたので、それをSwiftで書きなおしたという無いようです。

# Amazon Cognitoとは
クラスメソッドさんのブログをご覧ください。

# Facebookアプリ、Amazon Cognitoアイデンティティプールの設定
クラスメソッドさんのブログをご覧ください。

# アプリの実装
podfileは本家と同様です。

まずはAppDelegate.swift。
ボタンをクリックしたらFacebook認証とCognitoの動作が始まります。

```swift
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
        let rootViewController = RootViewController()
        self.window!.rootViewController = rootViewController
        self.window!.backgroundColor = UIColor.whiteColor()
        self.window!.makeKeyAndVisible()
        return true
    }
    
    func application(application: UIApplication, openURL url: NSURL, sourceApplication: NSString?, annotation: AnyObject) -> Bool {
        FBSession.activeSession().handleOpenURL(url)
        var wasHandled:Bool = FBAppCall.handleOpenURL(url, sourceApplication: sourceApplication)
        return wasHandled
    }

    (中略)

    func applicationWillTerminate(application: UIApplication) {
        self.saveContext()
        FBSession.activeSession().close()
    }
```

次に本体の実装ですが、ViewとViewControllerで分けているので、本家とは別の構成になっていますが、ViewControllerには同様のものが書いてあります。

まずはViewから。

```swift
import UIKit

@objc protocol RootViewDelegae {
    func showTappedMessage() -> ()
}

class RootView : UIView {

    var delegate : RootViewDelegae!
    var loginButton : UIButton!

    func setTouchEvent() {
        self.loginButton!.addTarget(delegate, action: "showTappedMessage", forControlEvents: UIControlEvents.TouchUpInside)
    }

    func setInitialParams(view : UIView!) {
        self.loginButton = UIButton()
        self.loginButton?.setTitle("Login", forState: UIControlState.Normal)
        self.loginButton?.setTitleColor(UIColor.blueColor(), forState: UIControlState.Normal)
        self.loginButton?.setTitle("Tapped", forState: UIControlState.Highlighted)
        self.loginButton?.frame = CGRectMake(0, 0, 300, 50)
        self.loginButton?.tag = 1
        self.loginButton?.layer.position = CGPoint(x: 150, y: 500)
        self.loginButton?.backgroundColor = UIColor(red: 0.7, green: 0.2, blue: 0.2, alpha: 0.2)
        self.loginButton?.layer.cornerRadius = 10
        self.loginButton?.layer.borderWidth = 1
        view.addSubview(self.loginButton)
    }
}
```

そしてViewController

```swift

import UIKit

class RootViewController : UIViewController, RootViewDelegae {

    var rootView : RootView?

    override func viewDidLoad() {
        super.viewDidLoad()

        self.rootView = RootView()
        self.view = self.rootView
        self.rootView!.delegate = self
        self.rootView!.setInitialParams(self.view)
        self.rootView!.setTouchEvent()
    }

    func showTappedMessage() {
        NSLog("hoge")
        var completionHandler : FBSessionStateHandler = {
            session, status, error in
            
            let cognitoAccountId = "0123456789"
            let cognitoIdentityPoolId = "us-east-1:123456-aaaa-bbbb-cccc-ddddeeeeffff"
            let cognitoUnauthRoleArn = "arn:aws:iam::0123456789:role/Cognito_sampleAuth_DefaultRole"
            let cognitoAuthRoleArn = "arn:aws:iam::0123456789:role/Cognito_sampleAuth_DefaultRole"
            
            if(status == FBSessionState.ClosedLoginFailed || status == FBSessionState.CreatedOpening) {
                NSLog("Login Failed")
                FBSession.activeSession().closeAndClearTokenInformation()
                FBSession.setActiveSession(nil)
            } else {
                NSLog("Login Succeed")
                FBSession.setActiveSession(session)

                let credentialsProvider : AWSCognitoCredentialsProvider = AWSCognitoCredentialsProvider.credentialsWithRegionType(
                    AWSRegionType.USEast1,
                    accountId: cognitoAccountId,
                    identityPoolId: cognitoIdentityPoolId,
                    unauthRoleArn: cognitoUnauthRoleArn,
                    authRoleArn: cognitoAuthRoleArn)
                
                var token : NSString = FBSession.activeSession().accessTokenData.accessToken
                
                var logins = NSDictionary(dictionary: ["graph.facebook.com" : token])
                credentialsProvider.logins = logins
                
                let defaultServiceConfiguration = AWSServiceConfiguration(
                    region: AWSRegionType.USEast1,
                    credentialsProvider: credentialsProvider)

                AWSServiceManager.defaultServiceManager().setDefaultServiceConfiguration(defaultServiceConfiguration)
                
                credentialsProvider.getIdentityId().continueWithSuccessBlock {
                    (task: BFTask!) -> AnyObject! in
                    let cognitoId : NSString = credentialsProvider.identityId
                    self.launchCount()
                    return nil
                }
            }
        }
        
        FBSession.openActiveSessionWithReadPermissions(["public_profile"], allowLoginUI: true, completionHandler: completionHandler)
    }
    
    func launchCount() {
        var syncClient : AWSCognito = AWSCognito.defaultCognito()
        var dataset : AWSCognitoDataset = syncClient.openOrCreateDataset("myDataset")

        dataset.synchronize()
        NSLog("Complete")
    }
}
```

アプリを起動しボタンをクリックした後にCognitoの管理画面に行くと、認証されていることがわかります。

※ スクリーンショット撮影前に2回認証をしたので、回数が1ではなくなっています。
![スクリーンショット 2014-12-20 23.38.26.png](https://qiita-image-store.s3.amazonaws.com/0/11737/57ed2abd-fbce-f176-86c0-26cef385d353.png)

また、コンソールは以下の様なデバッグメッセージが出力されています。
![スクリーンショット 2014-12-20 23.36.08.png](https://qiita-image-store.s3.amazonaws.com/0/11737/261d11ac-a456-3474-1018-520567613ce3.png)

# まとめ
ユーザ認証機能は、

* アプリの提供価値そのものではない
* しかしほぼ必須の機能である
* そのくせ不具合が出やすく致命的

という厄介者です。

モバイルアプリ開発でもAmazonを使って、アプリケーションの本質に集中したいものです。

# 参考URL

* http://dev.classmethod.jp/smartphone/amazon-cognito-ios-auth/
* http://www.shrikar.com/blog/2014/11/06/ios-app-development-upload-images-to-aws-s3/
* https://forums.aws.amazon.com/thread.jspa?messageID=589101
* http://qiita.com/yuch_i/items/081a05e2a27550047897