• Archive
  • RSS
  • Ask me anything

Node JS - Listen to External IP Address

Good to know !

  • 2 months ago
  • Comments
  • Permalink
  • Share
    Tweet

The SOOMLA Blog: Amazon's Global Coin - Smart Move or Doomed to Fail

soomla:

Amazon recently announced the launch of a global coin allowing game developers for the kindle fire to recognize it as a form of payment for in app purchases. This is not a new idea, far from it. One might remember that Facebook discontinued a similar service by the name of Facebook credits or…

  • 3 months ago > soomla
  • 2
  • Comments
  • Permalink
  • Share
    Tweet

MongoHQ vs MongoLab: Selecting a Hosted MongoDB Provider - Dan Adams

  • 3 months ago
  • Comments
  • Permalink
  • Share
    Tweet

android tip #2: @JavascriptInterface

If your targetSdkVersion (in AndroidManifest.xml) is 17 and you’re adding a Javascript interface to a Webview than you’ll have to add the annotation @JavascriptInterface above every JS interface function (all functions must also be public).

@JavascriptInterface
public void getDeviceId() {
    return "blabla";
}
(taken from http://developer.android.com/guide/webapps/webview.html)

  • 3 months ago
  • Comments
  • Permalink
  • Share
    Tweet

android tip: allow cross-origin for local files

If you’re on JellyBean and you get this error: ‘Origin null is not allowed by Access-Control-Allow-Origin’ when trying to navigate to a local file when inside a Webview, just add the following:

if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
  webView.getSettings().setAllowUniversalAccessFromFileURLs(true);
}

  • 3 months ago
  • Comments
  • Permalink
  • Share
    Tweet

Great project for publish-subscribe in Android

I found Square’s otto while looking for a way to implement the publish-subscribe pattern in an Android application.

This great project has exactly what you need to have a notification center (like ios’s NSNotificationCenter) in Android.

  • 4 months ago
  • Comments
  • Permalink
  • Share
    Tweet

ios tip: Screen Rotation ios5 vs ios6

Just a small tip for ios devs:

Rotating a UIViewController is done differently in ios5 and ios6. Just copy the relevant code below into your UIViewController. You can copy both code blocks if you want to support both io5 and ios6.

ios5:


-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation{
    if ([[StorefrontInfo getInstance] orientationLandscape]){
        return UIInterfaceOrientationIsLandscape(toInterfaceOrientation);
    }

    return UIInterfaceOrientationIsPortrait(toInterfaceOrientation);
}

ios6:


- (BOOL)shouldAutorotate
{
    //returns true if want to allow orientation change
    return YES;
}

- (NSUInteger)supportedInterfaceOrientations
{
    if ([[StorefrontInfo getInstance] orientationLandscape]){
        return UIInterfaceOrientationMaskLandscape;
    }

    return UIInterfaceOrientationMaskPortrait;
}
  • 4 months ago
  • Comments
  • Permalink
  • Share
    Tweet

Rails with AJAX Facebook Connect

Just implemented an option to connect to our Designer using Facebook and i wanted to share. It’s actually quite easy when you get the hang of it.

My requirements:

  1. Let users connect with their Facebook account (duh!).
  2. The login process must be done with AJAX and not like the regular process where you get redirected to facebook and back to the home page when the login process finishes.

Lets get right to work:

Integrating Facebook into your Rails application


Everyone knows about the AMAZING authentication plugin devise. This entire tutorial is based on The ‘devise’ team’s GREAT tutorial that explains how to integrate Facebook into your Rails application. Go over the tutorial and make sure it works. Things i did different:
  1. I’m using MongoDB (with Mongoid) so instead of creating a migration to the User table i just added ‘provider’ and ‘uid’ straight to the User model.
  2. In the function ‘find_for_facebook_oauth’ (in the User model) i added code that checks if the user that just connected with his Facebook account already registered with us (by the user’s email). This is my code:

  def self.find_for_facebook_oauth(auth, signed_in_resource=nil)
    user = User.where(provider: auth.provider, uid: auth.uid).first
    unless user
      # checking if the email from facebook already exists for one of the users
      user = User.where(email: auth.info.email).first
      if user
        user.provider = auth.provider
        user.uid = auth.uid
        user.save!
      else
        user = User.create(name:auth.extra.raw_info.name,
                           provider:auth.provider,
                           uid:auth.uid,
                           email:auth.info.email,
                           password:Devise.friendly_token[0,20]
              )
      end
    end
    user
  end

Supporting AJAX

We want to respond to the Facebook-Connect login process with a JSON format response. You’ll see in the next step below that you’ll handle this response and use it to change stuff on the screen.
(look in the code below for [PUT YOUR POST-LOGIN CODE HERE]).

To change the response to a JSON response, go to ‘OmniauthCallbacksController’ and make it look like this:


class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def facebook
    @user = User.find_for_facebook_oauth(request.env["omniauth.auth"], current_user)

    if @user.persisted?
      sign_in(@user)

      render :status => 200, :json => { :user => { :email => @user.email, :name => @user.name } }
    else
      render :status => 401, :json => { :errors => alert }
    end
  end
end

Adding Facebook javascript SDK

If we don’t want to navigate out of the page in order to connect with Facebook and we want to get the Facebook-Connect callback into our Rails application, we have to take the default code from Facebook javascript SDK and change it a bit. This is what you need to add to your application.html.erb:


      <!-- Facebook connect script -->
      <div id="fb-root"></div>
      <script type="text/javascript">
          window.fbAsyncInit = function() {
              // init the FB JS SDK
              FB.init({
                  appId      : '[YOUR APP ID HERE]', // App ID from the App Dashboard
                  channelUrl : '//[YOUR DOMAIN HERE]/channel.html', // Channel File for x-domain communication
                  status     : true, // check the login status upon init?
                  cookie     : true, // set sessions cookies to allow your server to access the session?
                  xfbml      : true  // parse XFBML tags on this page?
              });

              // Additional initialization code such as adding Event Listeners goes here

          };

          (function(d, debug){
              var js, id = 'facebook-jssdk', ref = d.getElementsByTagName('script')[0];
              if (d.getElementById(id)) {return;}
              js = d.createElement('script'); js.id = id; js.async = true;
              js.src = "//connect.facebook.net/en_US/all" + (debug ? "/debug" : "") + ".js";
              ref.parentNode.insertBefore(js, ref);
          }(document, /*debug*/ false));

          $(function() {
              $('#facebook-connect').click(function(e) {
                  e.preventDefault();

                  FB.login(function(response) {
                      if (response.authResponse) {
                          // since we have cookies enabled, this request will allow omniauth to parse
                          // out the auth code from the signed request in the fbsr_XXX cookie
                          $.getJSON('<%=user_omniauth_callback_path(:facebook)%>', function(data) {
                              // 'data' contains a 'user' object with 'email' and 'name' in it.
                              [PUT YOUR POST-LOGIN CODE HERE]
                          });
                      }
                  }, { scope: '[ADD THE SCOPE YOU WANT HERE]' });
              });
          });
      </script>
      <!-- end Facebook connect script -->
  • channelUrl is just for cross domain issues on some browsers. Add a file called ‘channel.html’ to your public folder and in it put this one line:
    
    <script src="//connect.facebook.net/en_US/all.js"></script>
    
  • You can see that i put the Facebook-Connect login process to a button with id ‘facebook-connect’. Make sure you have it in your code.

That’s it! You got Facebook Connect working with AJAX in your Rails application

IMPORTANT: omniauth-facebook has an issue with CSRF in version 1.4.1 so use 1.4 until they’ll fix it. (thanks Alex Dolgov)

    • #facebook
    • #facebook connect
    • #rails
    • #devise
    • #omniauth
    • #ajax
  • 4 months ago
  • 1
  • Comments
  • Permalink
  • Share
    Tweet

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

  • 1 year 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 year ago
  • 1
  • Comments
  • Permalink
  • Share
    Tweet
← Newer • Older →
Page 1 of 2

Logo

Avatar I'm a technology addict and a co-founder of SOOMLA. Interested in anything! In my blog i write about stuff that I program, stuff that other people program and other stuff. ENJOY !

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