Safariを使った端末側でのTwitter OAuth認証方法

[Twitter-OAuth-iPhone](https://github.com/bengottlieb/Twitter-OAuth-iPhone)を参考に、セキュリティコードのスクレイピングをしないものを作る。

[OAuthConsumer](https://github.com/jdg/oauthconsumer) を使う。
ARCへはXcodeで変換するか、上手くいかなければ手動で。
(※いちおう私がforkして変換したレポジトリは[ここ](https://github.com/skonb/oauthconsumer?source=c)にある)

- まず [Twitterのデベロッパページ](https://dev.twitter.com//)でアプリを登録する。

以下のステップで見るようにコールバックURLとしてはiOSのカスタムURLスキームに割り当てるから本来設定は不要だが、Twitterのページにはhttp(s)しか指定できず、指定しないとなぜか以下のOAuth認証ステップが失敗するため、何でもよいのでhttp(s)の正常なURLを指定しておく。

- iOSにてリクエストトークンを取得する。

```objc

-(void)requestRequestToken{
    self.consumer =[[OAConsumer alloc] initWithKey:@"consumer_key" secret:@"consumer_secret"];
    OAMutableURLRequest				*request = [[OAMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"https://api.twitter.com/oauth/request_token"]  consumer: self.consumer token:nil realm:nil signatureProvider: nil];
    if (!request) return;
    
    [request setHTTPMethod: @"POST"];
    
    /*
     Added oauth_callback param to the request, which is required for OOB (out-of-band) authentication, without which
     twitter does not return a pin but redirects to the callback url set in the app on twitter.com, hence failing the authentication.
     https://dev.twitter.com/docs/auth/pin-based-authorization
     */
    NSMutableArray *params =  [[NSMutableArray alloc] initWithArray:request.parameters];
    [params addObject:[[OARequestParameter alloc] initWithName:@"oauth_callback" value:@"sample-app://" ]];
    request.parameters = params;
    
    self.fetcher = [[OADataFetcher alloc] init];
    [self.fetcher fetchDataWithRequest: request delegate: self didFinishSelector:@selector(setRequestToken:withData:)  didFailSelector: @selector(outhTicketFailed:data:)];

}
```
`sample-app://`はこのアプリで使うURL-Scheme
取得後には以下を呼ぶようにした。


```objc

- (void) setRequestToken: (OAServiceTicket *) ticket withData: (NSData *) data {
	if (!ticket.didSucceed || !data){
    }else{
        NSString *dataString = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
        if (!dataString) {

        }else{
            self.requestToken = [[OAToken alloc] initWithHTTPResponseBody:dataString];
            self.fetcher = nil;
            NSString *urlString = [NSString stringWithFormat:@"%@?oauth_token=%@",@"https://api.twitter.com/oauth/authorize", self.requestToken];
    [[UIApplication sharedApplication]openURL:[NSURL URLWithString:urlString]];
        }
    }
}

```

これでSafariでTwitter認証のページが開かれる。
認証が終わると、`-(BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url`にパラメータ付きで返ってくる。

```objc

-(BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url{
    if ([url.absoluteString hasPrefix:@"sample-app://"]) {
        NSDictionary *params = [NSDictionary parameterDictionaryWithURL:url];
        if (params[@"oauth_verifier"]) {
            [self requestAuthTokenWithOAuthToken:params[@"oauth_token"] verifier:params[@"oauth_verifier"]];
            return YES;
        }else{
            return NO;
        }
    }else{
        return NO;
    }
}


```

上記のように`oauth_verifier`をパーズし、アクセストークンを取得しに行く。

```objc


-(void)requestAuthTokenWithOAuthToken:(NSString *)oauthToken verifier:(NSString *)verifier{
        if (![self.requestToken.key isEqualToString:oauthToken] || ! verifier.length) {

        }else{
            NSString *urlString = [ @"https://api.twitter.com/oauth/access_token" stringByAppendingFormat:@"?oauth_verifier=%@", verifier];
            OAMutableURLRequest		*request = [[OAMutableURLRequest alloc] initWithURL:[NSURL URLWithString:urlString]  consumer: self.consumer token:self.requestToken realm:nil signatureProvider: nil];
            if (!request) return;
            [request setHTTPMethod: @"POST"];
            NSMutableArray *params =  [[NSMutableArray alloc] initWithArray:request.parameters];
            [params addObject:[[OARequestParameter alloc] initWithName:@"oauth_verifier" value:verifier]];
            [params addObject:[[OARequestParameter alloc]initWithName:@"oauth_token" value:oauthToken]];
            self.fetcher = [[OADataFetcher alloc] init];
            [self.fetcher fetchDataWithRequest:request delegate:self didFinishSelector:@selector(setAccessToken:withData:) didFailSelector:@selector(getAccessTokenFailed:data:)];
        }
    }
}

```

結果が返ってくると以下の関数が呼ばれるようにした。

```objc

- (void) setAccessToken: (OAServiceTicket *) ticket withData: (NSData *) data {
	if (!ticket.didSucceed || !data) return;
	
	NSString *dataString = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
	if (!dataString) return;
}
```
`screen_name`, `user_id`, `oauth_token`, `oauth_token_secret` が URLパラメータの形式で返ってくる。
例) `screen_name=hoge&user_id=...`

取得したtoken/token_secretのペアを用いて、この端末に関わらずTwitter APIにアクセスが可能になる。
TwitterのOAuth トークンは今のところ期限が[設けられていない](https://dev.twitter.com/docs/auth/oauth/faq)らしいので、ユーザが自身でリボークするまで使用できる。