annotate vendor/plugins/open_id_authentication/README @ 8:0c83d98252d9 yuya

* Add custom repo prefix and proper auth realm, remove auth cache (seems like an unwise feature), pass DB handle around, various other bits of tidying
author Chris Cannam
date Thu, 12 Aug 2010 15:31:37 +0100
parents 513646585e45
children
rev   line source
Chris@0 1 OpenIdAuthentication
Chris@0 2 ====================
Chris@0 3
Chris@0 4 Provides a thin wrapper around the excellent ruby-openid gem from JanRan. Be sure to install that first:
Chris@0 5
Chris@0 6 gem install ruby-openid
Chris@0 7
Chris@0 8 To understand what OpenID is about and how it works, it helps to read the documentation for lib/openid/consumer.rb
Chris@0 9 from that gem.
Chris@0 10
Chris@0 11 The specification used is http://openid.net/specs/openid-authentication-2_0.html.
Chris@0 12
Chris@0 13
Chris@0 14 Prerequisites
Chris@0 15 =============
Chris@0 16
Chris@0 17 OpenID authentication uses the session, so be sure that you haven't turned that off. It also relies on a number of
Chris@0 18 database tables to store the authentication keys. So you'll have to run the migration to create these before you get started:
Chris@0 19
Chris@0 20 rake open_id_authentication:db:create
Chris@0 21
Chris@0 22 Or, use the included generators to install or upgrade:
Chris@0 23
Chris@0 24 ./script/generate open_id_authentication_tables MigrationName
Chris@0 25 ./script/generate upgrade_open_id_authentication_tables MigrationName
Chris@0 26
Chris@0 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:
Chris@0 28
Chris@0 29 OpenIdAuthentication.store = :file
Chris@0 30
Chris@0 31 This particular plugin also relies on the fact that the authentication action allows for both POST and GET operations.
Chris@0 32 If you're using RESTful authentication, you'll need to explicitly allow for this in your routes.rb.
Chris@0 33
Chris@0 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:
Chris@0 35
Chris@0 36 map.root :controller => 'articles'
Chris@0 37
Chris@0 38 This plugin relies on Rails Edge revision 6317 or newer.
Chris@0 39
Chris@0 40
Chris@0 41 Example
Chris@0 42 =======
Chris@0 43
Chris@0 44 This example is just to meant to demonstrate how you could use OpenID authentication. You might well want to add
Chris@0 45 salted hash logins instead of plain text passwords and other requirements on top of this. Treat it as a starting point,
Chris@0 46 not a destination.
Chris@0 47
Chris@0 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
Chris@0 49 model you are using for authentication.
Chris@0 50
Chris@0 51 Also of note is the following code block used in the example below:
Chris@0 52
Chris@0 53 authenticate_with_open_id do |result, identity_url|
Chris@0 54 ...
Chris@0 55 end
Chris@0 56
Chris@0 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' -
Chris@0 58 If you are storing just 'example.com' with your user, the lookup will fail.
Chris@0 59
Chris@0 60 There is a handy method in this plugin called 'normalize_url' that will help with validating OpenID URLs.
Chris@0 61
Chris@0 62 OpenIdAuthentication.normalize_url(user.identity_url)
Chris@0 63
Chris@0 64 The above will return a standardized version of the OpenID URL - the above called with 'example.com' will return 'http://example.com/'
Chris@0 65 It will also raise an InvalidOpenId exception if the URL is determined to not be valid.
Chris@0 66 Use the above code in your User model and validate OpenID URLs before saving them.
Chris@0 67
Chris@0 68 config/routes.rb
Chris@0 69
Chris@0 70 map.root :controller => 'articles'
Chris@0 71 map.resource :session
Chris@0 72
Chris@0 73
Chris@0 74 app/views/sessions/new.erb
Chris@0 75
Chris@0 76 <% form_tag(session_url) do %>
Chris@0 77 <p>
Chris@0 78 <label for="name">Username:</label>
Chris@0 79 <%= text_field_tag "name" %>
Chris@0 80 </p>
Chris@0 81
Chris@0 82 <p>
Chris@0 83 <label for="password">Password:</label>
Chris@0 84 <%= password_field_tag %>
Chris@0 85 </p>
Chris@0 86
Chris@0 87 <p>
Chris@0 88 ...or use:
Chris@0 89 </p>
Chris@0 90
Chris@0 91 <p>
Chris@0 92 <label for="openid_identifier">OpenID:</label>
Chris@0 93 <%= text_field_tag "openid_identifier" %>
Chris@0 94 </p>
Chris@0 95
Chris@0 96 <p>
Chris@0 97 <%= submit_tag 'Sign in', :disable_with => "Signing in&hellip;" %>
Chris@0 98 </p>
Chris@0 99 <% end %>
Chris@0 100
Chris@0 101 app/controllers/sessions_controller.rb
Chris@0 102 class SessionsController < ApplicationController
Chris@0 103 def create
Chris@0 104 if using_open_id?
Chris@0 105 open_id_authentication
Chris@0 106 else
Chris@0 107 password_authentication(params[:name], params[:password])
Chris@0 108 end
Chris@0 109 end
Chris@0 110
Chris@0 111
Chris@0 112 protected
Chris@0 113 def password_authentication(name, password)
Chris@0 114 if @current_user = @account.users.authenticate(params[:name], params[:password])
Chris@0 115 successful_login
Chris@0 116 else
Chris@0 117 failed_login "Sorry, that username/password doesn't work"
Chris@0 118 end
Chris@0 119 end
Chris@0 120
Chris@0 121 def open_id_authentication
Chris@0 122 authenticate_with_open_id do |result, identity_url|
Chris@0 123 if result.successful?
Chris@0 124 if @current_user = @account.users.find_by_identity_url(identity_url)
Chris@0 125 successful_login
Chris@0 126 else
Chris@0 127 failed_login "Sorry, no user by that identity URL exists (#{identity_url})"
Chris@0 128 end
Chris@0 129 else
Chris@0 130 failed_login result.message
Chris@0 131 end
Chris@0 132 end
Chris@0 133 end
Chris@0 134
Chris@0 135
Chris@0 136 private
Chris@0 137 def successful_login
Chris@0 138 session[:user_id] = @current_user.id
Chris@0 139 redirect_to(root_url)
Chris@0 140 end
Chris@0 141
Chris@0 142 def failed_login(message)
Chris@0 143 flash[:error] = message
Chris@0 144 redirect_to(new_session_url)
Chris@0 145 end
Chris@0 146 end
Chris@0 147
Chris@0 148
Chris@0 149
Chris@0 150 If you're fine with the result messages above and don't need individual logic on a per-failure basis,
Chris@0 151 you can collapse the case into a mere boolean:
Chris@0 152
Chris@0 153 def open_id_authentication
Chris@0 154 authenticate_with_open_id do |result, identity_url|
Chris@0 155 if result.successful? && @current_user = @account.users.find_by_identity_url(identity_url)
Chris@0 156 successful_login
Chris@0 157 else
Chris@0 158 failed_login(result.message || "Sorry, no user by that identity URL exists (#{identity_url})")
Chris@0 159 end
Chris@0 160 end
Chris@0 161 end
Chris@0 162
Chris@0 163
Chris@0 164 Simple Registration OpenID Extension
Chris@0 165 ====================================
Chris@0 166
Chris@0 167 Some OpenID Providers support this lightweight profile exchange protocol. See more: http://www.openidenabled.com/openid/simple-registration-extension
Chris@0 168
Chris@0 169 You can support it in your app by changing #open_id_authentication
Chris@0 170
Chris@0 171 def open_id_authentication(identity_url)
Chris@0 172 # Pass optional :required and :optional keys to specify what sreg fields you want.
Chris@0 173 # Be sure to yield registration, a third argument in the #authenticate_with_open_id block.
Chris@0 174 authenticate_with_open_id(identity_url,
Chris@0 175 :required => [ :nickname, :email ],
Chris@0 176 :optional => :fullname) do |result, identity_url, registration|
Chris@0 177 case result.status
Chris@0 178 when :missing
Chris@0 179 failed_login "Sorry, the OpenID server couldn't be found"
Chris@0 180 when :invalid
Chris@0 181 failed_login "Sorry, but this does not appear to be a valid OpenID"
Chris@0 182 when :canceled
Chris@0 183 failed_login "OpenID verification was canceled"
Chris@0 184 when :failed
Chris@0 185 failed_login "Sorry, the OpenID verification failed"
Chris@0 186 when :successful
Chris@0 187 if @current_user = @account.users.find_by_identity_url(identity_url)
Chris@0 188 assign_registration_attributes!(registration)
Chris@0 189
Chris@0 190 if current_user.save
Chris@0 191 successful_login
Chris@0 192 else
Chris@0 193 failed_login "Your OpenID profile registration failed: " +
Chris@0 194 @current_user.errors.full_messages.to_sentence
Chris@0 195 end
Chris@0 196 else
Chris@0 197 failed_login "Sorry, no user by that identity URL exists"
Chris@0 198 end
Chris@0 199 end
Chris@0 200 end
Chris@0 201 end
Chris@0 202
Chris@0 203 # registration is a hash containing the valid sreg keys given above
Chris@0 204 # use this to map them to fields of your user model
Chris@0 205 def assign_registration_attributes!(registration)
Chris@0 206 model_to_registration_mapping.each do |model_attribute, registration_attribute|
Chris@0 207 unless registration[registration_attribute].blank?
Chris@0 208 @current_user.send("#{model_attribute}=", registration[registration_attribute])
Chris@0 209 end
Chris@0 210 end
Chris@0 211 end
Chris@0 212
Chris@0 213 def model_to_registration_mapping
Chris@0 214 { :login => 'nickname', :email => 'email', :display_name => 'fullname' }
Chris@0 215 end
Chris@0 216
Chris@0 217 Attribute Exchange OpenID Extension
Chris@0 218 ===================================
Chris@0 219
Chris@0 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
Chris@0 221
Chris@0 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:
Chris@0 223
Chris@0 224 authenticate_with_open_id(identity_url,
Chris@0 225 :required => [ :email, 'http://schema.openid.net/birthDate' ]) do |result, identity_url, registration|
Chris@0 226
Chris@0 227 This would provide the sreg data for :email, and the AX data for 'http://schema.openid.net/birthDate'
Chris@0 228
Chris@0 229
Chris@0 230
Chris@0 231 Copyright (c) 2007 David Heinemeier Hansson, released under the MIT license