oAuth авторизация в iOS

Нашел хорошую библиотеку, в которой реализована поддержка oAuth 1 & oAuth 2 для iOS/Mac OS. К сожалению, документации по ней нет, зато есть демка использования, в которой реализована поддержка авторизации для twitter, facebook, flickr, qq.

Далее расскажу как я делал авторизацию LinkedIn с ее помощью.

Создаем новый проект “Single View Application”, у меня он будет называться “LinkedInAuth”.

Подключаем зависимости:

Теперь нужно указать дополнительные флаги линкеру (это требует ytoolkit).

  • открываем “Build Settings”
  • в разделе “Linking” находим “Other Linker Flags” и добавляем туда “-all_load” и “-ObjC” (если вы не видите “Other Linker Flags” переключите отображение списка на “All”)

Нам нужен класс в проекте, в котором будут хранится ключ и секрет от LinkedIn’а. Для этого добавляем новый “Objective-C class” под названием AuthCredentials, унаследованный от NSObject. Мы потом уберем обьявленый Xcode’ом интерфейс. В AuthCredentials.h обявлием константы kLinkedInKey и kLinkedInSecret:

#import <Foundation/Foundation.h>
#import <ytoolkit/ydefines.h>

YEXTERN NSString * const kLinkedInKey;
YEXTERN NSString * const kLinkedInSecret;

А в AuthCredentials.m указываем значения наших констант:

#import "AuthCredentials.h"

NSString * const kLinkedInKey = @"сюда вписываем ключ";
NSString * const kLinkedInSecret = @"а сюда секрет )";

Добавляем в проект новый “Objective-C class”, который назовем AuthViewController и наследуем от UIViewController. Также не забываем поставить галку “With XIB for user interface”. Открываем AuthViewController.h и приводим его вот к такому виду:

#import <UIKit/UIKit.h> #import <ytoolkit/ydefines.h> #import “ASIHTTPRequest.h”

@class AuthViewController;

@protocol AuthViewControllerDelegate

  • (void)authViewControllerDidFinish:(AuthViewController *)controller; @end

@interface AuthViewController : UIViewController <UIWebViewDelegate, ASIHTTPRequestDelegate>

@property (assign, nonatomic) id delegate; @property (retain, nonatomic) IBOutlet UIWebView *webView; @property (copy, nonatomic) NSString *accesstoken; @property (copy, nonatomic) NSString *tokensecret; @property (copy, nonatomic) NSString *verifier; @property (copy, nonatomic) NSString *step; // нужен что бы в будующем понимать на каком шаге авторизации мы находимся

  • (IBAction)done:(id)sender;

@end

Открываем AuthViewController.xib, добавляем на него WebView и привязываем к нашему webView(с зажатым “control” тянем связть от “File’s Owner” до “WebView” и из выпавшего списка выбираем “webView”) и в обратном порядке выбирая delegate.

Теперь можно перейти к самой большой части, непосредственно к функционалу авторизации через LinkedIn для этого открываем наш AuthViewController.m и добавляем в него код.

Для начала прописываем нужные импорты:

#import "ASIHTTPRequest+YOAuthv1Request.h"
#import "AuthCredentials.h"
#import "SBJson/SBJson.h"
#import <ytoolkit/ymacros.h>
#import <ytoolkit/ycocoaadditions.h>
#import <ytoolkit/yoauthadditions.h>
В "viewDidLoad" начинам процедуру авторизации:
// создаем запрос
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"https://api.linkedin.com/uas/oauth/requestToken"]];
request.delegate = self; // указываем что мы будем обрабатывать ответ
[request setRequestMethod:@"POST"]; // тип запроса POST
// вызываем функцию подготовки запроса, которая добавит нужные параметры в url
[request prepareOAuthv1AuthorizationHeaderUsingConsumerKey:kLinkedInKey
                                         consumerSecretKey:kLinkedInSecret
                                                     token:nil
                                               tokenSecret:nil
                                           signatureMethod:YOAuthv1SignatureMethodHMAC_SHA1
                                                     realm:nil
                                                  verifier:nil
                                                  callback:@"http://linkedinauth"]; // линк нашего приложения
self.step = @"0"; // это первый шаг
[request startAsynchronous];

Добавляем метод который покажет ошибку если она произошла:

- (void)requestFailed:(ASIHTTPRequest *)request {
    UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:@"LinkedIn Auth"
                                                     message:[request.error localizedDescription]
                                                    delegate:nil
                                           cancelButtonTitle:@"OK"
                                           otherButtonTitles:nil]
                          autorelease];
    [alert show];
}

И обработчик вернувшегося запроса:

- (void)requestFinished:(ASIHTTPRequest *)request {
    NSDictionary *params = [request.responseString decodedUrlencodedParameters];
    self.accesstoken = [params objectForKey:YOAuthv1OAuthTokenKey]; // забираем токены
    self.tokensecret = [params objectForKey:YOAuthv1OAuthTokenSecretKey];

    if (self.accesstoken && self.tokensecret) {
        if ([self.step isEqualToString:@"0"]) {
       // если есть токены и это первый шаг, то открываем страницу авторизации LinekdIn в нашем WebView
            NSString *url = [NSString stringWithFormat:@"https://www.linkedin.com/uas/oauth/authorize?%@=%@", YOAuthv1OAuthTokenKey, self.accesstoken];
            NSMutableURLRequest *r = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
            [self.webView loadRequest:r];
        } else {
            // если шаг не первый, то завершаем процесс авторизации
            [self.delegate authViewControllerDidFinish:self];
        }
    }
}

Теперь ловим обновление WebView:

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    NSURL *url = request.URL;
    NSString *s = [url absoluteString];
    NSString *host = [url host];

    // если загружаемый урл принадлежит нашему приложению
    if ([host isEqualToString:@"linkedinauth"]) {
        NSDictionary *params = [s queryParameters];
        // проверяем не заприщен ли доступ
        if ([params objectForKey:@"denied"] == nil) {
            self.verifier = [params objectForKey:YOAuthv1OAuthVerifierKey]; // сохраняем верификационный код
            ASIHTTPRequest *r = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"https://api.linkedin.com/uas/oauth/accessToken"]];
            [r setRequestMethod:@"POST"];
            r.delegate = self;
            // отправляем запрос авторизации наших токенов
            [r prepareOAuthv1AuthorizationHeaderUsingConsumerKey:kLinkedInKey
                                               consumerSecretKey:kLinkedInSecret
                                                           token:self.accesstoken
                                                     tokenSecret:self.tokensecret
                                                 signatureMethod:YOAuthv1SignatureMethodHMAC_SHA1
                                                           realm:nil
                                                        verifier:self.verifier
                                                        callback:nil];
            self.step = @"1"; // дальше пошел второй шаг
            [r startAsynchronous];
        } else {
            NSString *deniedMsg = [NSString stringWithFormat:@"authorize denied: %@", [params objectForKey:@"denied"]];
            UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:[[self class] description]
                                                             message:deniedMsg
                                                            delegate:nil
                                                   cancelButtonTitle:@"OK"
                                                   otherButtonTitles:nil]
                                  autorelease];
            [alert show];
        }

        return NO;
    }

    return YES;
}

Вот собственно и все. Осталось только сохранить полученные нами токены. Для этого мы в ViewController.h добавляем объявление переменных, импорт контролера авторизации и делегирование AuthViewControllerDelegate:

#import <UIKit/UIKit.h>
#import "AuthViewController.h"

@interface ViewController : UIViewController <AuthViewControllerDelegate>

@property (strong, nonatomic) NSString *accesstoken;
@property (strong, nonatomic) NSString *tokensecret;

@end
Сохранение полученных значений в "ViewController.m":
- (void)authViewControllerDidFinish:(AuthViewController *)controller {
    self.accesstoken = controller.accesstoken;
    self.tokensecret = controller.tokensecret;
    [self dismissModalViewControllerAnimated:YES];
}

Исходный код: скачать