The Artima Developer Community
Sponsored Link

Ruby Buzz Forum
Using Aleph's Asynchronous HTTP Client for OAuth

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
john hume

Posts: 82
Nickname: duelnmrkrs
Registered: Oct, 2005

John Hume is a developer and consultant with ThoughtWorks.
Using Aleph's Asynchronous HTTP Client for OAuth Posted: Nov 14, 2012 4:00 PM
Reply to this message Reply

This post originated from an RSS feed registered with Ruby Buzz by john hume.
Original Post: Using Aleph's Asynchronous HTTP Client for OAuth
Feed Title: El Humidor
Feed URL:
Feed Description: John D. Hume on Ruby and development with agile teams.
Latest Ruby Buzz Posts
Latest Ruby Buzz Posts by john hume
Latest Posts From El Humidor


If you're implementing authentication in your web app on the server-side using some third party's OAuth, you ideally want to dedicate minimal system resources to waiting for HTTP responses. If you're working in Clojure, you can do this using Aleph's asynchronous http client. Here I'll walk through using it to do server-side authentication with Facebook.

[There's nothing auth-specific about this. Any integration with HTTP end points might benefit from similar treatment.]

Here's what our application needs to do:

  1. Send the user to Facebook's OAuth "dialog," passing along a redirect_uri parameter. On the dialog they'll see what permissions your app wants and click a button to say they want to log in. Facebook will redirect them back to your redirect_uri with a code parameter in the query string.
  2. Make a server-side request for an access-token for the user, passing along the code parameter and your own redirect_uri
  3. Make a second server-side request for whatever information the app needs (e.g., the user's name and email address), passing along the access-token.

We can't do anything for the user between steps two and three, so we'll perform them one after another. It's easy to picture what that might look like if we aren't worried about tying up a thread waiting for responses.

Ideally we'd like an asynchronous implementation to read just as clearly.

We'll start with the easiest implementation to understand, which uses on-realized to register success and error callbacks on each HTTP request. The fn now takes success and error callbacks. In an aleph web application, those would each enqueue a ring-style response map onto the response channel.

Unfortunately this reads terribly due to the nested callbacks.

We can use lamina's run-pipeline macro to create a cleaner version of the same thing.

At this point, however, we realize that we're missing something. In addition to the user data we get from the second HTTP request, we want the "fb-user" passed to the success callback to have the access-token and access-token-expiration we got in response to the first HTTP request.

We can achieve that by breaking the pipeline into two and holding onto the result of the first so that we can refer to it again later (in the call to combine-results.

Note the deref ("@") in that second reference to access-token-result. It's needed because the pipeline returns a result-channel which may not yet be realized. We don't have to worry about the deref blocking, since the value is guaranteed to be realized before the second pipeline will proceed past the initial value, but it looks like something that might block. The whole thing's also a bit sloppy. Zack Tellman pointed out that it's a bit cleaner to split them like so.

The deref is no longer needed because the nested fn will be called with the realized value once it's ready.

If we extract the details so that we have the same level of abstraction we had in the initial (synchronous) version, we end up with this.

Comparing that last fn to the synchronous version, we come out looking pretty good here! The only thing I find a little awkward is that the options to run-pipeline are the second argument. It breaks up the flow when the first "value" is just a call to another pipeline.

Assorted Thoughts

Reusing the connection

Aleph's http-client fn lets you reuse one connection for multiple requests to the same host. It mucks things up a bit, since you want to be sure the connection gets closed at the end.

Not being able to just wrap a (try ... (finally ...)) around the whole thing is a bummer, but it's still not awful.

The :error-handler

The empty :error-handler fns on the "get-fb-" fns suppress "unhandled exception" logging from lamina. Since both of those pipelines are run as part of the outer pipeline, when either of them errors, the error-handler from the outer pipeline will be run, so that's the only place we need to use the error-callback. In my real app, those inner error-handlers just log, but you could just as easily leave the options out completely if you're ok with lamina's logging.

The rest

I've left out many details, like the FB-specific URL-templates and response parsing. If there's interest, I'm happy to share those. I just thought the lamina/aleph stuff was the interesting part.

Read: Using Aleph's Asynchronous HTTP Client for OAuth

Topic: Ruby Programming 38th Batch: Registrations now open Previous Topic   Next Topic Topic: Clojure: Converting scenarios With Interleaved expect Calls To Bare expectations

Sponsored Links


Copyright © 1996-2018 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use