Handling user explicit Deny action

Jan 23, 2011 at 12:36 PM

Hi!

I noticed that there's no built-in functionality for handling situation where user has denied (clicked "Deny") application access. The MVC demo app asks for access every time if user clicks "Deny". I know that it is a desired behavior in this demo, as there's only a check to see if user.IsAuthorized is true. There should also be a value stating that user explicitely denied access (because eg. doesn't have a Twitter account). Am I missing something or there's no way (other than looking into QueryString) of checking explicit Deny request?

Basically I'm building an application where users are allowed to voluntarily allow my application access their accounts. I want to do this automatically after logon, so every user is asked for permission (even if he doesn't have a Twitter account). Application is storing neccesary data to send messages in the future through users Twitter account. Many users don't have a Twitter account so in such situation I want to fall back to other means of communication.

Cheers, Piotr

Jan 23, 2011 at 8:17 PM

Hi Joe,

Yes, I watched the whole conversation in Fiddler and noticed how it works. When user clicks Deny, Twitter displays a page with a link back to the calling site - as you said, there's no automatic callback. The link on this page has ?denied=<some hashed string> appended to it (don't know what is it for).

I was wondering about handling this this way:

  1. Have 2 pages (or in MVC terminology - 2 actions, can be in the same or different controllers)
  2. Page 1 checks user status and if not authenticated - redirects to Twitter with callback url to Page 2
  3. Page 2 only checks status and does something basing on allow/deny status (no redirection at this point). This can be handled by an ActionFilter which checks if the request comes from Twitter (to make this action callable only as a callback), checks authentication status and does something based on it (redirect, exception, whatever). Of course status can be checked in the controller instead if you'd want to make some more complex action, which shouldn't be done in a filter.
  4. It would be perfect if we could by some means redirect user from Page 2 to Page 1 only if authenticated (this would need some ReturnUrl param passed with callback URL in point 2.). In this case the Mvc example you provided would work without problems.

I was dealing lately with CAS server integration, so I understand your point about trying to handle some kind of state in such case to be tricky;) I had to use some hacks to make it work...

Cheers, Piotr

Coordinator
Jan 23, 2011 at 8:53 PM

Hi Piotr,

I'm glad you brought this up because it's something I've thought about, but am waiting for feedback to see what other people, such as yourself think and if you have good ideas.  First, I want to discuss Twitter's implementation of the OAuth protocol and then how it affects us using LINQ To Twitter (L2T).  After getting a request token (something you don't see unless you're watching with Fiddler or some other HTTP sniffing tool), L2T redirects the user to the authorization page.  Essentially, this redirection is a fire and forget operation and your application doesn't wait on a response.  L2T is using a custom action result, MvcOAuthActionResult, to perform this redirection.  The only continuation of the OAuth protocol for your application is when the user clicks the Allow button; then Twitter performs a redirect to the same page that made the original request.  Twitter never sends back a response or redirects back to your application if the user clicked the Deny button.

Right now, the browser leaves your application, relying on Twitter to bring the user back, which won't happen in the Deny scenario.  In the past, I've handled strange login scenarios, where I wanted the user to stay on the original site, by launching the authentication in a separate window with javascript.  In this situation, you might also consider setting a timer and do a VerifyCredentials call (see demo code) and capturing the exception to see if you get an exception back with 401 Unauthorized.  It's all very tricky code and somewhat of a hack, but I mention it anyway in case it's helpful.

One exception to this situation is that the Twitter puts a link back to your site on the Deny landing page.  If the user clicks that link, Twitter will redirect them back to your page and I would be able to indicate whether the user denied your site.  Handling this situation would require an enhancement of the authorizer that allowed you to associate your user ID with the OAuth token and then add an IsDenied to the authorizer that would compare OAuth tokens. A quick hack might be to modify the MvcOAuthActionResult so that it saves the OAuth token into session and then check Request.QueryString["denied"] and compare it to the Session value. Regardless, this scenario depends on the user clicking the link to return to your site and any scenario that depends on the user doing something on a site that you don't control could be problematic. The following link describes why Twitter took this approach:

http://code.google.com/p/twitter-api/issues/detail?id=545

If you or anyone else has ideas on how I can improve this situation, let me know.

Joe