Skip to main content

Sign In With Twitter using Omniauth and the Twitter gem

40

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


Transcripts

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 127.0.0.1 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

config/initializers/omniauth.rb

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

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

app/models/users_controller.rb

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
    user.update(
      name: auth_hash.info.nickname,
      profile_image: auth_hash.info.image,
      token: auth_hash.credentials.token,
      secret: auth_hash.credentials.secret
    )
    user
  end
end

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] = @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:

app/controllers/application_controller.rb

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

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

app/views/layouts/application.html.erb

      <% if current_user %>
        <p class="navbar-text navbar-right">Signed in as <%= current_user.name %></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

app/models/user.rb

  def twitter
    @client ||= Twitter::REST::Client.new 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
    end

app/models/tweet.rb

class Tweet < ActiveRecord::Base
  belongs_to :user

  validates :user_id, :body, presence: true

  before_create :post_to_twitter

  def post_to_twitter
    user.twitter.update(body)
  end
end

app/controllers/tweets_controller.rb

def create 
  @tweet = Tweet.new(tweet_params)
  @tweet.user_id = current_user.id 
  @tweet.save 
  respond_with(@tweet)
end

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.

Loading...

Subscribe to the newsletter

Join 18,000+ developers who get early access to new screencasts, articles, guides, updates, and more.

By clicking this button, you agree to the GoRails Terms of Service and Privacy Policy.

More of a social being? We're also on Twitter and YouTube.