2013-04-10 1 views
8

Mi sono rotto la testa per tutto il giorno.Implementazione di OAuth 1.0 in un'app per iOS

Desidero integrare la mia app iOS con Withings api. Usa OAuth 1.0 e non riesco a capire appieno come implementarlo.

Ho scaricato più framworks OAuth (MPOAuth, gtm-oauth, ssoauthkit) ma non sono riuscito a capire completamente cosa esattamente dovrei fare.

Ho cercato molto, anche in overflow dello stack per buoni riferimenti su come implementare OAuth 1.0 in generale & integrandosi con Withings in particolare senza successo.

Gentilmente spiegare il flusso di integrazione di un'app per iOS con un'API che richiede OAuth 1.0. Gli esempi di codice sarebbero molto utili. Anche i quadri di terze parti suggeriti sarebbero belli.

Giusto per chiarire, comprendo pienamente i principi di OAuth 1.0, ho solo problemi con l'implementazione effettiva nella mia app.

Penso che una risposta esaustiva con esempi di codice e riferimenti validi sarebbe molto utile per molte persone perché non riuscivo a trovarne una. Se qualcuno ha una buona esperienza con l'implementazione, si prega di prendere il tempo per condividerlo.

+0

hai guardato [il progetto di esempio incluso in gtm-OAuth] (https://github.com/jdg/gtm-oauth/tree/master/Examples/OAuthSample)? – bdesham

+0

si ce l'ho. Questi esempi di progetti sono davvero difficili da navigare e comprenderli completamente. –

+0

C'è molto codice OAuth non valido, in particolare per iOS. Quindi attenzione. L'unica persona decente che abbia mai incontrato è Google (gtm-oauth), quindi riceve un +1 da me. –

risposta

11

TDOAuth a mio parere era la soluzione migliore. è pulito e semplice, una sola .he di file .m con cui lavorare, e non ci sono esempi complicati progetti ..

Questa è l'OAuth 1.0 flusso:

step 1 - ottenere richiesta di token

//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"]; 

fase 2 - ottenere l'autorizzazione di token (caricando la richiesta in un UIWebView, il metodo webViewDidFinishLoad delegato gestirà la chiamata indietro ..)

//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]; 

gestire la WebView da seguire per avviare il passaggio 3 (so che l'isAuthorizeCallBack odori molto, ma fa il lavoro, dovrebbe refactoring ..)

- (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]; 
} 

e, infine, - punto 3 - ottenere token di accesso

- (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

Ho implementato il tuo codice ma mi sto bloccando al punto 3. Visualizzo la pagina di login di Withings nella webview ma restituisce sempre con "token di giuramento non valido". Mi sto perdendo qualcosa? Grazie in anticipo. – Dmorneault

+0

@Dmorneault, ho modificato la risposta con una piccola correzione. guarda il metodo webViewDidFinishLoad e aggiungi il codice sotto il segno "ugly patchup per correggere un bug di token non valido". si noti che chiama un metodo denominato "startOAuthFlow" che è di nuovo semplicemente step1 e step2. Sembra esserci un problema di cache, e ho provato a risolverlo con il supporto di Withings, ma alla fine ho vissuto questa brutta ma funzionante patch .. –

+0

come hai creato le richieste di interazione con l'API dopo l'autenticazione? –

0

Ti suggerisco di controllare questo progetto sia come riferimento sia come classe OAuth realmente funzionante. Si eredita da un altro grande progetto, quindi dovrai aggiungere entrambi i tuoi. Controlla se la licenza soddisfa le tue esigenze. https://github.com/rsieiro/RSOAuthEngine

+0

Se il progetto è obsoleto, è inutile procedere a downvote. Al momento della risposta ha funzionato perfettamente e ho usato in una mia app con successo. – Andrea

2

ho additionaly risparmio intestazioni di richiesta qui

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; 

E nel metodo di callback aggiungo i parametri mancanti alla richiesta. In questo modo, evito "brutto aggiornamento patchup".

- (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; 
} 

spero che aiutare qualcuno