[iOS] Facebookアカウントを使ってCognitoでAWSヘアクセス!!


# この記事で紹介すること

iOSアプリでFacebookアカウントを介してCognitoでAWSへアクセス!!
(AWS素人が調べた内容なので誤りがある可能性があります）

# Facebook App の登録

Facebook認証をiOSアプリから利用するために、そのiOSアプリをFacebook Appとして登録する。

## 手順

1 https://developers.facebook.com/ にアクセス
2 My Apps からAdd a New Appを押して、AppIDを取得する.

![スクリーンショット 2015-05-22 17.56.13.png](https://qiita-image-store.s3.amazonaws.com/2116/37443/ea6cdddd-e96e-4b4e-1ca1-8466e1bb2766.png "スクリーンショット 2015-05-22 17.56.13.png")


3 Getting Startedの内容に従って、iOSアプリからFacebookにログインできるようにする。(手順に従えば、Facebookログインボタンまで簡単に追加できるはず！！）

※　もし手順どおりInfo.plistにURL schemesも登録したのに、それが見つからない！！って怒られた場合は、Info.plistをテキスト形式で表示してみると良いかも。 FacebookAppIDの最初に空白が入ってたりするのに気づけるかもしれない...

![スクリーンショット 2015-05-22 18.53.43.png](https://qiita-image-store.s3.amazonaws.com/2116/37443/f4392e23-f007-4fa4-267e-da4f88a85eca.png "スクリーンショット 2015-05-22 18.53.43.png")


## FacebookApp用のテストユーザーについて
FacebookAppのテスト用ユーザーを作成する場合は、My AppsページのRolesからTestUsersを作成できる。

![スクリーンショット 2015-05-22 17.57.49.png](https://qiita-image-store.s3.amazonaws.com/2116/37443/07b1b0f9-9579-37a8-8f75-31af46eae402.png "スクリーンショット 2015-05-22 17.57.49.png")




## 他のFacebookアカウントで認証するには

一度Facebookへ認証した後ログアウトするだけでは、他のユーザーで
認証できない（認証済みとして、勝手にログインしてしまう）。その場合はFacebookアプリの方で一度ログアウトすると、他のユーザーでログインできるようになる。

# AWS Cognito

## Identity pool の作成

1  AWS Cognitoのページにアクセス.(東京リージョンはまだない）
2  Create new Identity poolを選ぶ
3  Identity pool nameを入力する。(アプリ名など）
4  Unauthenticated identitiesを有効にするか選ぶ。（アプリが未認証のゲストユーザーを利用する場合は有効にする。今回は無効。）
5  Authentication providersを選択する。今回はFacebookを選び、FacebookAppのApp IDを入力する。

![スクリーンショット 2015-05-22 18.00.02.png](https://qiita-image-store.s3.amazonaws.com/2116/37443/73c8e104-2f02-0dc8-d98f-bb5b767699c3.png "スクリーンショット 2015-05-22 18.00.02.png")

6 認証用のIAMロールと未認証用のIAMロールの作成を許可する。

![スクリーンショット 2015-05-22 18.04.10.png](https://qiita-image-store.s3.amazonaws.com/2116/37443/377b8029-3e3d-1a29-d46f-241f3273b1cd.png "スクリーンショット 2015-05-22 18.04.10.png")


## IAM ロール ポリシーの編集.(必要であれば）

1 IAMページにアクセス.
2 ロールを選択.
![スクリーンショット 2015-05-22 18.06.19.png](https://qiita-image-store.s3.amazonaws.com/2116/37443/daadab0f-6232-523f-4f2f-35c748f19a59.png "スクリーンショット 2015-05-22 18.06.19.png")

3 AWS Cognito Identity poolで作成した, __XXX__Auth_Roleを編集する。

4 例えばS3へユーザー毎にファイル追加と取得のアクセス許可を出す例.

> 前提
> S3へ保存するファイルパスは、以下の形式.
> mybucket/cognito-identity/ファイル名
> ※ cognito-identityがユーザーのID.

```json

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": [
                "arn:aws:s3:::mybucket/${cognito-identity.amazonaws.com:sub}",
                "arn:aws:s3:::mybucket/${cognito-identity.amazonaws.com:sub}/*"
            ]
        }
    ]
}
```

- ポリシー変数を利用するために、"Version": "2012-10-17"は必須。
- ${cognito-identity.amazonaws.com:sub}は、cognitoで認証したユーザーのIDを表すポリシー変数。

参考

AWS STS ウェブ ID フェデレーションについて
http://docs.aws.amazon.com/ja_jp/STS/latest/UsingSTS/web-identity-federation.html

## CognitoとFacebookログインの連携例

手順: Facebook認証できたら、CognitoへFacebookのtokenを設定して、AWSへ認証する.

### AppDelegate

```Objc

@implementation AppDelegate

// Cognitoのサンプルコードでは、Cognitoの認証をおこなっているけど、
// FacebookSDKの処理のみにする。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    return [[FBSDKApplicationDelegate sharedInstance] application:application
                                    didFinishLaunchingWithOptions:launchOptions];
    
}

- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication
         annotation:(id)annotation {
    return [[FBSDKApplicationDelegate sharedInstance] application:application
                                                          openURL:url
                                                sourceApplication:sourceApplication
                                                       annotation:annotation];
}

```

### ViewController(Facebookのログインボタンを表示しているController）

便宜上ViewControllerでFacebookボタンを表示しているとする。

```Objc

// ログイン/ログアウト結果を取得できるようにFBSDKLoginButtonDelegateを追加.
@interface ViewController : <FBSDKLoginButtonDelegate>
```

### ログインボタン

参考
https://developers.facebook.com/docs/facebook-login/ios/v2.3
https://developers.facebook.com/docs/facebook-login/permissions

```objc
    FBSDKLoginButton *loginButton = [[FBSDKLoginButton alloc] init];
    loginButton.center = self.view.center;
    loginButton.readPermissions = @[@"public_profile", @"email"];
    // delete追加
    loginButton.delegate = self;
    [self.view addSubview:loginButton];
```

### FBSDKLoginButtonDelegate実装

```Objc
-(void)loginButton:(FBSDKLoginButton *)loginButton didCompleteWithResult:(FBSDKLoginManagerLoginResult *)result error:(NSError *)error {
  if( !error ) {
    if( !result.isCancelled ) {
        [self authorizeAWS];
    }
  }
}
```

```Objc
-(void)authorizeAWS {
     NSDictionary* logins = @{@"graph.facebook.com": [FBSDKAccessToken currentAccessToken].tokenString};

    // AWS Cognitoのサンプルコード.
   
    AWSCognitoCredentialsProvider* credentialProvider =
        [[AWSCognitoCredentialsProvider alloc] initWithRegionType:CognitoRegionType /* Cognitoのリージョン */
    identityPoolId:CognitoIdentityPoolId /* CognitoのIdentityPoolId */];
    
    // ここでFacebookのログイン情報を設定.
    credentialProvider.logins = logins;
    
    AWSServiceConfiguration* configuration =
    [[AWSServiceConfiguration alloc] initWithRegion:DefaultServiceRegionType /* S3などサービスのあるリージョン */ credentialsProvider:credentialProvider];
    [AWSServiceManager defaultServiceManager].defaultServiceConfiguration = configuration;
    
    self.credentialProvider = credentialProvider;


```

### Cognitoで認証したユーザーIDの取得.

AWSCognitoCredentialsProvider::credentialProvider.getIdentityId で取得できるが、 以下のコードを実行しないとnilが返ってくる。

※　適切な挿入箇所は未調査なので、とりあえず、上のauthorizedAWSメソッドの直後に実行してます。

stackoverflow
http://stackoverflow.com/questions/25245236/aws-cognito-identity-null


```Objc
    // Cognitoのユーザー毎のIDを    
    [[credentialProvider getIdentityId] continueWithBlock:^id(BFTask *task){
        
        if (task.error == nil)
        {
            NSString* cognitoId = credentialProvider.identityId;
            NSLog(@"cognitoId: %@", cognitoId);
        }
        else
        {
            NSLog(@"Error : %@", task.error);
        }
        
        return nil;
    }];
}

```


### ViewController初期化時

```Objc

- (void)viewDidLoad {
    [super viewDidLoad];

	// 表示時にすでに認証済みなら、自動的にAWSヘログイン
    if( [FBSDKAccessToken currentAccessToken] ) {
        [self authorizeAWS];
    }
}
```

----

以上で、Facebookアカウントを介してAWSへアクセスができる。(間違いがある可能性は十分あります。気になる点があれば指摘ください.）

### おまけ Facebookからログアウト

 Facebookログインと同じ処理で、ログアウトボタンを表示できる。

```Objc
    FBSDKLoginButton *loginButton = [[FBSDKLoginButton alloc] init];
    loginButton.center = self.view.center;
    loginButton.readPermissions = @[@"public_profile", @"email"];
    loginButton.delegate = self;
    [self.view addSubview:loginButton];

```

```Objc
-(void)loginButtonDidLogOut:(FBSDKLoginButton *)loginButton {
    NSLog(@"logout facebook result.");
    [self.tableView reloadData];
    [loginButton removeFromSuperview];
    
    [self logoutAws];
}

-(void)logout {
    [self.credentialProvider clearCredentials];
    self.credentialProvider = nil;
}

```


### おまけ Facebook profile（ユーザー名など）の取得.

Facebook認証に成功した後、遅れてprofileが取得できるようなので、NSNotificationCenterでProfile変化を受信できるようにする。

```Objc


- (void)viewDidLoad {
    [super viewDidLoad];
    
    [FBSDKProfile enableUpdatesOnAccessTokenChange:YES];
    
    if(  [FBSDKAccessToken currentAccessToken] ) {
        [self authorizeAWS];
    }
        
    NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
    [center addObserver:self selector:@selector(fbSDKProfileDidChangeNotification:) name:FBSDKProfileDidChangeNotification object:nil];
}


-(void)fbSDKProfileDidChangeNotification:(NSNotificationCenter*)center {
    NSLog(@"something change...%@", [FBSDKProfile currentProfile]);
}
```

# 参考

Cognitoでユーザー認証:http://docs.aws.amazon.com/cognito/devguide/identity/?platform=ios

AWS STS ウェブ ID フェデレーションについて:
http://docs.aws.amazon.com/ja_jp/STS/latest/UsingSTS/web-identity-federation.html