views:

348

answers:

2

I'm allowing users to login to my site with either OpenID, Twitter OAuth or FBConnect. If the user attempts to go to a page that requires them to be logged in, after that user logs in I want to send them BACK to that page. Is there an easy way to accomplish this with all of these or should I simply just write the redirect page to a cookie and upon a successful login send them to that page? I'm using Django so if there are any nice tips or tricks involving that specifically that would be great.

Thanks for the input in advance!

A: 

Sadly OAuth and OpenID aren't really aware of your app states (while OAuth WRAP can be). So you have to take the following assumption:

  • The user will complete the sign-in WITHOUT switching tabs/windows or doing other requests on your site.

Then you can do the following:

  1. Once you detect the access of a protected site, store the full query in the session. This won't work at all if it's a POST request, you have to prepare for this problem (show them a warning site with a lik that they must login first).
  2. Store a timestamp of when this request happend.
  3. On your OpenID callback check whether the session variables are set and redirect the user to the stored query. Check the timestamp (don't redirect if the timestamp is older than 5 minutes or so). After that clear both variables from the session.

This will lead to odd behaviour if the user violates against the assumption, but I don't think there is any way you can circumvent that.

Marcel J.
I figured this was the only solution, that kind of sucks but oh well. Thanks for the input.
Jordan Messina
This isn't accurate. OAuth WRAP is no more state-aware than OAuth or OpenID. All three have a concept of a callback URL (WRAP has a state parameter too), and the callback URL can be used to save state.
Andrew Arnott
Indeed - OpenID should work, however most websites (including Twitter) require fixed OAuth callback URLs. WRAP allows fixed callback URLs while adding a state param.
Marcel J.
+1  A: 

You could thread that parameter (the page they were at) through as a parameter to your return_to. As noted in the spec:

Note: The return_to URL MAY be used as a mechanism for the Relying Party to attach context about the authentication request to the authentication response. This document does not define a mechanism by which the RP can ensure that query parameters are not modified by outside parties; such a mechanism can be defined by the RP itself.

For example:

def sendOpenIDCheck(...):
    # after getting an AuthRequest from Consumer.begin
    return_to = oidutil.appendArgs(return_to,
        {'destination_url': that_place_they_tried_to_go})
    return redirect(auth_request.redirectURL, realm, return_to))


def handleReturnTo(request):
   # after doing Consumer.complete and receiving a SuccessResponse:

   return redirect(request.GET['destination_url'])

If there's some other state you need to track (like POST data), or you have an extraordinarily long URL that you can't fit in as a query parameter, or you need to have the destination_url tampered with by the user, you store that information server-side, send the key as a query parameter instead of a URL, and look it up when they get back.

Not very different from storing it in the session, unless the user's got several simultaneous tabs in one session that run in to this, and then having it in the query helps.

keturn
The callback URL is the best place to put this information, as keturn says. That way, the login experience itself is tracking where the user wants to go, so that session state doesn't carry into the wrong auth attempt and take the user to a place he doesn't want to go.
Andrew Arnott