2014-04-09 13 views
11

Sto utilizzando l'SDK di Facebook iOS per inviare il token di accesso di Facebook al mio URI del server Django. La funzione views.py corrispondente è mostrata di seguito e ottengo un codice di risposta 200 quando eseguo il POST da iOS. Tuttavia, ho un secondo URI decorato @ login_required che chiamo dal dispositivo iOS immediatamente afterword che mi considera non connesso e che mi reindirizza alla mia pagina principale. Che cosa sto facendo di sbagliato? Come posso "rimanere" connesso dopo il mio POST di successo da iOS?django-allauth accesso con token Facebook dal dispositivo iOS

# For POSTing the facebook token 
from django.views.decorators.csrf import csrf_exempt 
from allauth.socialaccount import providers 
from allauth.socialaccount.models import SocialLogin, SocialToken, SocialApp 
from allauth.socialaccount.providers.facebook.views import fb_complete_login 
from allauth.socialaccount.helpers import complete_social_login 

# Log in from Facebook 
@csrf_exempt 
def mobile_facebook_login(request): 
    response = HttpResponse() ## Create an HTTP Response Object 
    if request.method == "POST": # The method better be a POST 
     access_token = request.POST.get('access_token') # Get token 
     try: 
      app = SocialApp.objects.get(provider="facebook") 
      token = SocialToken(app=app, token=access_token) 

      # Check token against facebook     
      login = fb_complete_login(request, app, token) 
      login.token = token 
      login.state = SocialLogin.state_from_request(request) 

      # Add or update the user into users table 
      ret = complete_social_login(request, login) 

      # If we get here we've succeeded 
      response['Auth-Response'] = 'success' 
      response.status_code = 200 # Set status 
      return response 
     except Exception,e: 
      # If we get here we've failed 
      response['Auth-Response'] = 'failure: %s'%(e) 
      response.status_code = 401 # Set status 
      return response 
    else: 
     # If we get here we've failed 
     response['Auth-Response'] = 'failure' 
     response.status_code = 401 # Set status 
     return response 

UPDATE ======= ==========

Ok, grazie per i commenti. Così ora invio anche l'indirizzo email di Facebook e chiedo l'accesso e l'accesso manuale. Tuttavia, le richieste successive STILL non sono autenticate. Quindi il decoratore @login_required fallisce ancora. Altre idee?

# Log in from Facebook 
@csrf_exempt 
def mobile_facebook_login(request): 
    response = HttpResponse() ## Create an HTTP Response Object 
    if request.method == "POST": # The method better be a POST 
     access_token = request.POST.get('access_token') # Get token 
     email = request.POST.get('email') # Get email 
     try: 
      app = SocialApp.objects.get(provider="facebook") 
      token = SocialToken(app=app, token=access_token) 

      # Check token against facebook     
      login = fb_complete_login(request, app, token) 
      login.token = token 
      login.state = SocialLogin.state_from_request(request) 

      # Add or update the user into users table 
      ret = complete_social_login(request, login) 

      # Try to get username from email 
      try: 
         user = User.objects.get(email=email) # Get User 
       # Login the user from Django's perspective 
       user.backend = 'django.contrib.auth.backends.ModelBackend' 
       auth_login(request,user) 
       except User.DoesNotExist: 
         # If we get here we've failed 
       response['Auth-Response'] = 'failure: %s'%(e) 
       response.status_code = 401 # Set status 
       return response 

      # If we get here we've succeeded 
      response['Auth-Response'] = 'success' 
      response.status_code = 200 # Set status 
      return response 
     except Exception,e: 
      # If we get here we've failed 
      response['Auth-Response'] = 'failure: %s'%(e) 
      response.status_code = 401 # Set status 
      return response 
    else: 
     # If we get here we've failed 
     response['Auth-Response'] = 'failure' 
     response.status_code = 401 # Set status 
     return response 

==== Un altro aggiornamento ==========

Sulla base della seconda risposta in questo post: django authentication without a password

ho creato un backend di login personalizzato che fa non richiede una password. La terza risposta in questo post discute come:

user.backend = 'django.contrib.auth.backends.ModelBackend' 
login(request, user) 

Non memorizza la convalida dell'accesso nella sessione. Così ho provato a utilizzare un back-end personalizzato.

Ecco il mio codice modificato:

# Log in from Facebook 
@csrf_exempt 
def mobile_facebook_login(request): 
    response = HttpResponse() ## Create an HTTP Response Object 
    if request.method == "POST": # The method better be a POST 
     access_token = request.POST.get('access_token') # Get token 
     email = request.POST.get('email') # Get email 
     try: 
      app = SocialApp.objects.get(provider="facebook") 
      token = SocialToken(app=app, token=access_token) 

      # Check token against facebook     
      login = fb_complete_login(request, app, token) 
      login.token = token 
      login.state = SocialLogin.state_from_request(request) 

      # Add or update the user into users table 
      ret = complete_social_login(request, login) 

      # Try to get username from email 
      try: 
         user = User.objects.get(email=email) # Get User 
       # Login the user from Django's perspective 
       user.backend = 'django_tours.auth_backend.PasswordlessAuthBackend' 
       user = authenticate(email=user.email) 
       auth_login(request,user) 
       #request.session.cycle_key() 
        except User.DoesNotExist: 
         # If we get here we've failed 
       response['Auth-Response'] = 'failure: %s'%(e) 
       response.status_code = 401 # Set status 
       return response 

      # If we get here we've succeeded 
      response['Auth-Response'] = 'success' 
      response['User-Is-Authenticated'] = '%s'%(request.user.is_authenticated()) 
      response.status_code = 200 # Set status 
      return response 
     except Exception,e: 
      # If we get here we've failed 
      response['Auth-Response'] = 'failure: %s'%(e) 
      response.status_code = 401 # Set status 
      return response 
    else: 
     # If we get here we've failed 
     response['Auth-Response'] = 'failure' 
     response.status_code = 401 # Set status 
     return response 

Utilizzando hurl.it ho questa risposta HTTP 200, ma sono ancora considerati loggato da iPhone:

Auth-Response: success 
Content-Encoding: gzip 
Content-Length: 20 
Content-Type: text/html; charset=utf-8 
Date: Thu, 08 May 2014 00:22:47 GMT 
Server: Apache/2.2.22 (Ubuntu) 
Set-Cookie: csrftoken=UuJDP6OB3YCSDtXLEa10MgJ70tDtIfZX; expires=Thu, 07-May-2015 00:22:48 GMT; Max-Age=31449600; Path=/, sessionid=kdr061v1pcsbqtvgsn3pyyqj9237z6k8; expires=Thu, 22-May-2014 00:22:48 GMT; httponly; Max-Age=1209600; Path=/, messages="4f919699a4730a3df220a0eb3799ed59d2756825$[[\"__json_message\"\0540\05425\054\"Successfully signed in as philbot.\"]]"; Path=/ 
User-Is-Authenticated: True 
Vary: Cookie,Accept-Encoding 
+0

Hai provato a farlo nel browser? Il problema si ripresenta se chiami URL dal browser? I cookie sono abilitati nell'SDK di iOS? –

+0

Non penso che tu possa usare @login_required. La prossima volta che chiami una vista django, django non sa chi è l'utente, perché il sdk non passerà le informazioni sui cookie. L'uscita è per passare il token per ogni chiamata effettuata alla vista e accedere all'utente utilizzando un decoratore personalizzato. Tieni presente che non è la migliore strada da seguire. –

+0

Non ho molte informazioni su 'allauth'. Tuttavia, so che Django 'login_required' farà qualcosa come' if request.user.is_authorized() == True'. Non vedo nulla cambia lo stato di autorizzazione dell'utente, a meno che Allauth lo stia facendo per te. Quindi quello che sto suggerendo è di cambiare lo stato del tuo utente 'django.auth' e rendere is_authorized = True. Assicurati che il 'Session' sia creato in modo che quando l'utente si sposta su un'altra vista Django possa controllare lo stato di autenticazione dell'utente. Lasciaci cosa vedere dopo questo processo, così possiamo aiutarti. – Othman

risposta

0

Grazie per tutto l'aiuto e l'input - ho finalmente risolto. Non conosco la causa alla radice esatta del motivo per cui l'accesso con Facebook ha modificato i cookie e l'accesso standard ha funzionato correttamente. Ho notato che il dominio dei cookie restituiti dal login di Facebook è stato formattato con un leader "."Come questo:

[ .domain.com ] 

considerando che il login standard che ha lavorato aveva domini di cookie come questo:

[ www.domain.com ] 

ho analizzato i cookie dalla risposta HTTP dopo l'accesso con successo a Facebook e li memorizzati nella Singleton :

   // Extract cookie information 
       NSRange range = [cookieString rangeOfString:@"csrftoken="]; 
       if (range.location!=NSNotFound){ 
        cookieString = [cookieString substringFromIndex:NSMaxRange(range)]; 
        range = [cookieString rangeOfString:@";"]; 
        if (range.location!=NSNotFound){ 
         self.appDelegate.djangoCsrftoken = [cookieString substringToIndex:range.location]; 
        } 
       } 
       range = [cookieString rangeOfString:@"sessionid="]; 
       if (range.location!=NSNotFound){ 
        cookieString = [cookieString substringFromIndex:NSMaxRange(range)]; 
        range = [cookieString rangeOfString:@";"]; 
        if (range.location!=NSNotFound){ 
         self.appDelegate.djangoSessionId = [cookieString substringToIndex:range.location]; 
        } 
       } 

       if (LOGIN_DEBUG) { // Debug the response 
        NSLog(@"Extracted csrftoken is: %@",self.appDelegate.djangoCsrftoken); 
        NSLog(@"Extracted sessionid is: %@",self.appDelegate.djangoSessionId); 
       } 

dunque io, creato quei biscotti in modo esplicito per la seguente richiesta:

// Clear all cookies when app launches 
    NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; 
    for (NSHTTPCookie *each in cookieStorage.cookies) { 
     //if ([each.domain isEqualToString:DOMAIN]) { 
     NSLog(@"Deleting cookie: %@ -- %@",each.name,each.domain); 
     [cookieStorage deleteCookie:each]; 
     //} 
    } 

    //////////////// CSRF TOKEN ///////////////////// 

    // Create cookies based on parsed values 
    NSMutableDictionary *cookieCsrfProperties = [NSMutableDictionary dictionary]; 
    [cookieCsrfProperties setObject:@"csrftoken" forKey:NSHTTPCookieName]; 
    [cookieCsrfProperties setObject:self.appDelegate.djangoCsrftoken forKey:NSHTTPCookieValue]; 
    [cookieCsrfProperties setObject:DOMAIN forKey:NSHTTPCookieDomain]; 
    [cookieCsrfProperties setObject:DOMAIN forKey:NSHTTPCookieOriginURL]; 
    [cookieCsrfProperties setObject:@"/" forKey:NSHTTPCookiePath]; 
    [cookieCsrfProperties setObject:@"0" forKey:NSHTTPCookieVersion]; 

    // Set expiration to one month from now or any NSDate of your choosing 
    // this makes the cookie sessionless and it will persist across web sessions and app launches 
    /// if you want the cookie to be destroyed when your app exits, don't set this 
    [cookieCsrfProperties setObject:[[NSDate date] dateByAddingTimeInterval:2629743] forKey:NSHTTPCookieExpires]; 

    NSHTTPCookie *csrfCookie = [NSHTTPCookie cookieWithProperties:cookieCsrfProperties]; 
    [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:csrfCookie]; 

    //////////////// SessionId TOKEN ///////////////////// 

    // Create cookies based on parsed values 
    NSMutableDictionary *cookieSessionIdProperties = [NSMutableDictionary dictionary]; 
    [cookieSessionIdProperties setObject:@"sessionid" forKey:NSHTTPCookieName]; 
    [cookieSessionIdProperties setObject:self.appDelegate.djangoSessionId forKey:NSHTTPCookieValue]; 
    [cookieSessionIdProperties setObject:DOMAIN forKey:NSHTTPCookieDomain]; 
    [cookieSessionIdProperties setObject:DOMAIN forKey:NSHTTPCookieOriginURL]; 
    [cookieSessionIdProperties setObject:@"/" forKey:NSHTTPCookiePath]; 
    [cookieSessionIdProperties setObject:@"0" forKey:NSHTTPCookieVersion]; 

    // Set expiration to one month from now or any NSDate of your choosing 
    // this makes the cookie sessionless and it will persist across web sessions and app launches 
    /// if you want the cookie to be destroyed when your app exits, don't set this 
    [cookieCsrfProperties setObject:[[NSDate date] dateByAddingTimeInterval:2629743] forKey:NSHTTPCookieExpires]; 

    NSHTTPCookie *sessionIdCookie = [NSHTTPCookie cookieWithProperties:cookieSessionIdProperties]; 
    [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:sessionIdCookie]; 

    /////////////////////////////////////////////////// 

    // Create request 
    NSURL *url = [NSURL URLWithString:requestUrl]; 
    NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url]; 
    urlRequest.HTTPShouldHandleCookies = YES; 

    NSHTTPCookie *setCookie; 
    for (setCookie in [NSHTTPCookieStorage sharedHTTPCookieStorage].cookies) { 
     if (([setCookie.name isEqualToString:@"csrftoken" ] || [setCookie.name isEqualToString:@"sessionid"])) { 
      NSLog(@"Adding Cookie: %@ = %@ [ %@ ]", setCookie.name, setCookie.value, setCookie.domain); 
      [urlRequest addValue:setCookie.value forHTTPHeaderField:setCookie.name]; 
     } 
    } 
    NSURLResponse *response = nil; 
    NSError * error = nil; 
    NSData *responseData = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:&error]; 

Dopo aver fatto ciò, ho potuto accedere correttamente a Facebook utilizzando Django-allauth.

1

ho incontrato un problema molto simile al tuo, nell'implementazione dell'accesso di Facebook da un'app per iOS a un server che esegue django-allauth. Ho notato nella risposta POST riuscita in iOS che il cookie sessionid non veniva salvato automaticamente come normalmente. Credo che questo sia il motivo per cui le tue chiamate successive vengono rifiutate e reindirizzate alla tua pagina principale.

Aggiungere la riga seguente sembrava risolverlo per me, ma ammetto che non ho una comprensione completa del perché funzioni. Qualcosa che ha a che fare con l'aggiornamento della chiave di sessione, forse. In ogni caso, dal momento che non vi erano altre risposte, pensato che questo potrebbe essere utile per provare:

user = User.objects.get(email=email) # Get User 
# Login the user from Django's perspective 
user.backend = 'django.contrib.auth.backends.ModelBackend' 
auth_login(request,user) 
request.session.cycle_key() #Refresh session key 

Poi, sul lato app iOS, a verificare se esiste un cookie di sessione:

NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:[NSURL URLWithString:WEB_APP_BASE_URL]]; 
for (NSHTTPCookie *cookie in cookies) 
{ 
    if ([cookie.name isEqualToString:@"sessionid"]) { 
     NSLog(@"found session cookie: %@",cookie.value); 
    } 
} 
+0

Grazie per il suggerimento, ho apportato la modifica (cycle_key()), ma non sono ancora considerato "connesso" in Django 1.6. Altre idee? – PhilBot

+0

Nella mia app iOS, controllo se il cookie di sessione è stato rinviato dal server (che viene poi automaticamente utilizzato dalla tua app su richieste successive al server). Puoi provare a farlo per aiutarti a eseguire il debug. – weiy

Problemi correlati