Merge branch 'master' into openid
[rails.git] / vendor / plugins / open_id_authentication / README
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