Skip to main content

Sign In With Twitter using Omniauth and the Twitter gem

Episode 33 · November 20, 2014

Learn how to let your users authorize and connect to the Twitter API using their Twitter account with your Rails app

Gems Authentication


In this episode, we're going to talk about using OmniAuth Twitter to connect Twitter to a rails application and allow you to build a very simple Twitter client in your browser using rails. I've already scaffolded up this simple application, and we basically have a user and a body for each of these tweets, we're going to keep track of the user id in our application that will be connected to a Twitter account using OmniAuth, and then we'll also have the tweet body, so we'll cache those, and allow you to have them just saved in your application for quick access. The first things we need to do is visit Here and fill out the new application form. You want to give it a name and a description, the public website, and most importantly you want to specify this callback url here. What happens here is that when you log in with Twitter, it will send you back to the original application to test it out. That original application will then say: Ok, let's look at these credentials, it looks secure and correct so then your application can make sure that this person really did sign in with Twitter, and we can verify that they were correctly signed in. OmniAuth Twitter will provide this url here in your application off Twitter callback, and the is important because you can't use localhost in their configuration, so you can specify that instead of localhost, which is the exact same thing, and make sure that you specify the port that you'll be running your rails server on locally.

Take a look at the developer agreement, agree to that, and then create your Twitter application. Once you've created your Twitter app, you'll want to click over to the keys and access tokens tab, so that we can grab the consumer API key and the consumer secret the API secret. We're going to copy these and put them in our config/secrets.yml file. In here, I'm going to add my Twitter API key, and paste that in, and then the other one that we need to add is the Twitter API secret, and we can copy that from here. We'll save this, and this will make it accessible for our application when we configure OmniAuth. One quick security tip here is that you don't always want to keep your secret keys in your Git repository. If you ever make this open source, or you share this with other people, they'll be able to steal your keys potentially and that's not a good thing. One way to do this is to replace these with environment variables like you see here at the bottom, and you can configure that in your server or Heroku to set those as well, and the other thing that you'll probably want to do, is if you set them like this, you can hope into your terminal, and open up the .gitignore file, and here you can add config/secrets.yml, and then when you take a look at your git status you want to make sure that when you add the files in here like config, if you say: git add config, when you check out git status you should make sure that it doesn't add config/secrets.yml into your git repository.

Of course, we need to paste in here the OmniAuth Twitter gem into our Gemfile, and then we need to jump into our terminal and run bundle to install it. Coming back to our editor, we can open up the config/initializers/omniauth.rb file, and let's create that and paste in the middleware that OmniAuth injects into your application. OmniAuth provides this OmniAuth builder, that is told that rails needs to use that, and here we can configure it and say


Rails.application.config.middleware.use OmniAuth::Builder do 
  provider :twitter, Rails.application.secrets.twitter_api_key, Rails.application.secrets.twitter_api_secret

Let's take a look and see how much we've gotten working so far. I'm going to restart the rails server, and then in our browser, we can go to localhost:3000/auth/twitter. This will be OAuth redirecting us, and we get this "Authorize App" page, which tells us that we're authorizing the GoRails app on Twitter to allow us to read tweets and see who you follow. This doesn't give us access to be able to follow new people, update profile, post tweets or any of that stuff, and that's ok, unless you actually want to do that, and I think that's what we want to do in this case.

If we come back to our Twitter app and we click on the "Permissions" tab, you want to check the "Read and Write" access level for our application. When we update that and we go back to our application and go to auth/twitter. This time we should be redirected back and all of those update and write abilities have been added to our twitter account authorization page. Now, once we authorize the app, we'll be taken back to our rails app, and then the keys that will have the API keys for this user will be able to update your profile and post tweets for you. That's pretty cool, and we can now click "Authorize App", because we're going to get the API keys we want. This is taking us back to auth/twitter/callback which is what we wanted before, and we put that inside of our settings in our GoRails callback url. This has taken us to that same url and now that we have that, we have the OAuth token, the verifier, and we can build a callback here in our application to handle that.

Inside our config/routes.rb file, we actually need to set up a handler for when Twitter has successfully authenticated and sent us back to the application, so that we can take the response and save those API keys for the user so that we can use Twitter on their behalf, I'm going to paste in a little route here that's a GET route for the auth, and then it has a changeable provider in it, and then we're going to send that to the sessions create controller, which we'll create right now. I'm going to edit the app/controllers/sessions_controller.rb file, and paste in the example from OmniAuth README here. The first thing they do is they have this method that they have created on the user model that accepts the OmniAuth hash, and then we'll find or create that user, then, we'll set the current user in our application to that. This is the actual sign in part, and then we'll redirect you to the root path. I'm going to clean this up really quick, and we'll say: redirect_to root_path, let's actually talk about creating this user. We need to create the user model in our application, and we need to do a handful of things there, but at first, let's just raise an error here so that we can see what is going on in our response using the interactive console in the browser. Let's come back here, and we'll go back to our application, we'll redirect ourselves to Twitter again, we'll authorize the app, and when it comes back here, we get the exception raised as we expected, and we can play with this Auth hash that you see here. This is pretty cool, we can open this up and you'll see that we have an OmniAuth hash, it has credentials so we have the user's token and secret, this means that if we use these and present them to the Twitter gem, we can use Twitter on behalf of that user we authenticated as. There's all kind of other information here, we've got a lot of other things, such as profile image url's, we have screen names, we have the description of your profile, of the user, and all kinds of other little things that are useful for us. Each of these that you connect, so if you connect Facebook, it will be different than Twitter, so you'll have some generic things that are shared, like the credentials hash, and secrets and tokens, but not all of them have secrets; some of them just have tokens. This hash is actually changed every time, and we have to handle it differently for each. In this case, we're just going to take a look at Twitter.

For a prettier example of that hash of values, you can take a look on the OmniAuth Twitter's authentication hash example in the README that shows you how all of this information is nested. A lot of times you're going to look at Twitter's and the provider and the uid, which is your user id basically, which is different than your nickname or screen name. This gem auto parses out some of the stuff to standardize it into the info hash here, but if you want the raw information, you can see that there's a screen name here, as opposed to nickname there. It knows how to parse these things out. We actually need to generate our user model, and I'm going to create the model here, and we need to handle a handful of things.

First, we need the provider, which will be a string, we'll also need the uid, which will also be a string because sometimes-- One good example is I think LinkedIn, the user ids are not integers, so you want to make this a string just in case of that when you want to add maybe another authentication provider that way. We're also going to want a name for the user, so this might be the real name, or it might just be the @excid3 or @gorails like the username from Twitter, we're also going to want the credentials that we saw here. These credentials, in this case are going to need a token and a secret, so we'll also have the token and secret here as well. One last thing you might want like a profile image, and you can grab that from the image key value here in the info section as well. All of these should generally have a profile image that they will provide for you. Let's create that, and then open that up in our editor, so we'll open up app/models/user and then here, let's take a look at that sessions controller that we just created, and let's create this method


class User < ActiveRecord::Base
  def self.find_or_create_from_auth_hash(auth_hash)
    user = where(provider: auth_hash.provider, uid: auth_hash.uid).first_or_create
      token: auth_hash.credentials.token,
      secret: auth_hash.credentials.secret

We're going to do this and update the user because we always want the latest name, image and their token in secret, so we're going to do it that way, and then return the user at the end. We'll look the user up, we'll update them with the latest information on their profile, and then save the user. Twitter uses different urls for each profile image, so if you change it, a lot of those websites that connected with Twitter will have broken images. We want to do that so that next time it comes back we'll update your profile, make sure we've got your stuff in sync, and then you'll generally be best off that way.

Back here in the sessions controller, we can raise that error again, and let's try going back to our browser and then going to auth/twitter again and seeing what happens now that we get there. Of course, our migrations are pending and we need to run rake db:migrate. Now that those are finished running, let's go back to on Twitter, authorize the app and we should get the error page again, and this time unknown attribute 'image' for User. We just need to go back into the user, change this to profile_image and try that again. If you refresh this, you'll see that you get the user, it's been created in the database with id of number one, set the provider as Twitter, the uid, the name, token, secret and the profile image. Everything looks to have gotten set correctly and loaded up properly, and here we can now take our sessions controller and actually sign in the user. We don't have this self.current_user as a thing yet, and I'm going to just use the session, and we'll set session[:user_id] = We'll use and create our own sessions system by hand for this just because it's simple, and that's what we'll do, and in the application controller, we can have:


def current_user 
    User.find(session[:user_id]) if session[:user_id]

That will look the user up and set that, and we're going to use a caching here to the instance variable so that when you're signed it we'll look this up from the database once, but we'll only do that once and save it to this variable. Here in our


      <% if current_user %>
        <p class="navbar-text navbar-right">Signed in as <%= %></p>
      <% end %>

Last but not least, we need the

helper_method :current_user

Save that, refresht the page, and you'll see that I'm now signed in as "Chris Oliver", we're able to authenticate our user and we've saved their information to the database records which we can see if we open up the rails console as well, we grab the first user, so that is my name that we've got. If we want to, we can experiment with this and change some things and we can say: Here, instead of that, we'll use a nickname instead, and now if we go to auth/twitter, and authorize the application again, it will come back here and see that I'm signed in as @excid3, we are correctly updating these credentials every single time, which is fantastic, and now we can go into our user model and add some functionality in here to connect with the Twitter gem.

The Twitter gem by sferick is pretty fantastic in always being constantly updated, tons of tests for it really well written. Here, we'll just be able to take a look at their very simple new REST client and we'll use this inside of our application. Let's dive into doing that. First thing is first, paste the gemfile line in there, then dive into our terminal, run bundle install to install it, and once it's installed, we can grab this line from the README here, and then let's jump to our user model and set up just this simple Twitter client here under the Twitter method. Here I'm going to say


  def twitter
    @client ||= do |config|
      config.consumer_key        = Rails.application.secrets.twitter_api_key
      config.consumer_secret     = Rails.application.secrets.twitter_api_secret
      config.access_token        = token
      config.access_token_secret = secret


class Tweet < ActiveRecord::Base
  belongs_to :user

  validates :user_id, :body, presence: true

  before_create :post_to_twitter

  def post_to_twitter


def create 
  @tweet =
  @tweet.user_id = 

This way, we make sure that this always gets set to your current user's id and you can never impersonate someone else and tweet from their account, which would be very bad. The last thing we need to do is update our form, kill off that user_id method, and that is all we need to do, and we should be able to create a new tweet. "Test post from our @goRaills episode", and create that Tweet. Cross your fingers and see what happens.

It looked like everything worked, we got right back to the first tweets page, and we can go check Twitter to make sure that it actually posted.

Here you go, so this correctly posted to Twitter, and everything worked just like we expected it to. This Twitter REST client is actually the full API client for your users that you can access in behalf of them, so if you check out the sferick Twitter page, you can see all of the different things you can do, such as following users, fetching users, seeing all the followers as a specific user, finding friends and so on. If you want to get your timeline to display maybe on the homepage here, you could do that using the home time line or the user timeline or whatever you want to do. You have full access to that using the user.twitter method, so all you need to do is use that from a user instance and there you go, you have full access to the Twitter client.

I hope you found this episode useful, I really enjoyed making applications with the Twitter API and connecting with OmniAuth, you can take a lot of this and switch out the keys from Twitter and switch them with Facebook and add OmniAuth Facebook and do very much the same thing there using a different gem like Koala to set up the API connection for you. I hope that's a great foundation for you to get started with and we'll probably dive a little bit deeper into it in the future.



Excellent episode Chris! 2 off-episode questions: why does your version of Chrome have a "People" menu and any chance you'll post this to github?


I think it came in Chrome beta a month or two ago. It's so useful to separate out your Google accounts between windows.

And I've uploaded the repo for this app to Github for you. :) It's the last link in the resources list.


Nice episode Chris. A small note that might help people new to adding initializers is that you need to restart your app after you add the omniauth initializer so that localhost:3000/auth/twitter works as expected. Might throw a few people off if they follow your steps along and forget to restart the app.


Good catch! At a point, it becomes second nature and I forget to mention things like that being required. :)


Rutul, you saved my bacon with that. I was using a RailsCast tutorial for ages and completely forgot that tip. This one is much more current and useful though. Thanks all!


Agree with Tim. This tut is awesome and way better than the railscast video about twitter. Rutul, you saved my life too.


The Omniauth auth hash actually uses a gem called Hashie to allow method access for keys, not HashWithIndifferentAccess which only allows you to use symbols/strings keys interchangeably.

The main annoyance of Omniauth I find is when using it with Devise. The Devise user model requires an email to work 'out of the box' and Twitter's API does not give you the users email.


Yeah my mistake there. Hashie is great.

Agreed with Twitter's API. They want to control that information which is unfortunate. In that case, you can generate a fake email if you don't want to ask the user for it, or you can send them to a form to fill out that information. Neither is ideal, but it gets the job done.


I found this post useful:

It works on the basis that a user might want to login with different OAuth providers but for them to link to the same account. It fixes the Twitter problem by basically asking for an email addresses after logging in.


Good one nicely explained


Super helpful episode! I was in the middle of connecting my app to Stripe Connect (which also uses OmniAuth) and the steps are pretty much identical. Just wondering if anyone has come up with a good strategy for testing this kind of behaviour?


This looks like a reasonable approach to testing your user creation and lookups but it isn't a full example:


Brilliant! Got it working. More info here too if anyone's interested:


How it to realize in database Mongoid? Mistake $oil... "id" mongo "_id" error, name no safe in database... help


Chris, I keep trying to take a version of this into the production environment but have had no luck. Keep getting "OAuth::Unauthorized 401 Authorization Required" errors. Any idea why?


I believe that can happen with the callback url on Twitter. You might try instead of localhost.


I figured it out. I had forgotten to move my twitter api key and secret to the production area of secrets.yml. Thanks mate!


Awesome, glad you got it figured out!


Any thoughts to why my variables declared in secrets.yml file would not be loading correctly when I restart my app? My Rails.applications.secrets.twitter_xxxx isn't pulling correctly to the point where the omniauth.rb initializer crashes rails when I restart the server.


Great episode Chris, I'm currently trying to integrate this gem with devise but not very successful, any chance you could run through this in the near future?


That's the plan. Twitter is a bit frustrating because it doesn't give you an email so you can't create Devise users easily with it. Need to store the OAuth hash in the session so you can ask for an email first. I'll be doing an episode on that soon.


Thanks again Chris, it's also nice to see your rapid reply to a newbie like me 😃


did you ever manage to get this going? Can't login users because of


I haven't done an episode on it, but for now, check out this tutorial. It should do the trick for you!


Recently twitter started to white-list applications to provide developers with more access to user data; such as, email. Check out this link and fill the application to get white listed.


Awesome, didn't realize they started offering that. This is going to help a lot with Twitter integrations. Thanks for sharing!


Just on the gitignore for secrets.yml. You can't do this after you have already commit secrets.yml. I followed the instructions but had already commited it so gitignore would always see it even if I deleted it etc.

The answer I am now looking for is how to generate a new secrets.yml secret_key_base


from the command line `rake secret` and copy into your secrets.yml file for develop and test environments (if you are choosing to gitignore the new file).


Please note that if you follow this guide and you are also using devise omniauthable you will not need to create the omniauth.rb file. You can go to devise.rb and find the omniauth configuration lines there (just uncomment the config.omniauth lines and set it like Chris does (I think initially Devise had ENV instead of Rails.application.secrets...

# ==> OmniAuth
# Add a new OmniAuth provider. Check the wiki for more information on setting
# up on your models and hooks.
# config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'
config.omniauth :yammer, Rails.application.secrets.yammer_api_key, Rails.application.secrets.yammer_api_secret

If you did what I did and set the devise.rb as well as the omniauth.rb then you will get two callback requests and trigger a CSRF error. (note I am using Yammer and yammer-omniauth gem instead of Twitter but pretty much all worked the same.

Started GET "/users/auth/yammer/callback?code=keyQ&state=secret" for at 2015-02-05 16:31:15 +1000
I, [2015-02-05T16:31:15.558087 #3900] INFO -- omniauth: (yammer) Callback phase initiated.
I, [2015-02-05T16:31:42.574957 #3900] INFO -- omniauth: (yammer) Callback phase initiated.
E, [2015-02-05T16:31:42.575381 #3900] ERROR -- omniauth: (yammer) Authentication failure! csrf_detected: OmniAuth::Strategies::OAuth2::CallbackError, csrf_detected | CSRF detected
Processing by Users::OmniauthCallbacksController#failure as HTML
Parameters: {"code"=>"key", "state"=>"secret"}
Redirected to http://localhost:3000/sign_in
Completed 302 Found in 2ms (ActiveRecord: 0.0ms)


Really good walkthrough, I was able to use this for Facebook as well with some elbow grease. Thank you. And you make me want to learn VIM!


Learning Vim proved to be super useful for me over the years! :)

Fabián Francisco Leiva

Really good tutorial, i tried this for facebook as well and works great! but now im having issues when i try to obtain user's data such as the email. Some say that i have to edit the "scope" parameter on omniatuh.rb, plz can anybody help me?


You can update your config when you setup Omniauth, you can specify your scope there. There are a lot of options for this, so just look up the available scopes for facebook to get the full list.

config.omniauth :facebook, "APP_ID", "APP_SECRET", scope: 'email', info_fields: 'email, name'


Is there a previous video that I'm missing? This was a great video until the part about the actual tweets-- it seems to be missing the part where used rails to create the Tweet and TweetsController scaffolding and routes.


Very helpful Chris! Learnt a lot in this 20 minutes session.. Thank you!

Shawn Nigel Rebello

Very helpful tutorial Chris! Got it working for facebook as well


I got this type of error please give me a way to solve it. https://uploads.disquscdn.c...

Login or create an account to join the conversation.