0

So I'm using Rdio to login and create users, and wrote a backend to handle its oauth. The first time you try to sign in using Rdio, it creates a user and an attached Rdio user, but it doesn't create a session and return the session cookie.

The flow is like any oauth2 flow: you press a button on my app, it redirects w/ get params to Rdio, and Rdio calls a callback view on my app (along with a code in the GET params). In that callback view, I call authenticate:

class RdioCallbackView(View):
def get(self, request):
    """ here, you need to create and auth a django user and create and tie the rdio user's stuff to it """
    if request.user.is_authenticated() == False:
        try:
            rdio_code = request.GET['code']
        except KeyError:
            return redirect(reverse('login'))
        # authenticate
        user = auth.authenticate(rdio_code=rdio_code)
        if user is not None and user.is_active:
            auth.login(request, user)
        else:
            return render(request, 'home/login.html', {'rdio_url': create_rdio_auth_url(), 'message': "That code didn't seem to work"})
    else:
        # user exists!
        user = request.user
    return HttpResponseRedirect(reverse('the-next-view'))

The custom auth backend looks like this:

class RdioBackend(object):
def authenticate(self, rdio_code=None):
    token_info = exchange_rdio_code(rdio_code)
    try:
        access_token = token_info['access_token']
        refresh_token = token_info['refresh_token']
    except KeyError:
        return None
    except TypeError:
        # the code was probably already used.
        return None

    rdio_user_dict = get_rdio_user_for_access_token(access_token)
    rdio_key = rdio_user_dict['key']

    try:
        rdio_user = RdioUser.objects.get(rdio_id=rdio_key)
        rdio_user.access_token = access_token
        rdio_user.refresh_token = refresh_token
        rdio_user.save()
        user = rdio_user.user
    except RdioUser.DoesNotExist:
        user = User.objects.create(username=rdio_key)
        user.set_unusable_password()
        rdio_user = RdioUser.objects.create(
            rdio_id = rdio_key,
            access_token = access_token,
            refresh_token = token_info['refresh_token'],
            user = user,
            )
    return user

def get_user(self, user_id):
    try:
        return User.objects.get(pk=user_id)
    except User.DoesNotExist:
        return None

And that's where things get weird. It doesn't seem to make a new Session object, and definitely doesn't return a session cookie. However, when I go back and do the Rdio login again for a second time, it returns a session cookie, makes the session on the backend, and login and auth work perfectly.

And I think my AUTHENTICATION_BACKENDS settings is right:

AUTHENTICATION_BACKENDS = (
'appname.backend.RdioBackend',
'django.contrib.auth.backends.ModelBackend',
)

Edit: More possibly relevant info: The views that it's redirecting to have a LoginRequiredMixin:

class LoginRequiredMixin(object):
@classmethod
def as_view(cls, **initkwargs):
    view = super(LoginRequiredMixin, cls).as_view(**initkwargs)
    return login_required(view)

And in RdioCallbackView, when I change the final line from return HttpResponseRedirect(reverse('the-next-view')) to instead just serve the template directly with return render(request, 'path/to.html', param_dict), it does serve the cookie and make a sessionid, but then it deletes it from the DB and from the browser the moment I navigate away from that screen.

1 Answer 1

2

This might be the dumbest bug ever. It turns out that if you create a user without a password, you don't need to call user.set_unusable_password(). And if you do call user.set_unusable_password(), it somehow messes with any auth you do (even AFTER you call that).

So to fix this, I just got rid of the call to user.set_unusable_password() in my custom django auth backend.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.