5

I've asked a similar question before, but I've done some more research and this iteration should be a bit different. It seems as though several SO users have had an issue with registering and logging in users in a single view and it hasn't really been answered.

The issue is that I register, authenticate, and login a user in a single Django view. For most users that's fine, but for other users, their subsequent request (they click a link on my site) returns an Anonymous User. Somehow, the logged in user loses their session and is redirected to a page on my sit ethat doesn't require authentication.

When they then log in via a pure login view (as opposed to the register + login view), the session data stays in tact. The issue really seems to be registering and logging in a single view.

See this post for the same issue: https://stackoverflow.com/questions/1693726/problem-with-combined-authentication-login-view.

It has been suggested that this is potentially a threading issue. I've also seen it suggested that it relates to the backend for caching session data.

Any thoughts on what it really relates to? I can't reproduce the error, which is really holding me back.

EDIT--I should note that I'm using the default database backed sessions.

Here is my register/login view

def splash_register(request):
  if request.session.get('beta'):

    if request.method=='POST':
        userform=MyUserCreationForm(request.POST)
        if userform.is_valid():
            #username of <30 char is required by Django User model.  I'm storing username as a hash of user email 

            user=userform.save(commit=False)
            user.username=hash(user.email)
            user.save()



            username=user.username
            password=str(userform.cleaned_data['password'])
            user=auth.authenticate(username=username, password=password)
            if user is not None:
                auth.login(request,user)
                request.session['first_visit']=True
                return HttpResponseRedirect("/")
            else:
                return HttpResponseRedirect('/splash/register/')
        else:
            userform=MyUserCreationForm(request.POST)
            return render_to_response("website/splash_register.html", {'userform':userform}, context_instance=RequestContext(request))
    return render_to_response("website/splash_register.html", context_instance=RequestContext(request))     
else:
    return HttpResponseRedirect('/splash/')        
Cœur
  • 37,241
  • 25
  • 195
  • 267
Ben
  • 15,010
  • 11
  • 58
  • 90
  • Are you using mod python like the related post? When I started into learning python I was heavily steered into mod_wsgi. I can't say that this will fix any issues though. – James Khoury Apr 27 '11 at 05:31
  • Hi James, I'm using mod_wsgi. – Ben Apr 27 '11 at 12:55
  • I'm begging somebody to help me out here... – Ben Apr 29 '11 at 02:28
  • @brendan I think the major problem is that you mentioned you can't replicate it? Are you sure you can fix it then? – James Khoury Apr 29 '11 at 03:58
  • Maybe solving it outright is difficult at this stage, but I'm hoping for some sort of general starting point. Right now, I'm thinking it's either a problem with my server config (threading), my session backend (I'm just using default database right now to store session data), or something related to that 'beta' session variable (though I don't get that). Any leads from there would be super helpful. – Ben Apr 29 '11 at 06:01
  • Quick question, why are you using `str(userform.cleaned_data['password'])` instead of just `userform.cleaned_data['password']`? If you were running into problems earlier without the `str` function that suggests another problem going on. – Jordan Reiter Apr 29 '11 at 20:30
  • Interesting, I'm not sure why I used the string function there. I removed it without any issues – Ben Apr 30 '11 at 16:15
  • I added a comment below about my mod_wsgi set up. Maybe that could help... – Ben Apr 30 '11 at 18:00

3 Answers3

19

You don't have to use authenticate and, in this scenario, it's not really needed. All you need to do is set the backend of the user record.

So something like this would work:

def splash_register(request):
  if request.session.get('beta'):

    if request.method=='POST':
        userform=MyUserCreationForm(request.POST)
        if userform.is_valid():
            #username of <30 char is required by Django User model.  I'm storing username as a hash of user email 

            user=userform.save(commit=False)
            user.username=hash(user.email)
            user.backend='django.contrib.auth.backends.ModelBackend'
            user.save()


            username=user.username
            password=str(userform.cleaned_data['password'])
            auth.login(request, user)
            request.session['first_visit']=True
            return HttpResponseRedirect("/")
        else:
            userform=MyUserCreationForm(request.POST)
            return render_to_response("website/splash_register.html", {'userform':userform}, context_instance=RequestContext(request))
    return render_to_response("website/splash_register.html", context_instance=RequestContext(request))     
else:
    return HttpResponseRedirect('/splash/')

Update

I mentioned this in a comment, but in terms of an "answer" the solution is to add this to your settings file:

SESSION_COOKIE_DOMAIN = 'yourdomain.com'

This will allow users coming in from www.yourdomain.com or yourdomain.com to log in to the website.

Jordan Reiter
  • 20,467
  • 11
  • 95
  • 161
  • Hi Jordan, that was a useful piece of info regarding setting the backend. I appreciate that. I'm not sure if it will fix the bug. Would it spawn one less thread or some other meaningful impact? – Ben Apr 30 '11 at 16:10
  • Here is some more info. I'm running mod_wsgi with apache2. Mod_wsgi configuration is as follows: SERVER MPM: Preform, threaded:no, forked, yes(variable process count). In my log files, I keep raising a KeyError in the module threading, which is ignored. I've looked online and it seems that this is probably an innocuous thing and not cause the bigger problem, but I wanted to put it out there. – Ben Apr 30 '11 at 17:57
  • thanks for pointing out the SESSION_COOKIE_DOMAIN settings, that solved my problem! – joshcartme Aug 30 '11 at 21:08
3

Oh man, I am both incredibly relieved and also full of self-loathing. I had no idea that that the Cookies weren't transferable between www and non-www domain names.

A set of my users were coming to www and then were redirected to non-www, killing their session. I'm setting up mod_rewrite now to resolve the situation.

Ben
  • 15,010
  • 11
  • 58
  • 90
  • 1
    If that was the cause of your problem, you want to set SESSION_COOKIE_DOMAIN to `.yourdomain.com`. See http://docs.djangoproject.com/en/dev/ref/settings/?from=olddocs#session-cookie-domain – Jordan Reiter May 01 '11 at 12:59
  • Thanks Jordan for this and all the rest of your help. What is the etiquette for assigning a bounty in this case? I genuinely answered my own question after days of confusion. Any clue? – Ben May 01 '11 at 15:31
  • Technically your answer is not entirely correct, since cookies *are* transferrable between www and non-www domains. And I'm biased, of course, but considering that the ultimate cause was a loss of session data because the cookie was not tied to the domain, and that my updated answer (and comment) resolves this specific problem, I'd say you can assign it to me if you feel that going along this path is the best solution to your problem (I certainly think it is!) – Jordan Reiter May 01 '11 at 16:40
  • Ok, that seems fair to me. Thanks Jordan, you've helped me out a ton. – Ben May 02 '11 at 13:26
1

Just incase this helps anybody else, I had this problem where in the current view, request.user.is_authenticated() is True, but after a HttpResponseRedirect to another page, same host, request.user became anonymous. I am using sessions, but turns out it wasn't the session. I did my own custom authentication backend, and the 1.2 docs say you must implement get_user(self, user_id) but I didn't think user_id (primary key) was anything special so I implemented it as get_user(self, username) .. but apparently that was the source of the problem!

Jay
  • 594
  • 10
  • 23