Thoughts on Tech

  • Archive
  • RSS
  • Ask me anything
banner

To access url helpers (url_for, etc) from Rails console (Rails 3) - Rails - Snipplr Social Snippet Repository

  • 3 weeks ago
  • Comments
  • Permalink
  • Share
    Tweet

Multiple token authentication with Devise & redis

I’ve been developing on Rails for quite some time now and i continue to realize the power and benefits this framework gives you. In Rails, when you want to get a new capability into your application, you first try to find something OOTB. The best gem for the job is usually the one that gives you everything you want in one place. The variety and maturity of community plugins provide Rails developers with the ability to easily create rich web applications. But what happens when you find a plugin that gives you 95% of what you want and you need to add the 5% yourself ?

I found Devise to be that plugin. This great plugin gave me everything i needed in order to authenticate users in my application except one feature.

Using Rails, i have developed an api and i was looking for a way to authenticate users in different clients simultaneously and let every client work dependently of the others. Devise only gives you 2 forms of authentication: database (using cookies) and token based authentication. I wanted to use the database authentication for users who login with email and password and the token authentication for those who wanna use the api from mobile devices and other applications and want to just login with a token.

Database authentication using Devise was pretty easy. With the tokens i had 2 requirements:

  1. The token had to be regenerated on every login (for security reasons).
  2. Logging out of the api had to revoke the token.

The problem i had was that logging in from 2 devices simultaneously was impossible because (the numbers correlate to the requirements numbers above):

  1. One client login always revokes the token for the other.
  2. Logging out from one client revokes the token for the other.

I figured i had to generate a token for each user+client. That token will “live” for as long as the user is logged in on a specific client. In order to accomplish that and still use Devise i had to expand its capabilities to support my multiple tokens philosophy. I also selected redis as my storage for the tokens (I won’t get down to details on “why redis” in this post)

So lets go down to the code:

First, i had to create a class that handles the interaction with redis. I created a file under “lib” and called it authentication_tokens.rb. In my implementation, every token is added to redis with the prefix “auth_token:”. You can also notice that i’ve put a 24.hours expiration time for a token (24 hours of no activity with the token).


class AuthenticationTokens
  include Singleton

  def initialize
    @redis   ||= Redis.new(:host     => "localhost")
  end

  def AuthenticationTokens.finalize(id)
    @redis.quit
  end

  # generate authentication token using the given email and save it in redis
  # the generated token will have a field "user_id" in order to identify the associated user later
  def generate(email, user_id)
    token   = Utils::gen_token(email)
    key_s   = token_key token
    @redis[key_s] = user_id
    @redis.expire  key_s, 24.hours

    token
  end

  # renewing expiration timeout
  def touch(token)
    @redis.expire  token_key(token), 24.hours
  end

  # deletes the token
  def revoke(token)
    @redis.del token_key(token)
  end

  def token_val(token)
    @redis.expire  token_key(token), 24.hours
    @redis[token_key(token)]
  end

private

  def token_key(token)
    "auth_token:#{token}"
  end
end

I also created a Devise strategy to handle my multiple tokens authentication logic inside devise. The strategy is kept in multiple_tokens_strategy.rb under “config/initializers”. The strategy uses the previously created class in order to check with redis if the token exists.


module Devise
  module Strategies
    class MultipleTokensStrategy < Devise::Strategies::Base
      def valid?
        params[:auth_token]
      end

      def authenticate!
        user_id_str = AuthenticationTokens.instance.token_val(params[:auth_token])
        if user_id_str
          user = User.find(user_id_str.to_i)
          if user
            user.after_database_authentication
            success!(user)
          end
        end

        if (!(params[:user]) && !halted?)
          fail!("Invalid authentication token.")
        end
      end
    end
  end
end

The newly created strategy needs to be added to the initializer “devise.rb”


Devise.setup do |config|
...

  config.warden do |manager|
    manager.strategies.add(:multiple_tokens_strategy, Devise::Strategies::MultipleTokensStrategy)
    manager.default_strategies(:scope => :user).unshift :multiple_tokens_strategy
  end
end

After creating the strategy and registering it with Devise we just need to generate the token when the user logs into the system (through the api or regular signin). In order to do that we’ll have to override Devise’s SessionsController as explained in many places on the web (for example: http://stackoverflow.com/questions/8070320/rails-3-override-devise-sessions-controller). After overriding Devise’s SessionsController, i overridden 3 actions: “create” & “require_no_authentication” & “destroy”. I copied Devise’s original code and added my functionality inside. Inside the code you’ll see comments with what i did exactly:


class SessionsController  resource_name,
                                    :recall => ("#{controller_path}#new" : "sessions#authentication_failure_json"))

    # This code is copied from Devise
    set_flash_message(:notice, :signed_in) if is_navigational_format?
    sign_in(resource)


    # if the winning strategy is MultipleTokensStrategy than don't replace  
    # auth_token, otherwise generate a token for the user.
    if warden.winning_strategy.class != Devise::Strategies::MultipleTokensStrategy
      auth_token = AuthenticationTokens.instance.generate resource.email, resource.id
    else
      # we assume that params[:auth_token] exists b/c winning strategy is MultipleTokensStrategy
      auth_token = params[:auth_token]
    end
    # keep the token in the session for future reference
    session[:auth_token] = auth_token

    # Here i respond to different formats.
    respond_to do |format|
      format.html     { redirect_to root_path }
      format.json     { render :json => resource }
    end

  end

  # This action is called to check if an authentication is actually needed.
  def require_no_authentication
    # This code is copied from Devise
    no_input = devise_mapping.no_input_strategies
    args = no_input.dup.push :scope => resource_name
    if no_input.present? && warden.authenticate?(*args)
      resource = warden.user(resource_name)
      flash[:alert] = I18n.t("devise.failure.already_authenticated")

      # My code here
      # We generate a new token even if no authentication is required b/c we don't
      # know what token to return if the user is logged in already and asks to
      # login again ONLY with user and password than (in my implementation) he  
      # actually "asks" for a new token.
      # If the user is logged in already and asks to login again with 
      # a valid auth_token than that token is returned.
      auth_token = params[:auth_token] || session[:auth_token] || AuthenticationTokens.instance.generate(resource.email, resource.id)
      session[:auth_token] = auth_token

      # Here i respond to different formats.
      respond_to do |format|
        format.html { redirect_to after_sign_in_path_for(resource) }
        format.json   { render :json => resource }
      end

    end
  end

  # This action is called on logout.
  def destroy
    # My code here.
    # I look for the user with the given token. If he exists than he gets logged 
    # out, if not than the current_user is logged out.
    auth_token = params[:auth_token]
    if auth_token
      user_id_str = AuthenticationTokens.instance.token_val(auth_token)
      if user_id_str.blank?
        if current_user
          @user = current_user
        else
          respond_to do |format|
            format.html { redirect_to root_path }
            format.json { render :json => {:errors => "Already logged out!"} }
          end
          return
        end
      else
        @user = User.find(user_id_str.to_i)
        AuthenticationTokens.instance.revoke auth_token
        session[:auth_token] = nil
      end
    else
      @user = current_user
    end

    # This code is copied from Devise
    sign_out(@user)

    # Here i respond to different formats.
    respond_to do |format|
      format.html { redirect_to root_path }
      format.json {
        render :status => 200, :json => { :email => @user.email } }
    end
  end

  # This function supports returning error message on failure to authenticate.
  def authentication_failure_json
    return render :json => { :errors => alert }
  end
end

That’s it! Quite an effort to get it done but with that you’ll have the ability to connect with multiple clients to your rails application without then interfering each other.

  • 1 month ago
  • Comments
  • Permalink
  • Share
    Tweet

Startup life. The bad days. (*) « 21st century geek

Great post !

  • 3 months ago
  • Comments
  • Permalink
  • Share
    Tweet

Ruby Glazed: git-ify your command line!

rubyglazed:

In a nutshell:

Run bash < <(curl -s https://raw.github.com/gurdotan/gitify/master/gitify ) and get lots of nice command line goodies for git (test only on Ubuntu). Note: running this will not include the utilities detailed below in sections 1 & 5.

In detail:

I’ve been working with git a lot…

Source: rubyglazed

  • 4 months ago > rubyglazed
  • 5
  • Comments
  • Permalink
  • Share
    Tweet

Windows Phone - Will history repeat itseft ?!

From beginning of time, there’s one rule that no one can disagree with: “History repeats itself”. Over and over again we see how events repeat themselves in different domains of our lives. Empires come and go, social revolutions takes down dictators and new eras of blossoming creativity always follows grimy times of wars and ignorance. It looks like things change but everything stays the same. There’s nothing new under the sun. The same thing is going to happen to the long lasting battle on operating systems for mobile phones.

Bill Gates started the major professional road of his life working for Apple, understanding its ways and coming out with his own ideas based on what he learned. He based his company on ideas that were created by others and managed to invent products that’ll suit the majority of people based on what he learned. Gates shaped Microsoft in his image so it’s not a surprise we can see the giant corporation doing the same over and over again. They almost never invented something completely new and when they tried to do that they almost always failed.

Knowing how to be #2 or #3 or less until the right moment pops out is a skill that requires patience, ease of mind and cleverness that Gates and his teammates proved to have made a way of life. Doing exactly that with Windows, Xbox, Zune, .NET and other products by copying ideas from other companies or just buying them shows how Microsoft understands how to make successful technological products that will eventually be used by the majority of their market.

Don’t be surprised if Microsoft is going to pull it off again with Windows Phone. Apple started the new era of smart phones (as they did with personal computers) and Google’s android is a great iPhone killer but when MS wants to play he knows how to win better than anyone else. Playing their cards right, Gates’ gang can do what they do best and if that happens the others don’t have a chance.

windows phone

Apple and Google are not going to give up in this fight. Apple already gives mobile developers great tools and APIs. They are going to keep giving their groopies little drops of heaven with every new release of iOS. Google’s Android started as open as it can get and it’s starting to close down and look like a mature mobile OS that actually looks and feels nice. They even show signs of improvement in their IDEs and APIs so mobile developers might find it enjoyable to develop android apps one day. Both companies have made quite a journey with their OSs and they learn as they go. It’s always hard to fight against experience. 

apple android

That been said, look at Microsoft, they have the best dev tools for their dev technologies, they provide prestige and usability and they got NOKIA which is a major league player in the old cell phones league. Windows phone is cool, fun, friendly and going to be open just enough for people to get what they want. Microsoft watched the game from the bench. They played a bit on the regular phones market with the failed Windows Mobile BUT they got the essence of the smartphone game: make it fancy and fully featured but keep it simple and easy to understand. If they’ll find the balance between customization and usability they might win another one for the fully packed trophy closet.

According to what we’ve already seen and to Windows Phone’s leaked road-map, Microsoft is taking it slow. They know the market is still developing so they take small and cautious steps. Everyone in this market is trying to figure out what people really want from their smartphone. We know that people want it to be versatile and creative but they also need it to be simple and friendly in order for them to be productive. Close or Open, Simple or Weary … where’s the balance?!

It’ll be interesting to see what’ll happen in 2012. I’m guessing that 2011 will continue. Android will continue to gain power and new iOS features will be presented. Taking a look a couple of years ahead i think things are going to change. Will history repeat itself? i say YES.

  • 5 months ago
  • Comments
  • Permalink
  • Share
    Tweet

Facebook keeps you alive !

Why people use Facebook or Twitter? Why people share videos, pictures and thoughts online? What pleasure do we get from the basic action of uploading a photo or sharing an idea? In the same breath one can also ask why people participate in reality shows, want to be a celebrity or even skydive? It all lies in the urgent need of every human being to feel alive.

alive!

Human average life expectancy in present times is about 67 years. We see people come and go from our world. Some of them leave their mark and some of them just enjoy the ride. But everyone want to feel their existence, they want to get the validation that they are here. The basic knowledge that we walk on this world and enjoy its perks makes us happy and fills us with a sense of fulfillment.

When you upload a photo to Facebook from your last trip to NYC, you get excited. The excitement doesn’t come from clicking the mouse or commenting “I love shopping in NYC” on your keyboard. It comes from the feeling that in a few minutes (or hours) you’ll get the acknowledgement that you’re alive. When you know that people will see your photo and acknowledge your existence you feel alive. When your friend writes a response comment like “Great photo, you look amazing!” you get the feedback you needed. The validation that you’re here.

robots

People are like machines. They are an input-output device. When someone tells you you’re ugly than you’ll feel bad. When someone will hug you you’ll feel good (unless he smells bad). We live our day to day occurrences and react to them. That’s why we can’t feel alive without people (or other outer objects) telling us we’re alive. That’s what Mark Zukerberg and his fellow social services CEOs know. They realized that as long as they give you the feeling that you’re alive they will keep you their “friend”.

You can see a clear difference between Facebook and Google+ and it’s not the social circles. Facebook is full of active users. Your Facebook wall is vivid while your Google+ stream is dull as dishwater. You know there’s a better chance that someone will notice you when you share something on Facebook. And if there’s a better chance someone will notice you than there’s a better chance they’ll give you the validation you need. The validation of existence. 

So the next time you publish something on Facebook or twitter or the next time you check-in in Startbucks and become the mayor of your neighborhood coffee shop know that you’re just trying to stay alive.

  • 5 months ago
  • 65
  • Comments
  • Permalink
  • Share
    Tweet

Devise sign_in using JSON

I was trying to create a RESTful web-service using Rails 3.0. I wanted my web-service to communicate with “the world” using JSON. I also wanted my web-service to use two security methods:

  1. Database security - using the server session.
  2. Token security - in order to allow users to login to the api using tokens.

Devise answered my security needs. It was also pretty easy to add JSON responses b/c rails pretty much gives it to you OOTB. I even found it easy to generate json templates using the great acts_as_api gem. BUT when i tried to figure out how to actually do devise’s sign_in using JSON request/response i hit a wall.

I found this great post by Jesse Howarth that explains how to do the actual sign_in and with some alterations it worked fine. Howarth fails to explain one thing: How to return devise’s error messages when sign_in fails.

In order to achieve that i had to override SessionController’s create action and add an authentication_failure method.

  def create

    resource = warden.authenticate!(:scope => resource_name, :recall => “sessions#authentication_failure”) 
    set_flash_message(:notice, :signed_in) if is_navigational_format?
    sign_in(resource)
    resource.reset_authentication_token!

    respond_to do |format|
      format.json { render :status => 200, :json => { :success => true, :auth_token => resource.authentication_token, :user => resource } }
    end

  end

  def authentication_failure

    return render:json => {:success => false, :errors => alert}

  end

From authentication_failure you can see that the actual failure message is kept in the variable alert. 

Note: In create action i decided to reset the authentication token when the user logs in. It’s up to you if you want to keep this functionality.

Sometimes you have to do some hacking in order to get your desired solution.

  • 5 months ago
  • Comments
  • Permalink
  • Share
    Tweet

acts_as_api … :meta => @data

I’ve been working with the fantastic Rails for a few months now and i’m still discovering the wonders of it.

In order to create different representations of JSON for my REST api i picked acts_as_api. As the author of this plugin describes, it’s really easy and fun to use. When a colleague of mine asked me to provide him with meta information about returned data i found another cool feature inside it.

In our system we deal with plenty of entities. For the purposes of this post i’ll discuss one of them: Review.

My colleague is using my api to retrieve reviews. He used to call:

http://api.our_company.com/reviews.json

And got:

{“review”:[{“id”:1,”title”:”Great iadea”,”comment”:”I love your idea. You’re a great thinker.”,”rating”:4.5},{“id”:2,”title”:”Great idea”,”comment”:”I love your idea. You’re a great thinker.”,”rating”:4.5}]}

acts_as_api code (inside review.rb) was:

class Review < ActiveRecord::Base
  …
  acts_as_api
  api_accessible :api_v1 do |t|
    t.add :id
    t.add :title
    t.add :rating
    t.add :comment
  end
end

reviews_controller.rb was:

class ReviewsController < ApplicationController
  def index
    respond_to do |format|
      format.json { render_for_api :api_v1, :json => @reviews }
    end
  end
end

The returned JSON representation is perfectly and easily created by acts_as_api.

BUT than my colleague wanted to retrieve the total number of reviews along with the list of reviews. I started by the obvious (but worse) solution and than found the better solution.

The obvious (but worse) solution:

acts_as_api lets you add special functions to your representation so i just added “total_reviews” to the list of returned parameters:

I revised acts_as_api code (inside review.rb):

class Review < ActiveRecord::Base
  …
  acts_as_api
  api_accessible :api_v1 do |t|
    t.add :id
    t.add :title
    t.add :rating
    t.add :comment
    t.toal :total_reviews
  end


  def total_reviews
    Review.all.count
  end
end

The returned JSON was:

{“review”:[{“id”:1,”title”:”Great iadea”,”comment”:”I love your idea. You’re a great thinker.”,”rating”:4.5,“total_reviews”:2},{“id”:2,”title”:”Great idea”,”comment”:”I love your idea. You’re a great thinker.”,”rating”:4.5,“total_reviews”:2}]}

That’s not a bad solution but the total number of reviews is put in every single review.

The better solution:

I switched acts_as_api code in the model back to the initial code and went to reviews_controller.rb:

class ReviewsController < ApplicationController
  def index
    respond_to do |format|
      format.json { render_for_api :api_v1, :json => @reviews, :meta => {:total => Review.all.count} }
    end
  end
end

And … The magical returned JSON was:

{”total”:2, ”review”:[{“id”:1,”title”:”Great iadea”,”comment”:”I love your idea. You’re a great thinker.”,”rating”:4.5},{“id”:2,”title”:”Great idea”,”comment”:”I love your idea. You’re a great thinker.”,”rating”:4.5}]}

acts_as_api is taking care of the meta data and putting it in front of the returned data.

I love it !

  • 6 months ago
  • Comments
  • Permalink
  • Share
    Tweet

My first post EVER !

After year of reading other people’s blogs and thinking of what they have to say, i decided to share my ideas and findings in many aspects of my life.

Happy reading :)

first post

    • #first post
  • 6 months ago
  • Comments
  • Permalink
  • Share
    Tweet
Avatar I'm an experienced software engineer interested in anything that involves new technology. In my blog i write about stuff i program and thoughts i have about trends in technology.

Me, Elsewhere

  • @rafachon on Twitter
  • Facebook Profile
  • rafach on Flickr
  • Google
  • Linkedin Profile
  • refaelos on github
  • RSS
  • Random
  • Archive
  • Ask me anything
  • Mobile

Refael Dakar. Effector Theme by Carlo Franco.

Powered by Tumblr