Skip to content

Instantly share code, notes, and snippets.

@cousine
Created November 15, 2010 13:18
Show Gist options
  • Save cousine/700343 to your computer and use it in GitHub Desktop.
Save cousine/700343 to your computer and use it in GitHub Desktop.
Sing Sign-in Solutions in Rails using Authentasaurus: The provider
# ---------------------------------- Add in app/controllers/remote_sessions_controller.rb
# This will handle the first step of authenticating through a token
def authenticate_token
# Read the token from the cookie
token = Token.authenticate cookies.signed[:provider_token]
# Find application in the database
current_application = App.find_by_name params[:application]
# Get the application url so we can redirect the user back to it.
application_callback = "#{current_application.url}#{current_application.callback_path}"
respond_to do |format|
unless token.nil?
# Found the token
#
# setup a session (Authentasaurus style)
session[:user_id] = token.user.id
session[:user_permissions] = {:read => token.user.permissions.collect{|per| per.area.name if per.read},
:write => token.user.permissions.collect{|per| per.area.name if per.write}}
# redirect the user back to the application with the token and username
format.html {redirect_to "#{application_callback}/#{token.user.username}/#{token.token_hash}"}
else
# Token invalid
#
# Just redirect back to the application so the user can enter his/her credentials
format.html {redirect_to application_callback}
end
end
end
# This will handle the second step of authenticating through a token
def authorize_token
# Here we make sure that the token belongs to the username given
user = Token.authorize_token(params[:username], params[:token])
respond_to do |format|
unless user.nil?
# Success
#
# respond back with the user data
format.xml { render :xml => user }
format.json { render :json => user }
else
# Failure
#
# respond with 401 Unauthorized
format.xml { render :text => "Forbidden: Invalid Token" , :status => 401}
format.json { render :text => "Forbidden: Invalid Token" , :status => 401}
end
end
end
# ---------------------------------- Add in app/models/token.rb
# Authenticates the token
def self.authenticate(token_hash)
token = self.find_by_token_hash token_hash
unless token.nil?
return token
end
return nil
end
# Authorizes the username to the given token
def self.authorize_token(username, token_hash)
user = User.find_by_username username
return if user.nil?
token = self.authenticate(token_hash)
return if token.nil?
return unless token.user.id == user.id
return user
end
#### Rails 3
scope "/:application/remote_sessions" do
post "/signin.:format" => "remote_sessions#create"
get "/authenticate_token.:format" => "remote_sessions#authenticate_token"
post "/authorize_token.:format" => "remote_sessions#signin_token"
end
#### Rails 2.3.*
map.with_options :controller => :remote_sessions, path_prefix => "/:application" do |remote_sessions|
remote_session.connect "/remote_sessions/signin.:format",
:action => "create",
:conditions => {:method => :post}
remote_sessions.connect "/remote_sessions/authenticate_token.:format",
:action => "authenticate_token",
:conditions => {:method => :get }
remote_sessions.connect "/remote_sessions/authorize_token.:format",
:action => "authorize_token",
:conditions => {:method => :post }
end
class RemoteSessionsController < ApplicationController
# Just as we would handle a normal authentication request locally
# only difference is how we respond back to the application
def create
# Here we will recieve a normal request from the application
# and we can get the data sent by accessing the params array object
# and use them to authenticate the user.
# If you are using Authentasaurus, this is one way you can do it
@session = Session.new(:username => params[:username], :password => params[:password])
respond_to do |format|
if @session.save
# Successfull Login
#
# Create the token to authenticate the user again from another application
# automatically
token = Token.tokenize(@session.user)
# Add the token and user data to the response
response = @session.user.attributes.merge({:token => token})
# Respond back to the application with the user data and token
format.xml { render :xml => response }
format.json { render :json => response }
else
# Failed Login
#
# Tell the application that the user credentials were wrong.
# Note that we respond with status 401 Unauthorized, this makes it easy
# for developers implementing your API to understand what is going on.
format.xml { render :text => "Forbidden: Invalid Username/Password combination" , :status => 401}
format.json { render :text => "Forbidden: Invalid Username/Password combination" , :status => 401}
end
end
end
end
require 'digest/sha1'
class Token < ActiveRecord::Base
belongs_to :user
def self.tokenize(user)
# is this user already logged in?
old_token = Token.find_by_user_id(user.id)
# generate a token hash
token = self.generate_token_hash(user)
unless old_token
# create new hash and token
Token.create! :user_id => user.id, :token_hash => token
else
# update the old token
old_token.update_attributes :token_hash => token
end
token
end
private
# Generates a token hash for the user supplied
def self.generate_token_hash(user)
token = "#{user.username}.#{Time.now.to_i}.some_salt_string_for_more_security"
Digest::SHA1.hexdigest(token)
end
end
class UsersController < ApplicationController
def create
... # your code to create users
respond_to do |format|
format.html {...} # handling the normal html format
format.xml {...} # handle xml format requests
format.json {...} # handle json format requests
end
end
... # rest of your actions
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment