To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / .svn / pristine / b4 / b49f8e8955f20d373ff50965f6541cbad8b508b5.svn-base @ 1297:0a574315af3e

History | View | Annotate | Download (8.36 KB)

1
OpenIdAuthentication
2
====================
3

    
4
Provides a thin wrapper around the excellent ruby-openid gem from JanRan. Be sure to install that first:
5

    
6
  gem install ruby-openid
7

    
8
To understand what OpenID is about and how it works, it helps to read the documentation for lib/openid/consumer.rb
9
from that gem.
10

    
11
The specification used is http://openid.net/specs/openid-authentication-2_0.html.
12

    
13

    
14
Prerequisites
15
=============
16

    
17
OpenID authentication uses the session, so be sure that you haven't turned that off. It also relies on a number of
18
database tables to store the authentication keys. So you'll have to run the migration to create these before you get started:
19

    
20
  rake open_id_authentication:db:create
21

    
22
Or, use the included generators to install or upgrade:
23

    
24
  ./script/generate open_id_authentication_tables MigrationName
25
  ./script/generate upgrade_open_id_authentication_tables MigrationName
26

    
27
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:
28

    
29
  OpenIdAuthentication.store = :file
30

    
31
This particular plugin also relies on the fact that the authentication action allows for both POST and GET operations.
32
If you're using RESTful authentication, you'll need to explicitly allow for this in your routes.rb. 
33

    
34
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:
35

    
36
  map.root :controller => 'articles'
37

    
38
This plugin relies on Rails Edge revision 6317 or newer.
39

    
40

    
41
Example
42
=======
43

    
44
This example is just to meant to demonstrate how you could use OpenID authentication. You might well want to add
45
salted hash logins instead of plain text passwords and other requirements on top of this. Treat it as a starting point,
46
not a destination.
47

    
48
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
49
model you are using for authentication.
50

    
51
Also of note is the following code block used in the example below:
52

    
53
  authenticate_with_open_id do |result, identity_url|
54
    ...
55
  end
56
  
57
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' -
58
If you are storing just 'example.com' with your user, the lookup will fail.
59

    
60
There is a handy method in this plugin called 'normalize_url' that will help with validating OpenID URLs.
61

    
62
  OpenIdAuthentication.normalize_url(user.identity_url)
63

    
64
The above will return a standardized version of the OpenID URL - the above called with 'example.com' will return 'http://example.com/'
65
It will also raise an InvalidOpenId exception if the URL is determined to not be valid.
66
Use the above code in your User model and validate OpenID URLs before saving them.
67

    
68
config/routes.rb
69

    
70
  map.root :controller => 'articles'
71
  map.resource :session
72

    
73

    
74
app/views/sessions/new.erb
75

    
76
  <% form_tag(session_url) do %>
77
    <p>
78
      <label for="name">Username:</label>
79
      <%= text_field_tag "name" %>
80
    </p>
81

    
82
    <p>
83
      <label for="password">Password:</label>
84
      <%= password_field_tag %>
85
    </p>
86

    
87
    <p>
88
      ...or use:
89
    </p>
90

    
91
    <p>
92
      <label for="openid_identifier">OpenID:</label>
93
      <%= text_field_tag "openid_identifier" %>
94
    </p>
95

    
96
    <p>
97
      <%= submit_tag 'Sign in', :disable_with => "Signing in&hellip;" %>
98
    </p>
99
  <% end %>
100

    
101
app/controllers/sessions_controller.rb
102
  class SessionsController < ApplicationController
103
    def create
104
      if using_open_id?
105
        open_id_authentication
106
      else
107
        password_authentication(params[:name], params[:password])
108
      end
109
    end
110

    
111

    
112
    protected
113
      def password_authentication(name, password)
114
        if @current_user = @account.users.authenticate(params[:name], params[:password])
115
          successful_login
116
        else
117
          failed_login "Sorry, that username/password doesn't work"
118
        end
119
      end
120

    
121
      def open_id_authentication
122
        authenticate_with_open_id do |result, identity_url|
123
          if result.successful?
124
            if @current_user = @account.users.find_by_identity_url(identity_url)
125
              successful_login
126
            else
127
              failed_login "Sorry, no user by that identity URL exists (#{identity_url})"
128
            end
129
          else
130
            failed_login result.message
131
          end
132
        end
133
      end
134
    
135
    
136
    private
137
      def successful_login
138
        session[:user_id] = @current_user.id
139
        redirect_to(root_url)
140
      end
141

    
142
      def failed_login(message)
143
        flash[:error] = message
144
        redirect_to(new_session_url)
145
      end
146
  end
147

    
148

    
149

    
150
If you're fine with the result messages above and don't need individual logic on a per-failure basis,
151
you can collapse the case into a mere boolean:
152

    
153
    def open_id_authentication
154
      authenticate_with_open_id do |result, identity_url|
155
        if result.successful? && @current_user = @account.users.find_by_identity_url(identity_url)
156
          successful_login
157
        else
158
          failed_login(result.message || "Sorry, no user by that identity URL exists (#{identity_url})")
159
        end
160
      end
161
    end
162

    
163

    
164
Simple Registration OpenID Extension
165
====================================
166

    
167
Some OpenID Providers support this lightweight profile exchange protocol.  See more: http://www.openidenabled.com/openid/simple-registration-extension
168

    
169
You can support it in your app by changing #open_id_authentication
170

    
171
      def open_id_authentication(identity_url)
172
        # Pass optional :required and :optional keys to specify what sreg fields you want.
173
        # Be sure to yield registration, a third argument in the #authenticate_with_open_id block.
174
        authenticate_with_open_id(identity_url, 
175
            :required => [ :nickname, :email ],
176
            :optional => :fullname) do |result, identity_url, registration|
177
          case result.status
178
          when :missing
179
            failed_login "Sorry, the OpenID server couldn't be found"
180
          when :invalid
181
            failed_login "Sorry, but this does not appear to be a valid OpenID"
182
          when :canceled
183
            failed_login "OpenID verification was canceled"
184
          when :failed
185
            failed_login "Sorry, the OpenID verification failed"
186
          when :successful
187
            if @current_user = @account.users.find_by_identity_url(identity_url)
188
              assign_registration_attributes!(registration)
189

    
190
              if current_user.save
191
                successful_login
192
              else
193
                failed_login "Your OpenID profile registration failed: " +
194
                  @current_user.errors.full_messages.to_sentence
195
              end
196
            else
197
              failed_login "Sorry, no user by that identity URL exists"
198
            end
199
          end
200
        end
201
      end
202
      
203
      # registration is a hash containing the valid sreg keys given above
204
      # use this to map them to fields of your user model
205
      def assign_registration_attributes!(registration)
206
        model_to_registration_mapping.each do |model_attribute, registration_attribute|
207
          unless registration[registration_attribute].blank?
208
            @current_user.send("#{model_attribute}=", registration[registration_attribute])
209
          end
210
        end
211
      end
212

    
213
      def model_to_registration_mapping
214
        { :login => 'nickname', :email => 'email', :display_name => 'fullname' }
215
      end
216

    
217
Attribute Exchange OpenID Extension
218
===================================
219

    
220
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
221

    
222
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:
223

    
224
        authenticate_with_open_id(identity_url, 
225
            :required => [ :email, 'http://schema.openid.net/birthDate' ]) do |result, identity_url, registration|
226
      
227
This would provide the sreg data for :email, and the AX data for 'http://schema.openid.net/birthDate'
228

    
229

    
230

    
231
Copyright (c) 2007 David Heinemeier Hansson, released under the MIT license