Integrating Twitter Authentication with Rails

I’m working on a project called Piggy Back, a simple service where friends can keep track of debts between one another. To encourage user registration, I’m lowering the barriers, allowing users to sign up their Twitter and Facebook accounts. I’d like share with you how I got this functionality into this Rails app. A shout out to Chris Powers and Jeff Talbot for their feedback along the way.

Eliminating Plugin Magic

I needed to get my hands into the authentication code if I was going to allow Twitter and Facebook integration. I was using Authlogic and could have extended it with other plugins to give me this support, but I wanted the process for my users to be as seamless as possible. I knew this would probably involve some Piggy Back specific customizations.

Furthermore, I was actually only using about 20% of the functionality Authlogic provides. I had “magic columns” (last_logged_in_at, failed_login_count, etc.) because they were free, but I didn’t actually need them.

Finally, making it possible for users to sign up with their Twitter accounts meant a user record with no email or password, fields Authlogic required. There were likely workarounds available, but that meant hacking Authlogic, not working on my app.

Hand Rolling Authentication

Authentication isn’t complicated. I liberated the heavy lifting cryptography algorithms from Authlogic. Obviously, it was important for me to use the exact same algorithm in my own system. Then I got inspired with Jamis Buck’s Bucketwise project and prepped my user to be authenticated.

See the code!

Authlogic encourages you to use a UserSession model which lends itself to restful design, but I found it much easier to just create two actions on my SiteController that simply responded to GET requests. Originally, I had used require_authentication as a before_filter, so I ported those over to the new system as well. Check it all out in the gist.

Now, Let’s Get Twitter Up in There!

Twitter supports OAuth authentication. It’s a little trickier to implement than basic HTTP authentication, but isn’t terribly complicated. Your application, the consumer, gets a couple tokens, which you exchange for request tokens. Then you use the request tokens to do what you will with Twitter, including obtaining access tokens to, well, access users accounts.

There’s a lot that OAuth does and I wasn’t prepared to start from scratch, so I installed the oauth Ruby gem and included it into my application. I also added 4 columns to my users table to store the Twitter information, twitter_id, twitter_handle, twitter_token, and twitter_secret. My user validation rules adapted to this change to, permitting users without emails or passwords in favor of these attributes.

You need two actions to support Twitter OAuth. One to start the request and the other, known as the callback, to finish it. In examples, these actions are nestled in among other controllers, but I decided for clarity to create a standalone TwitterController.

It’s a bit too large to embed here, but you can still take a look at the TwitterController.

The trickiest part was actually making the workflow seamless for all the users. I’m referencing comments in the TwitterController:

  1. A new users comes to Piggy Back and creates an account by logging in to Twitter. (C)
  2. An existing user comes back to Piggy Back and logs in via Twitter, but:
  3. Piggy Back doesn’t now about their Twitter account and the two must be linked (B) or
  4. Piggy Back knows about their Twitter account and logs them in (A).

To get this split behavior, I simply altered the callback URL that Twitter uses to return flow back to the application. This was simple enough with two named routes:

map.with_options({ :user_action => 'create' }) do |twitter_map|
  twitter_map.twitter_oauth '/twitter/oath/:user_action', { :controller => :twitter, :action => :oauth }
  twitter_map.twitter_callback '/twitter/callback/:user_action', { :controller => :twitter, :action => :callback }
end

To handle the case where accounts may be linked, I store the users Twitter attributes in session for a moment, while I redirect to my SiteController#login action. Here, the user is asked to login specifically to link their account to Twitter. Jeff Talbot offered great feedback about this workflow, suggesting that anywhere there’s a “Sign in with Twitter” link, it should just seamless create a users account. This creates a problem for existing users, however, where they could easily end up with two accounts. To solve this, I’m adding a big button to this login page, “Don’t Have an Account, Create One Instantly.”

Conclusion

So many web apps need to do exactly what I spent Monday doing, so hopefully you can reuse some of the strategies I implemented here.

Chris Powers asked if I thought this could be pulled out into a plugin, and I’m sure the answer is yes. However, it’s not a lot of code and there’s a lot of customizations your app will need within the areas I commented out. I’m not a control freak (I did, after all, convert from C# and ASP.net), but I do believe there is value in controlling your apps code.

References

Thanks for reading! I'm Avand.

I’ve been working on the web for over a decade and am passionate about building great products.

I devote most of my time to building Lopery, a free budgeting that helps people spend with confidence, recover from the unexpected, and achieve financial independence. I used simple (but time consuming) budgeting principles to buy my first home. Now, I'm codifying (pun intended) those same principles into an easy to use app that helps people achieve their financial goals.

My last job was with Airbnb, where I focused on internal products that helped teams measure the quality of the software they were building. I also built internal tools for employees to stay more connected, especially after the COVID-19 pandemic. Before that, I was lead engineer at Mystery Science, the #1 way in which science is now taught in U.S. elementary school classroms. For a while, I also taught with General Assembly, teaching aspiring developers the basics of front-end web development.

I was born in Boston, grew up in Salt Lake City, and spent many years living in Chicago. In 2013, I came out West to San Francisco, which I called home for almost a decade. Now, I’m based out Mariposa, in the foothills of the Sierras.

I enjoy the great outdoors and absolutely love music and dance. Cars have been an lifelong obsession of mine. I’m the proud owner of a 2002 E-250 Sportsmobile van, and he and I have enjoyed many trips to beautiful and remote parts of the Pacific North West spreading good vibes. I also have a very soft spot for magic (slight of hand, in particular). I love the feeling of being inspired and absolutely love inspiring others.

What can I do for you?

Read my other posts or get in touch: