How does L2T "block the auth flow until it's complete"?

Aug 7, 2009 at 12:47 AM

I've been struggling to understand how to wait for the user to complete the web-based authorization of an OAuth app.

The code in L2T is using the same Process.Start() call that others use to invoke a URL in the user's default browser, but in my own experience, lacking some "wrapper" function that waits for the user to interact successfully with the page, the application code will merrily fire off that web request and immediately invoke the next line of code.

However, L2T's code includes a comment that indicates they've come up with a strategy to wait until the user has authorized the application (found in the DesktopOAuthAuthorization class, ahead of the Authorize() implementation):

// Invokes the entire authorization flow and blocks until it is complete.

I've tried to follow the code paths that might have implemented the magic, but I'm stumped.  Can anyone help me understand what I've missed - i.e. where the L2T code prevents the calling application from invoking an "authorization-required request" before the user has successfully authorized the app?  Here's what I've thought of (right or wrong) so far:

  • DesktopOAuthAuthorization.Authorize() calls Process.Start() and then next calls return this.CompleteAuthorize()
  • does CompleteAuthorize() have some sort of internal looping/waiting function that ensures the user's permanent Authorization has happened, before completing the call?
  • CompleteAuthorize() calls this.Consumer.ProcessUserAuthorization() passing in both the requestToken and the GetVerifier - I keep getting confused: is the requestToken the one that provides the app with the permanent Authorization?  So if the user didn't authorize the app via the browser, would the requestToken be null, and invoke some internal logic in ProcessUserAuthorization()?
  • from what I'm seeing, ProcessUserAuthorization() is included via the DotNetOpenAuth assembly
  • sounds like the OAuth site will respond with an accessToken if (a) the request includes a valid requestToken and (b) the user has already authorized the application
  • Lutz' Reflector (I don't have the DNOA source code right now) indicates that there's checking to see if the requestToken is non-null, but there's no deeper logic than that
  • Looking back through the code again, I'm now seeing that Process.Start() isn't just sending a static Uri, but is invoking it through the this.BeginAuthorize() method - which I've never seen done before (and which only publicizes my ignorance of complex coding practices, rather than implying that the L2T code is doing something 'wrong')
  • while I don't understand the subtleties here, it's clear that RequestUserAuthorization() is being used, and the final parameter in the call seems to be sending "out" the requestToken --- perhaps *there* is where the implicit logic ensures the user has authorized the app through the invoked web page?
  • this function's documentation indicates it returns "The URL to open a browser window to allow the user to provide authorization" - but if the requestToken were returned from the URL, then how could it be handled as a parameter inside this function (even if it's just an 'out' parameter)?

Man, I'm so in over my head - can anyone throw me a lifejacket?  Thanks for any thoughts you can share.

Aug 7, 2009 at 1:42 PM

Hi there,

I'm not sure where to start as you've asked a lot of questions. :)  

The "magic" in blocking until authorization completes is that the user must supply the verifier code before authorization can complete.  The GetVerifier parameter that you noticed is a callback delegate that must prompt the user for that verifier code.  So, the Process.Start opens the browser, and then immediately calls the GetVerifier callback delegate.  Here is the secret: LinqToTwitter doesn't block.  You do.  That is, the GetVerifier method must block while waiting for the user to supply the verifier code, which cannot happen until the user sees the verifier code in the browser.  Once GetVerifier returns, the flow immediately proceeds to exchange the request token for an access token and return to the caller (you).  Does that make sense?

Now regarding the parameter passed to Process.Start not being a static Uri, but the result of the BeginAuthorize() method.  Well, I've never seen a static URI being passed in.  You almost always want to pass in a uri that's been crafted to include the request token so the user has as seemless an experience as possible.  BeginAuthorize internally obtains a request token from Twitter, and then tacks that onto the end of a URI and returns that URI.  Not all that complex.But DotNetOpenAuth is designed to hide as many complexities of OAuth as possible, so the idea is you just call a simple method to get the URI and and then invoke the browser.

Are ya swimming yet? :)

Aug 7, 2009 at 6:34 PM

AArnott, sorry if I posed too many questions - I was recording my stream of consciousness, but was hoping they'd merely illustrate what was going through my mind (and not 'demand' complete answers).

Seems like you honed in on the root problem I was struggling with, and I'm thrilled to hear how 'simple' the answer is.  I've never had occasion to understand delegates before, so I totally missed that subtlety of the code here.  Hopefully I'll be able to crash-course myself on this and appreciate either (a) why we need to convert over to DotNetOpenAuth immediately or (b) how to take advantage of some of the same techniques without a ton of trouble.

I really appreciate your taking the time to educate me (and the rest of the readership) on the mechanics of these implementations.  Sure isn't part of your day job, nor core to the development of this project, so please know that your extra special efforts are recognized.  Thank you.

Coordinator
Aug 7, 2009 at 7:53 PM
MIkesl wrote:

I've never had occasion to understand delegates before, so I totally missed that subtlety of the code here.  Hopefully I'll be able to crash-course myself on this and appreciate either (a) why we need to convert over to DotNetOpenAuth immediately or (b) how to take advantage of some of the same techniques without a ton of trouble.

Hi MIkesl,

Delegates... You can start here:

http://www.csharp-station.com/Tutorials/Lesson14.aspx

and then dig deeper here:

http://www.devsource.com/c/a/Languages/Working-with-Delegates-in-C/

Joe

Aug 7, 2009 at 8:04 PM
MIkesl wrote:

AArnott, sorry if I posed too many questions - I was recording my stream of consciousness, but was hoping they'd merely illustrate what was going through my mind (and not 'demand' complete answers).

No worries at all.  I didn't mind lots of questions as long as you found answers to them in my response. :)

MIkesl wrote:

Hopefully I'll be able to crash-course myself on this and appreciate either (a) why we need to convert over to DotNetOpenAuth immediately or (b) how to take advantage of some of the same techniques without a ton of trouble.

I'm particularly curious what you're wondering about here.  When you say "we need to convert over to DotNetOpenAuth", I take it you're a LinqToTwitter customer who has been using it since before it used DotNetOpenAuth.  Is that right?  Just wondering where you're converting from if you're converting to DotNetOpenAuth.  Anyway, I can give you some ideas.  If you've been watching the Twitter+OAuth community on Twitter, you may be aware of the huge turmoil most Twitter OAuth clients were in last week due to some security fixes Twitter made.  LinqToTwitter was completely unaffected.  This is because DotNetOpenAuth (in my biased opinion) is a quality library where great care is taken to maximize both convenience and security.  So although Twitter had a bug that didn't check signatures for a while, DNOA was sending the correct signatures in the first place, so when Twitter fixed their bug, DNOA kept on working whereas others only did "just enough" to get it to work and therefore broke when Twitter raised the requirements.

When you say "same techniques without a ton of trouble", what trouble are you referring to?  Is using DotNetOpenAuth what you're referring to here?  (Please understand, I am not offended, but eager to find out ways to improve the library)  If so, please tell me why using DNOA is a ton of trouble.  On the flip side, I suggest that writing your own miniature OAuth library to include with a Twitter client is not worth the risk. OpenID, OAuth, and other security-sensitive features justify a dedicated library that has very careful attention paid to security for its own sake, separated from the concerns of consumer libraries, IMO.  For my thoughts on the extra library dependency, see http://blog.nerdbank.net/2008/04/argument-for-extra-dependency-of.html