2013-04-10 56 views
8

我一整天都在爲這件事煩惱。在iOS應用中實現OAuth 1.0

我希望將我的iOS應用程序與Withings api集成。它使用OAuth 1.0,我似乎無法完全理解如何實現它。

我一直在下載多個OAuth framworks(MPOAuth,gtm-oauth,ssoauthkit),但不能完全弄清楚我應該做什麼。

我搜索了很多,也在堆棧溢出,以便如何實現OAuth 1.0的一般&與Withings尤其沒有成功的集成。

請解釋將iOS應用程序與需要OAuth 1.0的api集成的流程。代碼示例會非常有幫助。建議的第三方框架也不錯。

爲了澄清,我完全理解了OAuth 1.0原則,我只是在我的應用程序中實際執行它時遇到問題。

我認爲,通過代碼示例和良好參考的詳盡解答對於很多人來說都是非常有用的,因爲我無法找到它。如果任何人有很好的實施經驗,請花時間分享。

+0

你看[在GTM-的OAuth包括示例項目(https://github.com/jdg/gtm-oauth/tree/master/Examples/OAuthSample)? – bdesham 2013-04-10 16:00:32

+0

是的,我有。這些示例項目真的很難瀏覽並完全理解它們。 – 2013-04-10 16:11:47

+0

這裏有很多不好的OAuth代碼,尤其是iOS。所以要小心。我遇到過的唯一體面的就是Google的(gtm-oauth),因此我可以收到+1。 – 2013-04-10 17:43:55

回答

11

TDOAuth在我看來是最好的解決方案。它是乾淨和簡單,只有一個h和.m文件的工作,並沒有複雜的示例項目..

這是OAuth的1.0流量:

第1步 - 獲取請求令牌

//withings additional params 
NSMutableDictionary *dict = [NSMutableDictionary dictionary]; 
[dict setObject:CALL_BACK_URL forKey:@"oauth_callback"]; 

//init request 
NSURLRequest *rq = [TDOAuth URLRequestForPath:@"/request_token" GETParameters:dict scheme:@"https" host:@"oauth.withings.com/account" consumerKey:WITHINGS_OAUTH_KEY consumerSecret:WITHINGS_OAUTH_SECRET accessToken:nil tokenSecret:nil]; 

//fire request 
NSURLResponse* response; 
NSError* error = nil; 
NSData* result = [NSURLConnection sendSynchronousRequest:rq returningResponse:&response error:&error]; 
NSString *s = [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding]; 
//parse result 
NSMutableDictionary *params = [NSMutableDictionary dictionary]; 
NSArray *split = [s componentsSeparatedByString:@"&"]; 
for (NSString *str in split){ 
    NSArray *split2 = [str componentsSeparatedByString:@"="]; 
    [params setObject:split2[1] forKey:split2[0]]; 
} 

token = params[@"oauth_token"]; 
tokenSecret = params[@"oauth_token_secret"]; 

步驟2 - 獲取授權令牌(通過加載在一個UIWebView的請求,webViewDidFinishLoad委託方法將處理呼叫背面..)

//withings additional params 
NSMutableDictionary *dict2 = [NSMutableDictionary dictionary]; 
[dict setObject:CALL_BACK_URL forKey:@"oauth_callback"]; 

//init request 
NSURLRequest *rq2 = [TDOAuth URLRequestForPath:@"/authorize" GETParameters:dict2 scheme:@"https" host:@"oauth.withings.com/account" consumerKey:WITHINGS_OAUTH_KEY consumerSecret:WITHINGS_OAUTH_SECRET accessToken:token tokenSecret:tokenSecret]; 

webView.delegate = self; 
[DBLoaderHUD showDBLoaderInView:webView]; 
[webView loadRequest:rq2]; 

處理web視圖如下發起第3步(我知道isAuthorizeCallBack聞起來有很多,但它的工作,應該重構它..)

- (void)webViewDidFinishLoad:(UIWebView *)aWebView 
{ 
    [DBLoaderHUD hideDBLoaderInView:webView]; 

    NSString *userId = [self isAuthorizeCallBack]; 
    if (userId) { 

     //step 3 - get access token 
     [DBLoaderHUD showDBLoaderInView:self.view]; 
     [self getAccessTokenForUserId:userId]; 
    } 

    //ugly patchup to fix an invalid token bug 
    if ([webView.request.URL.absoluteString isEqualToString:@"http://oauth.withings.com/account/authorize?"]) 
    [self startOAuthFlow]; 
} 

- (NSString *)isAuthorizeCallBack 
{ 
    NSString *fullUrlString = webView.request.URL.absoluteString; 

    if (!fullUrlString) 
     return nil; 

    NSArray *arr = [fullUrlString componentsSeparatedByString:@"?"]; 
    if (!arr || arr.count!=2) 
     return nil; 

    if (![arr[0] isEqualToString:CALL_BACK_URL]) 
     return nil; 

    NSString *resultString = arr[1]; 
    NSArray *arr2 = [resultString componentsSeparatedByString:@"&"]; 
    if (!arr2 || arr2.count!=3) 
     return nil; 

    NSString *userCred = arr2[0]; 
    NSArray *arr3 = [userCred componentsSeparatedByString:@"="]; 
    if (!arr3 || arr3.count!=2) 
     return nil; 

    if (![arr3[0] isEqualToString:@"userid"]) 
     return nil; 

    return arr3[1]; 
} 

- (void)startOAuthFlow 
{ 
    [self step1]; 
    [self step2]; 
} 

,最後 - 第3步 - 獲得訪問令牌

- (void)getAccessTokenForUserId:(NSString *)userId 
{ 
    //step 3 - get access token 

    //withings additional params 
    NSMutableDictionary *dict = [NSMutableDictionary dictionary]; 
    [dict setObject:CALL_BACK_URL forKey:@"oauth_callback"]; 
    [dict setObject:userId forKey:@"userid"]; 

    //init request 
    NSURLRequest *rq = [TDOAuth URLRequestForPath:@"/access_token" GETParameters:dict scheme:@"https" host:@"oauth.withings.com/account" consumerKey:WITHINGS_OAUTH_KEY consumerSecret:WITHINGS_OAUTH_SECRET accessToken:token tokenSecret:tokenSecret]; 

    //fire request 
    NSURLResponse* response; 
    NSError* error = nil; 
    NSData* result = [NSURLConnection sendSynchronousRequest:rq returningResponse:&response error:&error]; 
    NSString *s = [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding]; 

    //parse result 
    NSMutableDictionary *params = [NSMutableDictionary dictionary]; 
    NSArray *split = [s componentsSeparatedByString:@"&"]; 
    for (NSString *str in split){ 
     NSArray *split2 = [str componentsSeparatedByString:@"="]; 
     [params setObject:split2[1] forKey:split2[0]]; 
    } 

    [self finishedAthourizationProcessWithUserId:userId AccessToken:params[@"oauth_token"] AccessTokenSecret:params[@"oauth_token_secret"]]; 
} 
+0

我已經實現了你的代碼,但是我陷入了第3步。我在webview中顯示Withings登錄頁面,但它總是返回「Invalid oath token」。我錯過了什麼嗎?提前致謝。 – Dmorneault 2013-07-14 18:51:25

+0

@Dmorneault,我用一個小小的修正編輯了答案。查看webViewDidFinishLoad方法並在標記「醜陋補丁修復無效令牌錯誤」下添加代碼。注意它調用了一個名爲「startOAuthFlow」的方法,它只是step1和step2。似乎有一個緩存問題,我試着用Withings支持解決它,但我最終生活在這個醜陋但工作的補丁.. – 2013-07-15 10:44:44

+0

你是如何創建身份驗證後與API交互的請求? – 2013-12-14 18:49:16

0

我建議你檢查這個項目既作爲參考,也作爲一個真正的工作OAuth類。它繼承了另一個偉大的項目,因此您需要在您的項目中添加這兩個項目。檢查許可證是否適合您的要求。 https://github.com/rsieiro/RSOAuthEngine

+0

如果該項目已過時,則無法使用downvote。在答案的時候,它的功能完全適用於我的一個應用程序,並取得了成功。 – Andrea 2015-07-18 10:58:03

2

我additionaly這裏保存請求頭

NSMutableDictionary *dict2 = [NSMutableDictionary dictionary]; 
[dict2 setObject:CALL_BACK_URL forKey:@"oauth_callback"]; 
NSURLRequest *rq2 = [TDOAuth URLRequestForPath:@"/authorize" 
           GETParameters:dict2 
             scheme:@"https" 
              host:@"oauth.withings.com/account" 
            consumerKey:WITHINGS_OAUTH_KEY 
           consumerSecret:WITHINGS_OAUTH_SECRET 
            accessToken:self.token 
            tokenSecret:self.tokenSecret]; 
headers = rq2.allHTTPHeaderFields; 

而在回調方法中,我會將缺少的參數添加到請求中。通過這樣做,我避免了「醜陋的補丁修復」。

- (BOOL)webView:(UIWebView *)wV shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{ 
if (![request.allHTTPHeaderFields objectForKey:@"Authorization"] && 
    [request.URL.absoluteString rangeOfString:@"acceptDelegation=true"].location == NSNotFound){ 
    NSMutableURLRequest *mutableCp = [request mutableCopy]; 
    NSLog(@"request :::%@", request); 
    [mutableCp setAllHTTPHeaderFields:headers]; 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     [webView loadRequest:mutableCp]; 
    }); 
    return NO; 
} 
return YES; 
} 

我希望這會幫助別人