OAuth2をObjective-Cで実装してみる（ライブラリ不使用）

ずっとOAuth2の認証の際にはライブラリを使っていたのだけど、どうも中がどうなっているのか分からなくて気持ち悪かったので実装してみた。

結果から言うと、セキュリティが重視される技術なので難解な感じなのかとおもいきや想像していたよりも楽だった。

#OAuth2の流れ
１．指定された情報を元にURLを作り、webViewでそれを開く。
２．おなじみの認証画面が出るのでユーザーは認証する。
３．(１)で作ったURLで指定したリダイレクト先に飛ぶので、そのURLにコードが付いている
４．そのコードをトークン発行APIに投げるとアクセストークンが返ってくる
５．APIを叩く際にヘッダーにアクセストークンを付けていれば認証したアカウントからのアクセスとして扱われる

超簡単ですね！

それではFeedly Cloud APIを例に実際に実装してみましょう


##よんでね
https://developer.feedly.com

開発用のIDとsecretとかはここにあるよ
https://groups.google.com/forum/#!topic/feedly-cloud/ZNn0UUOyCZw



#実装してみる

##１．指定された情報を元にURLを作り、webViewでそれを開く。

https://developer.feedly.com/v3/auth/

の通りにURLを組み立てます

```objc

- (NSURL*)authorizationURL{
    //https://developer.feedly.com/v3/auth/
    NSString*base = @"https://sandbox.feedly.com";
    NSString*api  = @"/v3/auth/auth";
    NSDictionary*params = @{@"response_type":@"code",
                            @"client_id":self.clientId,
                            @"redirect_uri":@"http://localhost",
                            @"scope":@"https://cloud.feedly.com/subscriptions"};
    NSMutableString*url = [NSMutableString string];
    [url appendString:base];
    [url appendString:api];
    [url appendString:@"?"];
    NSCharacterSet* chars = [NSCharacterSet alphanumericCharacterSet];
    for (NSString*key in params.allKeys) {
        NSString*param = [NSString stringWithFormat:@"%@=%@&",
                          key,
                          [params[key] stringByAddingPercentEncodingWithAllowedCharacters:chars]];
        [url appendString:param];
    }
    return [NSURL URLWithString:url];
}
``` 

これで生成したURLをUIWebViewで開くと、認証ページに移ります。

##２．おなじみの認証画面が出るのでユーザーは認証する。

![iOS Simulator Screen Shot Jul 16, 27 Heisei, 5.59.21 PM.png](https://qiita-image-store.s3.amazonaws.com/0/24979/38f749e3-0956-4b37-3741-6c2c4bb33feb.png)


##３．(１)で作ったURLで指定したリダイレクト先に飛ぶので、そのURLにコードが付いている

どれでもいいので認証すると、

```objc

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
```

で、`http://localhost/?code=*********`というアドレスを拾う事ができます。

この****の部分を取得したら

##４．そのコードをトークン発行APIに投げるとアクセストークンが返ってくる

```objc
    NSMutableString*url = [NSMutableString string];
    NSString*base = @"https://sandbox.feedly.com";
    NSString*api = @"/v3/auth/token";
    NSDictionary *params = @{@"client_id":self.clientId,
                            @"client_secret":self.clientSecret,
                            @"grant_type":@"authorization_code",
                            @"redirect_uri":@"http://localhost:8080",
                            @"code":code};
    [url appendString:base];
    [url appendString:api];
    NSError*error=nil;
    NSData*data=[NSJSONSerialization dataWithJSONObject:params options:2 error:&error];
    NSString*jsonstr=[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
    NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
    [req setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    [req setHTTPMethod:@"POST"];
    NSLog(@"%@",jsonstr);
    [req setHTTPBody:[jsonstr dataUsingEncoding:NSUTF8StringEncoding]];
    [NSURLConnection sendAsynchronousRequest:req
                                       queue:[NSOperationQueue mainQueue]
                           completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
                               NSLog(@"responce");
                               if (data) {
                                   NSError*JSONError = nil;
                                   NSDictionary*result = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&JSONError];
                                   if (JSONError) {
                                       return;
                                   }
                                   if ([[result allKeys] containsObject:@"access_token"]) {
//とれた
                                   }
                               } else {
                               }
                           }];
```

Referenceの通りにURLを組み立てて、POSTリクエストをします。

これで認証は完了です！

##５．APIを叩く際にヘッダーにアクセストークンを付けていれば認証したアカウントからのアクセスとして扱われる

APIを叩く際は`NSMutableURLRequest`に

```objc
[req addValue:token forHTTPHeaderField:@"Authorization"];
```
とするだけで、そのアカウントからのアクセスだと判定されます。


簡単でしょ！