2014-04-09 2 views
11

Ich benutze das Facebook iOS SDK um den Facebook Access Token an meinen Django Server URI zu senden. Die entsprechende Funktion views.py wird unten gezeigt und ich bekomme einen 200 Response Code, wenn ich den POST von iOS mache. Allerdings habe ich einen zweiten @login_required dekorierten URI, den ich vom iOS-Gerät unmittelbar nach dem Anruf anrufe, der mich nicht eingeloggt betrachtet und mich auf meine Hauptseite weiterleitet. Was mache ich falsch? Wie bleibe ich nach meinem POST von iOS angemeldet?django-allauth einloggen mit Facebook Token von iOS Gerät

# 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, danke für die Kommentare. So poste ich jetzt auch die Facebook-E-Mail-Adresse und erhalte den Benutzer und logge sie manuell ein. Nachfolgende Anforderungen STILL werden jedoch nicht authentifiziert. Also der @login_required Decorator schlägt immer noch fehl .. Irgendwelche anderen Ideen?

# 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 

==== Ein weiteres Update ==========

Basierend auf der zweite Antwort in diesem Beitrag: django authentication without a password

ich einen benutzerdefinierten Login-Backend erstellt, das tut kein Passwort erforderlich. Die dritte Antwort in diesem Beitrag erläutert, wie dies geschieht:

speichert nicht die Anmeldebestätigung in der Sitzung. Also habe ich versucht, ein benutzerdefiniertes Backend zu verwenden.

Hier ist meine modifizierte Code:

# 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 

Mit hurl.it ich diesen HTTP 200 Antwort erhalten, aber ich bin immer noch nicht vom iPhone angemeldet als:

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

Haben Sie versucht, es im Browser zu tun? Tritt das Problem erneut auf, wenn Sie URLs vom Browser aus aufrufen? Sind Cookies im iOS SDK aktiviert? –

+0

Ich glaube nicht, dass Sie @login_required verwenden können. Das nächste Mal, wenn Sie eine Django-Ansicht aufrufen, weiß django nicht, wer der Benutzer ist, da der SDK die Cookie-Informationen nicht weitergibt. Eine Lösung besteht darin, das Token für jeden Aufruf an die Sicht weiterzuleiten und den Benutzer mit einem Benutzernamen anzumelden Custom Decorator.Bitte beachten Sie, dass es nicht der beste Weg nach vorne ist. –

+0

Ich habe nicht viel Information über 'allauth'. Ich weiß jedoch, dass Django 'login_required' etwas wie 'if request.user.is_authorized() == True' tun wird. Ich sehe nichts ändert den Benutzer Autorisierungsstatus, es sei denn allauth tut es für Sie. Also was ich vorschlage ist, den Status deines 'django.auth' Benutzers zu ändern und is_authorized = True zu machen. Stellen Sie sicher, dass die 'Sitzung' erstellt wurde. Wenn der Benutzer in eine andere Ansicht wechselt, kann Django den Status der Benutzerauthentifizierung überprüfen. Lassen Sie uns wissen, was nach diesem Prozess angezeigt wird, damit wir Ihnen helfen können. – Othman

Antwort

0

Danke für die Hilfe und den Input - ich habe es endlich gelöst. Ich kenne nicht die genaue Ursache dafür, warum die Anmeldung mit Facebook die Cookies durchgebrannt hat und der Standard-Login funktioniert. Ich habe bemerkt, dass die Domain der Cookies, die vom Facebook Login zurückgegeben wurden, mit einem führenden "."Wie folgt aus:

[ .domain.com ] 

Während die Standard-Login, die gearbeitet hatte Cookie-Domains wie folgt aus:

[ www.domain.com ] 

ich die Cookies von der HTTP-Antwort analysiert nach erfolgreicher Anmeldung mit Facebook und sie in der Singleton gespeichert :

   // 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); 
       } 

ich habe dann, diese Cookies explizit für die folgende Anfrage:

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

Danach konnte ich erfolgreich mit Facebook mit Django-allauth einloggen.

1

Ich betreibe haben in ein sehr ähnliches Problem wie das Ihre bei der Implementierung von Facebook Login von einer iOS App auf einen Server, auf dem django-allauth läuft. Bei der erfolgreichen POST-Antwort in iOS ist mir aufgefallen, dass der Sitzungs-Cookie nicht wie gewohnt gespeichert wurde. Ich glaube, das ist der Grund, warum Ihre nachfolgenden Anrufe abgelehnt und auf Ihre Hauptseite weitergeleitet werden.

Hinzufügen der folgenden Zeile schien es für mich zu lösen, aber ich gebe zu, dass ich nicht vollständig verstehe, warum es funktioniert. Etwas, das vielleicht mit dem Aktualisieren des Sitzungsschlüssels zu tun hat. Wie auch immer, da es keine anderen Antworten waren, fanden die hilfreich sein könnten, damit Sie versuchen:

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 

Dann wird auf der iOS-App Seite, überprüfe ich, ob ein Session-Cookie vorhanden ist:

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

Danke für den Vorschlag, ich habe die Änderung vorgenommen (cycle_key()), aber ich werde in Django 1.6 immer noch nicht als 'eingeloggt' betrachtet. Irgendwelche anderen Ideen? – PhilBot

+0

In meiner iOS App überprüfe ich, ob der Sitzungscookie vom Server zurückgeschickt wurde (dieser wird dann von Ihrer App bei nachfolgenden Anfragen an den Server automatisch verwendet). Sie können dies versuchen, um Ihnen beim Debuggen zu helfen. – weiy