본문 바로가기
프로그래밍/iOS

iPhone 에서 OAuth 라이브러리 사용하기

by 백룡화검 2012. 2. 29.

지난 주에 아이폰에 OAuth 컨슈머 라이브러리 돌리는데 삽질을 너무 많이 해서 반성하는 의미로 포스팅 해본다.http://oauth.net에 있는 라이브러리는 아이폰에 바로 쓸 수 없어서 약간 손을 봐야하는데 누군가 이미 해놓은 것도 있지만 기본(?) 라이브러리로 한번 해봤다. 붙이는건 그렇게 어렵지 않은데 아이폰에 써드파티 바이너리 프레임웍 추가 안되는거랑 Security.framework 내용이 Mac의 것과 다르다는 걸 몰라서 시간을 엄청 허비했음.

  1. OAuthConsumer 라이브러리를 체크아웃 한다.

    svn checkout http://oauth.googlecode.com/svn/code/obj-c/ .

  2. 프레임웍으로 빌드해서 넣으면 깔끔하겠지만 아이폰에는 그렇게 넣을 수 없으니 소스를 직접 넣는다. 다운받은 OAuthConsumer 디렉토리 아래에서 필요한 파일만 남기고 모두 삭제한다.

    rm -rf English.lproj OAuthConsumer.xcodeproj *.plist *.pch *.rb *Test.?

  3. Xcode에서 OAuthTest 라는 이름의 View-Based Application 하나를 생성한다.

  4. Finder 에서 OAuthConsumer 디렉토리를 끌어다 Groups & Files 창의 Classes 아래에 놓는다. 파일을 복사할꺼니까 끌어다 놓을때 뜨는 'Copy items into destination group's folder'에 체크하고 Add 한다.

  5. 이 상태에서 빌드를 해보면 OAToken_KeychainExtensions.m 파일에서 에러가 난다. 원래 Mac Cocoa 환경이라면 Security.framework 라이브러리를 추가해서 컴파일 할 수 있지만 아이폰용 Security.framework 라이브러리는 맥용과 달라서 OAToken_KeychainExtensions.m 파일을 컴파일 시킬 수 없다. 하지만 이 파일은 꼭 필요한건 아니고 나중에 OAuth 인증 끝나고 토큰과 시크릿을 키체인에 안전하게 보관할 수 있게 도와주는 유틸리티인데 필요하면 알아서 구현하고 일단 지워도 문제 없다. 파일 목록에서 OAToken_KeychainExtensions.? 파일을 삭제하고 빌드하면 일단 문제없이 컴파일이 될 것이다.

  6. 시작하기 전에 OAuthConsumer.h 파일을 열어서 import 경로를 올바른 경로로 바꾸어주자.

    #import <Foundation/Foundation.h> #import <OAuthConsumer/OAToken.h> #import <OAuthConsumer/OAConsumer.h> #import <OAuthConsumer/OAMutableURLRequest.h> ...

    이걸 아래처럼 변경.

    #import <Foundation/Foundation.h> #import "OAToken.h" #import "OAConsumer.h" #import "OAMutableURLRequest.h" ...

  7. 이 제 OAuthTestViewController.xib 파일을 열어서 아래 그림처럼 웹뷰, 'Get Request Token' 버튼, 'Get Access Token' 버튼, PIN 번호를 입력받을 텍스트 필드가 있는 화면을 생성한다. 'Get Request Token' 버튼을 누르면 Request Token을 얻어와서 트위터의 Authorize URL로 이동시키고, 사용자가 로그인 한 후 보여지는 PIN 번호를 넣고 'Get Access Token' 버튼을 누르면 Access Token을 가져올 것이다.

  8. 위에서 추가한 웹뷰, 텍스트필드의 아웃렛 변수와 두 버튼에 연결될 액션 메서드를 만들고 연결한다.

    #import <UIKit/UIKit.h> @interface OAuthTestViewController : UIViewController { UIWebView *webView; UITextField *textField; } @property (nonatomic, retain) IBOutlet UIWebView *webView; @property (nonatomic, retain) IBOutlet UITextField *textField; - (IBAction)requestTokenButton:(UIButton *)sender; - (IBAction)accessTokenButton:(UIButton *)sender; @end

  9. 먼 저 'Get Request Token' 버튼을 눌렀을때 Request Token을 얻어와서 로그에 찍어보자. 아래처럼 호출하도록 requestTokenButton: 메서드를 작성하고 성공/에러시에 호출될 델리게이트 메서드도 만든다.

    #import "OAuthConsumer.h" ... - (IBAction)requestTokenButton:(UIButton *)sender { OAConsumer *consumer = [[OAConsumer alloc] initWithKey:@"Z0DxPl4q7kmSOgh3LTpV4Q" secret:@"LWryiOYBAHJ58PX9Yn1yG5bzDwpMJiksqpXxfst7kcU"]; NSURL *url = [[NSURL alloc] initWithString:@"http://twitter.com/oauth/request_token"]; OAMutableURLRequest *request = [[OAMutableURLRequest alloc] initWithURL:url consumer:consumer token:nil realm:@"http://twitter.com/" signatureProvider:nil]; [request setOAuthParameterName:@"oauth_callback" withValue:@"oob"]; OADataFetcher *fetcher = [[OADataFetcher alloc] init]; [fetcher fetchDataWithRequest:request delegate:self didFinishSelector:@selector(requestTokenTicket:didFinishWithData:) didFailSelector:@selector(requestTokenTicket:didFailWithError:)]; [consumer release]; [url release]; [request release]; [fetcher release]; } - (void)requestTokenTicket:(OAServiceTicket *)ticket didFinishWithData:(NSData *)data { if (ticket.didSucceed) { NSString *responseBody = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; OAToken *token = [[OAToken alloc] initWithHTTPResponseBody:responseBody]; NSLog(@"Request Token: key=%@, secret=%@", [token key], [token secret]); [token release]; [responseBody release]; } else { NSLog(@"Finish but did not succeed."); } } - (void)requestTokenTicket:(OAServiceTicket *)ticket didFailWithError:(NSError *)error { NSLog(@"Error: %@", [error localizedDescription]); }

    위와 같이 작성해서 실행해보면 requestTokenTicket:didFailWithError: 쪽으로 떨어지면서,

    Operation could not be completed. (NSURLErrorDomain error -1012.)

    이런 메시지가 떨어진다. 이유는,

    [request setOAuthParameterName:@"oauth_callback" withValue:@"oob"];

    이 부분 때문인데 oauth_callback 이라는 파라미터를 추가하고 싶어서 이렇게 적었으면 이 파라미터를 포함하여 시그니쳐를 생성해야 하는데 OAuthConsumer 라이브러리에서는 추가된 파라미터를 제외하고 시그니처를 생성해 요청했고 결과적으로 트위터에서 시그니처가 틀리다는 이유로 200 OK가 떨어지지 않는 상태이다(버그이거나 OAuth 1.0a 스펙을 고려하지 않아서인 듯). 추가된 파라미터를 포함하여 시그니처를 생성하도록 OAMutableURLRequest.m 파일을 수정해보자. 파일을 열어서 _signatureBaseString 메서드 중간쯤에,

    ... for (OARequestParameter *param in [self parameters]) { [parameterPairs addObject:[param URLEncodedNameValuePair]]; } for(NSString *parameterName in [[extraOAuthParameters allKeys] sortedArrayUsingSelector:@selector(compare:)]) { [parameterPairs addObject:[[OARequestParameter requestParameterWithName:parameterName value:[extraOAuthParameters objectForKey:parameterName]] URLEncodedNameValuePair]]; } NSArray *sortedPairs = [parameterPairs sortedArrayUsingSelector:@selector(compare:)]; NSString *normalizedRequestParameters = [sortedPairs componentsJoinedByString:@"&"]; ...

    이렇게 for 루프를 하나 추가한다. 이제 다시 실행해서 'Get Request Token' 버튼을 눌러보면,

    2010-03-05 17:09:40.438 OAuthTest[1691:207] Request Token: key=wyGw0VUSE79MTuVm0iqs7ZdWiPI3ZRJWCZ2k6P73w, secret=AUGrkHfx3nr6xb9Q5dUbNQzykZQvFJ62WW90BojT8

    콘솔에 이렇게 찍히면서 정상적으로 토큰을 얻어올 수 있다.

  10. 이제 Request Token을 얻어왔으니 토큰을 임시로 저장하고 트위터의 Authorize URL을 호출한다. 아래처럼 OAuthTestViewController.h 파일을 수정하고,

    #import <UIKit/UIKit.h> @class OAServiceTicket; @class OAToken; @interface OAuthTestViewController : UIViewController { UIWebView *webView; UITextField *textField; OAToken *requestToken; } @property (nonatomic, retain) IBOutlet UIWebView *webView; @property (nonatomic, retain) IBOutlet UITextField *textField; @property (nonatomic, retain) OAToken *requestToken; ...

    아래처럼 OAuthTestViewController.m 파일을 수정한다.

    if (ticket.didSucceed) { NSString *responseBody = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; OAToken *token = [[OAToken alloc] initWithHTTPResponseBody:responseBody]; NSLog(@"Request Token: key=%@, secret=%@", [token key], [token secret]); self.requestToken = token; [token release]; [responseBody release]; NSString *authorizeURL = [[NSString alloc] initWithFormat:@"http://twitter.com/oauth/authorize?oauth_token=%@", [self.requestToken.key URLEncodedString]]; [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:authorizeURL]]]; [authorizeURL release]; } ...

    이제 실행하고 'Get Request Token' 버튼을 누르면 아래 이미지 처럼 인증을 요구하는 화면이 나오고 Allow 하고 나면,

    아래 이미지 처럼 PIN 번호가 보여진다.

  11. 이 제 PIN 번호를 입력하고 'Get Access Token'을 클릭했을 때의 처리를 하면 된다. accessTokenButton: 메서드를 아래처럼 작성하고 accessTokenTicket:didFinishWithData:, accessTokenTicket:didFailWithError: 델리게이트 메서드도 만들자.

    - (IBAction)accessTokenButton:(UIButton *)sender { OAConsumer *consumer = [[OAConsumer alloc] initWithKey:@"Z0DxPl4q7kmSOgh3LTpV4Q" secret:@"LWryiOYBAHJ58PX9Yn1yG5bzDwpMJiksqpXxfst7kcU"]; NSURL *url = [[NSURL alloc] initWithString:@"http://twitter.com/oauth/access_token"]; OAMutableURLRequest *request = [[OAMutableURLRequest alloc] initWithURL:url consumer:consumer token:self.requestToken realm:@"http://twitter.com/" signatureProvider:nil]; [request setOAuthParameterName:@"oauth_verifier" withValue:self.textField.text]; OADataFetcher *fetcher = [[OADataFetcher alloc] init]; [fetcher fetchDataWithRequest:request delegate:self didFinishSelector:@selector(accessTokenTicket:didFinishWithData:) didFailSelector:@selector(accessTokenTicket:didFailWithError:)]; [consumer release]; [url release]; [request release]; [fetcher release]; } - (void)accessTokenTicket:(OAServiceTicket *)ticket didFinishWithData:(NSData *)data { if (ticket.didSucceed) { NSString *responseBody = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; OAToken *token = [[OAToken alloc] initWithHTTPResponseBody:responseBody]; NSLog(@"Got Access Token [key:%@, secret:%@]", token.key, token.secret); [token release]; [responseBody release]; } else { NSLog(@"Finish but did not succeed."); } } - (void)accessTokenTicket:(OAServiceTicket *)ticket didFailWithError:(NSError *)error { NSLog(@"Error: %@", [error localizedDescription]); }

  12. 이제 실행해서 PIN 번호를 넣고 'Get Access Token' 버튼을 누르면 아래처럼 Access Token 정보가 콘솔에 출력된다.

    Got Access Token [key:14368376-CyPcPg7uVLcOkzRJBlJLMVKqZQMzCMQnRxHMmzQG8, secret:HY3eENZ6Pt4AFdUUtzAROlZDaKHOJHtcc7lMKMyKE]

  13. 이 제 위에서 출력되는 Access Token 정보를 저장해서 OAMutableURLRequest 클래스로 요청 할 때 token 인자에 넣어 보내면 된다. 요청 할 때 파라미터를 세팅해야 할 경우에는, OARequestParameter 파라미터를 NSArray 로 묶어 OAMutableURLRequest 생성 후에 setParameter: 해주면 된다.

틀린 내용이 있다면 지적해주세요!

출처 : http://kohanabi.tistory.com/68

'프로그래밍 > iOS' 카테고리의 다른 글

iOS Flip Transform  (0) 2012.03.06
Creating an iPad flip-clock with Core Animation  (0) 2012.03.06
OAuthConsumer를 이용한 xAuth  (0) 2012.02.29
localize  (0) 2012.02.17
Advance Localization in ios apps  (0) 2012.02.17