You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							223 lines
						
					
					
						
							8.0 KiB
						
					
					
				
			
		
		
	
	
							223 lines
						
					
					
						
							8.0 KiB
						
					
					
				OpenIdAuthentication
 | 
						|
====================
 | 
						|
 | 
						|
Provides a thin wrapper around the excellent ruby-openid gem from JanRan. Be sure to install that first:
 | 
						|
 | 
						|
  gem install ruby-openid
 | 
						|
 | 
						|
To understand what OpenID is about and how it works, it helps to read the documentation for lib/openid/consumer.rb
 | 
						|
from that gem.
 | 
						|
 | 
						|
The specification used is http://openid.net/specs/openid-authentication-2_0.html.
 | 
						|
 | 
						|
 | 
						|
Prerequisites
 | 
						|
=============
 | 
						|
 | 
						|
OpenID authentication uses the session, so be sure that you haven't turned that off.
 | 
						|
 | 
						|
Alternatively, you can use the file-based store, which just relies on on tmp/openids being present in RAILS_ROOT. But be aware that this store only works if you have a single application server. And it's not safe to use across NFS. It's recommended that you use the database store if at all possible. To use the file-based store, you'll also have to add this line to your config/environment.rb:
 | 
						|
 | 
						|
  OpenIdAuthentication.store = :file
 | 
						|
 | 
						|
This particular plugin also relies on the fact that the authentication action allows for both POST and GET operations.
 | 
						|
If you're using RESTful authentication, you'll need to explicitly allow for this in your routes.rb.
 | 
						|
 | 
						|
The plugin also expects to find a root_url method that points to the home page of your site. You can accomplish this by using a root route in config/routes.rb:
 | 
						|
 | 
						|
  map.root :controller => 'articles'
 | 
						|
 | 
						|
This plugin relies on Rails Edge revision 6317 or newer.
 | 
						|
 | 
						|
 | 
						|
Example
 | 
						|
=======
 | 
						|
 | 
						|
This example is just to meant to demonstrate how you could use OpenID authentication. You might well want to add
 | 
						|
salted hash logins instead of plain text passwords and other requirements on top of this. Treat it as a starting point,
 | 
						|
not a destination.
 | 
						|
 | 
						|
Note that the User model referenced in the simple example below has an 'identity_url' attribute. You will want to add the same or similar field to whatever
 | 
						|
model you are using for authentication.
 | 
						|
 | 
						|
Also of note is the following code block used in the example below:
 | 
						|
 | 
						|
  authenticate_with_open_id do |result, identity_url|
 | 
						|
    ...
 | 
						|
  end
 | 
						|
 | 
						|
In the above code block, 'identity_url' will need to match user.identity_url exactly. 'identity_url' will be a string in the form of 'http://example.com' -
 | 
						|
If you are storing just 'example.com' with your user, the lookup will fail.
 | 
						|
 | 
						|
There is a handy method in this plugin called 'normalize_url' that will help with validating OpenID URLs.
 | 
						|
 | 
						|
  OpenIdAuthentication.normalize_url(user.identity_url)
 | 
						|
 | 
						|
The above will return a standardized version of the OpenID URL - the above called with 'example.com' will return 'http://example.com/'
 | 
						|
It will also raise an InvalidOpenId exception if the URL is determined to not be valid.
 | 
						|
Use the above code in your User model and validate OpenID URLs before saving them.
 | 
						|
 | 
						|
config/routes.rb
 | 
						|
 | 
						|
  map.root :controller => 'articles'
 | 
						|
  map.resource :session
 | 
						|
 | 
						|
 | 
						|
app/views/sessions/new.erb
 | 
						|
 | 
						|
  <% form_tag(session_url) do %>
 | 
						|
    <p>
 | 
						|
      <label for="name">Username:</label>
 | 
						|
      <%= text_field_tag "name" %>
 | 
						|
    </p>
 | 
						|
 | 
						|
    <p>
 | 
						|
      <label for="password">Password:</label>
 | 
						|
      <%= password_field_tag %>
 | 
						|
    </p>
 | 
						|
 | 
						|
    <p>
 | 
						|
      ...or use:
 | 
						|
    </p>
 | 
						|
 | 
						|
    <p>
 | 
						|
      <label for="openid_identifier">OpenID:</label>
 | 
						|
      <%= text_field_tag "openid_identifier" %>
 | 
						|
    </p>
 | 
						|
 | 
						|
    <p>
 | 
						|
      <%= submit_tag 'Sign in', :disable_with => "Signing in…" %>
 | 
						|
    </p>
 | 
						|
  <% end %>
 | 
						|
 | 
						|
app/controllers/sessions_controller.rb
 | 
						|
  class SessionsController < ApplicationController
 | 
						|
    def create
 | 
						|
      if using_open_id?
 | 
						|
        open_id_authentication
 | 
						|
      else
 | 
						|
        password_authentication(params[:name], params[:password])
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
 | 
						|
    protected
 | 
						|
      def password_authentication(name, password)
 | 
						|
        if @current_user = @account.users.authenticate(params[:name], params[:password])
 | 
						|
          successful_login
 | 
						|
        else
 | 
						|
          failed_login "Sorry, that username/password doesn't work"
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      def open_id_authentication
 | 
						|
        authenticate_with_open_id do |result, identity_url|
 | 
						|
          if result.successful?
 | 
						|
            if @current_user = @account.users.find_by_identity_url(identity_url)
 | 
						|
              successful_login
 | 
						|
            else
 | 
						|
              failed_login "Sorry, no user by that identity URL exists (#{identity_url})"
 | 
						|
            end
 | 
						|
          else
 | 
						|
            failed_login result.message
 | 
						|
          end
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
 | 
						|
    private
 | 
						|
      def successful_login
 | 
						|
        session[:user_id] = @current_user.id
 | 
						|
        redirect_to(root_url)
 | 
						|
      end
 | 
						|
 | 
						|
      def failed_login(message)
 | 
						|
        flash[:error] = message
 | 
						|
        redirect_to(new_session_url)
 | 
						|
      end
 | 
						|
  end
 | 
						|
 | 
						|
 | 
						|
 | 
						|
If you're fine with the result messages above and don't need individual logic on a per-failure basis,
 | 
						|
you can collapse the case into a mere boolean:
 | 
						|
 | 
						|
    def open_id_authentication
 | 
						|
      authenticate_with_open_id do |result, identity_url|
 | 
						|
        if result.successful? && @current_user = @account.users.find_by_identity_url(identity_url)
 | 
						|
          successful_login
 | 
						|
        else
 | 
						|
          failed_login(result.message || "Sorry, no user by that identity URL exists (#{identity_url})")
 | 
						|
        end
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
 | 
						|
Simple Registration OpenID Extension
 | 
						|
====================================
 | 
						|
 | 
						|
Some OpenID Providers support this lightweight profile exchange protocol.  See more: http://www.openidenabled.com/openid/simple-registration-extension
 | 
						|
 | 
						|
You can support it in your app by changing #open_id_authentication
 | 
						|
 | 
						|
      def open_id_authentication(identity_url)
 | 
						|
        # Pass optional :required and :optional keys to specify what sreg fields you want.
 | 
						|
        # Be sure to yield registration, a third argument in the #authenticate_with_open_id block.
 | 
						|
        authenticate_with_open_id(identity_url,
 | 
						|
            :required => [ :nickname, :email ],
 | 
						|
            :optional => :fullname) do |result, identity_url, registration|
 | 
						|
          case result.status
 | 
						|
          when :missing
 | 
						|
            failed_login "Sorry, the OpenID server couldn't be found"
 | 
						|
          when :invalid
 | 
						|
            failed_login "Sorry, but this does not appear to be a valid OpenID"
 | 
						|
          when :canceled
 | 
						|
            failed_login "OpenID verification was canceled"
 | 
						|
          when :failed
 | 
						|
            failed_login "Sorry, the OpenID verification failed"
 | 
						|
          when :successful
 | 
						|
            if @current_user = @account.users.find_by_identity_url(identity_url)
 | 
						|
              assign_registration_attributes!(registration)
 | 
						|
 | 
						|
              if current_user.save
 | 
						|
                successful_login
 | 
						|
              else
 | 
						|
                failed_login "Your OpenID profile registration failed: " +
 | 
						|
                  @current_user.errors.full_messages.to_sentence
 | 
						|
              end
 | 
						|
            else
 | 
						|
              failed_login "Sorry, no user by that identity URL exists"
 | 
						|
            end
 | 
						|
          end
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      # registration is a hash containing the valid sreg keys given above
 | 
						|
      # use this to map them to fields of your user model
 | 
						|
      def assign_registration_attributes!(registration)
 | 
						|
        model_to_registration_mapping.each do |model_attribute, registration_attribute|
 | 
						|
          unless registration[registration_attribute].blank?
 | 
						|
            @current_user.send("#{model_attribute}=", registration[registration_attribute])
 | 
						|
          end
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      def model_to_registration_mapping
 | 
						|
        { :login => 'nickname', :email => 'email', :display_name => 'fullname' }
 | 
						|
      end
 | 
						|
 | 
						|
Attribute Exchange OpenID Extension
 | 
						|
===================================
 | 
						|
 | 
						|
Some OpenID providers also support the OpenID AX (attribute exchange) protocol for exchanging identity information between endpoints.  See more: http://openid.net/specs/openid-attribute-exchange-1_0.html
 | 
						|
 | 
						|
Accessing AX data is very similar to the Simple Registration process, described above -- just add the URI identifier for the AX field to your :optional or :required parameters.  For example:
 | 
						|
 | 
						|
        authenticate_with_open_id(identity_url,
 | 
						|
            :required => [ :email, 'http://schema.openid.net/birthDate' ]) do |result, identity_url, registration|
 | 
						|
 | 
						|
This would provide the sreg data for :email, and the AX data for 'http://schema.openid.net/birthDate'
 | 
						|
 | 
						|
 | 
						|
 | 
						|
Copyright (c) 2007 David Heinemeier Hansson, released under the MIT license |