Skip to content

Instantly share code, notes, and snippets.

@cousine
Created November 15, 2010 13:43
Show Gist options
  • Save cousine/700365 to your computer and use it in GitHub Desktop.
Save cousine/700365 to your computer and use it in GitHub Desktop.
Sing Sign-in Solutions in Rails using Authentasaurus: The consumer
## Authentasaurus configuration
development: &non_production_settings
:hashing: "SHA2" # MD5 - SHA1 - SHA2
:mail:
:email: &development_email "foo_bar@your-domain.com"
:host: "http://localhost:3001/"
:modules:
:remote:
:user:
:site: "http://localhost:3000/application_name"
:session_element: "remote_sessions"
:sync: true
:sync_to: "user"
:recoverable:
:token_expires_after: 10 # days
:send_email: true
:mail_subject: "Reset your password on your-domain.com"
:mail_from: *development_email
:invitable:
:send_email: true
:mail_subject: "You've been invited to your-domain.com"
:mail_from: *development_email
:validatable:
:send_email: true
:mail_subject: "Validate your account on your-domain.com"
:mail_from: *development_email
#### Rails 3
authentasaurus_routes :authorization, :validation # Add authentasaurus routes
# For redirecting the user back to your application after the token is authenticated
# Token was invalid
match "/sessions/signin" => "sessions#callback", :as => "sessions_callback"
# Token is authenticated successfully and the provider returned the username and token
match "/sessions/signin/:username/:token" => "sessions#callback"
#### Rails 2.3.*
map.authentasaurus_routes :authorizable, :validatable # Add authentasaurus routes
# For redirecting the user back to your application after the token is authenticated
# Token was invalid
map.sessions_callback "/sessions/signin", :controller => :sessions, :action => :callback
# Token is authenticated successfully and the provider returned the username and token
map.connect "/sessions/signin/:username/:token", :controller => :sessions, :action => :callback
class Session
include Authentasaurus::Models::Session # include the default functionalities from Authentasaurus
attr_accessor :token
# Creates a new session object using the token
def save_token(*session_types)
return false if self.username.nil? || self.token.nil?
session_types = session_types.flatten
if session_types.empty?
session_types = [:user_sync]
end
ret = true
session_types.each do |type|
# Authenticate token using the user_sync object
@user = type.to_s.camelize.constantize.authenticate_token(self.username, self.token)
if @user.nil?
ret &= false
else
ret = true
break
end
end
ret
end
end
class SessionsController < ApplicationController
include Authentasaurus::SessionsController
before_filter :check_is_logged_in, :only => [:callback]
before_filter :authenticate_token, :only => [:new]
layout "login"
# Called after the provider has authenticated the saved token
# The provider responds to this url with a username and the token if the token is found and correct
# A session object is created from the input data and the data is authenticated with Session#save_remote
def callback
begin
@session = Session.new :username => params[:username], :token => params[:token]
respond_to do |format|
if @session.save_token
session[:user_id] = User.find_by_username(@session.user.username).try(:id)
user = current_user # get local user for authorization
# authorize ...
session[:user_permissions] = {:read => user.permissions.collect{|per| per.area.name if per.read}, :write => user.permissions.collect{|per| per.area.name if per.write}}
session[:provider_token] = params[:token]
format.html { redirect_to session[:original_url] || root_url }
else
@session = Session.new
format.html { render :new }
end
end
rescue ActiveResource::ForbiddenAccess
respond_to do |format|
@session = Session.new
format.html { render :new }
end
rescue ActiveResource::UnauthorizedAccess
respond_to do |format|
@session = Session.new
format.html { render :new }
end
rescue ActiveResource::MethodNotAllowed
respond_to do |format|
@session = Session.new
format.html { render :new }
end
rescue
respond_to do |format|
@session = Session.new
format.html { render :new }
end
end
end
# Creates a new session object by authenticating with the provider
def create
begin
@session = Session.new params[:session]
respond_to do |format|
if @session.save(UserSync)
# setup cookie for other applications
if RAILS_ENV == "production"
cookie_options = {
:value => @session.user.token,
:domain => 'your-provider-domain.com',
:secure => true,
:httponly => true
}
else
cookie_options = {
:value => @session.user.token,
:httponly => true
}
end
# create cookie
if @session.remember == "1"
cookies.signed.permanent[:provider_token] = cookie_options
else
cookies.signed[:provider_token] = cookie_options
end
session[:user_id] = User.find_by_username(@session.user.username).try(:id)
user = current_user # get local user for authorization
session[:user_permissions] = {:read => user.permissions.collect{|per| per.area.name if per.read}, :write => user.permissions.collect{|per| per.area.name if per.write}}
session[:provider_token] = @session.user.token
format.html { redirect_to session[:original_url] || root_url }
else
format.html { render :action => :new }
end
end
rescue ActiveResource::ForbiddenAccess
@session.errors.add_to_base "Invalid API key"
respond_to do |format|
format.html { render :new }
end
rescue ActiveResource::UnauthorizedAccess
@session.errors.add_to_base I18n.t(:invalid_login, :scope => [:authentasaurus, :messages, :sessions])
respond_to do |format|
format.html { render :new }
end
rescue ActiveResource::MethodNotAllowed
@session.errors.add_to_base "You are not subscribed in this application"
respond_to do |format|
format.html { render :new }
end
end
end
# Logout
def destroy
session[:user_id] = nil
session[:user_permissions] = nil
session[:provider_token] = nil
cookies.delete :remember_me_token
if RAILS_ENV == "production"
cookies.delete(:provider_token, :domain => 'your-provider-domain.com')
else
cookies.delete(:provider_token)
end
respond_to do |format|
format.html { redirect_to :action => :new }
end
end
private
# Redirect user to the provider to authenticate the saved token
def authenticate_token
unless is_logged_in?
redirect_to url_for("#{AUTHENTASAURUS[:modules][:remote][:user][:site]}/remote_sessions/authenticate_token") # In Rails 2.3.*
redirect_to url_for("#{Rails.application.config.authentasaurus[:modules][:remote][:user][:site]}/remote_sessions/authenticate_token") # In Rails 3
end
end
end
class User < ActiveRecord::Base
authenticatable :strong_password, :validatable # Include default functions from Authentasaurus
@default_data = {
# we set the default group_id here to bypass the data incoming from the provider
# or to set default data for attributes that are not on the provider
:group_id => 2
}
# we dont need to save the token but we need to store it in the object
attr_accessor :provider_token
end
class UserSync < ActiveResource::Base
authenticatable # include the original functionality from Authentasaurus
# Authenticates the username and token with the provider
def self.authenticate_token(username, token)
req_body=self.post(:signin_token,:username => username, :token => token).body
case(self.format)
when ActiveResource::Formats::XmlFormat
user = self.new Hash.from_xml(req_body).values.first
when ActiveResource::Formats::JsonFormat
user = self.new ActiveSupport::JSON.decode(req_body)
else
user = self.new Hash.from_xml(req_body).values.first
end
# Caching the user
unless user.nil?
if self.sync && !self.sync_to.nil?
last_update = user.attributes.delete "updated_at"
local_user = self.sync_to.find_or_initialize_by_username user.username, user.attributes
unless local_user.new_record?
last_update_datetime = (last_update.kind_of?(String)) ? (DateTime.parse(last_update)) : (last_update)
if local_user.updated_at < last_update_datetime
local_user.update_attributes user.attributes
end
else
local_user.save
end
end
end
return user
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment