open_id_authentication 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,37 @@
1
+ * Dump heavy lifting off to rack-openid gem. OpenIdAuthentication is just a simple controller concern.
2
+
3
+ * Fake HTTP method from OpenID server since they only support a GET. Eliminates the need to set an extra route to match the server's reply. [Josh Peek]
4
+
5
+ * OpenID 2.0 recommends that forms should use the field name "openid_identifier" rather than "openid_url" [Josh Peek]
6
+
7
+ * Return open_id_response.display_identifier to the application instead of .endpoints.claimed_id. [nbibler]
8
+
9
+ * Add Timeout protection [Rick]
10
+
11
+ * An invalid identity url passed through authenticate_with_open_id will no longer raise an InvalidOpenId exception. Instead it will return Result[:missing] to the completion block.
12
+
13
+ * Allow a return_to option to be used instead of the requested url [Josh Peek]
14
+
15
+ * Updated plugin to use Ruby OpenID 2.x.x [Josh Peek]
16
+
17
+ * Tied plugin to ruby-openid 1.1.4 gem until we can make it compatible with 2.x [DHH]
18
+
19
+ * Use URI instead of regexps to normalize the URL and gain free, better matching #8136 [dkubb]
20
+
21
+ * Allow -'s in #normalize_url [Rick]
22
+
23
+ * remove instance of mattr_accessor, it was breaking tests since they don't load ActiveSupport. Fix Timeout test [Rick]
24
+
25
+ * Throw a InvalidOpenId exception instead of just a RuntimeError when the URL can't be normalized [DHH]
26
+
27
+ * Just use the path for the return URL, so extra query parameters don't interfere [DHH]
28
+
29
+ * Added a new default database-backed store after experiencing trouble with the filestore on NFS. The file store is still available as an option [DHH]
30
+
31
+ * Added normalize_url and applied it to all operations going through the plugin [DHH]
32
+
33
+ * Removed open_id? as the idea of using the same input box for both OpenID and username has died -- use using_open_id? instead (which checks for the presence of params[:openid_url] by default) [DHH]
34
+
35
+ * Added OpenIdAuthentication::Result to make it easier to deal with default situations where you don't care to do something particular for each error state [DHH]
36
+
37
+ * Stop relying on root_url being defined, we can just grab the current url instead [DHH]
@@ -0,0 +1,8 @@
1
+ Contributing to open_id_authentication:
2
+
3
+ 1. Fork the [official repository](http://github.com/Velir/open_id_authentication/tree/master).
4
+ 2. Make your changes in a topic branch.
5
+ 3. Send a pull request.
6
+
7
+ Notes:
8
+ * Please don't update the Gem version.
data/README.md ADDED
@@ -0,0 +1,242 @@
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.
18
+
19
+ 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:
20
+
21
+ OpenIdAuthentication.store = :file
22
+
23
+ This particular plugin also relies on the fact that the authentication action allows for both POST and GET operations.
24
+ If you're using RESTful authentication, you'll need to explicitly allow for this in your routes.rb.
25
+
26
+ 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:
27
+
28
+ root :to => "articles#index"
29
+
30
+ This plugin relies on Rails Edge revision 6317 or newer.
31
+
32
+
33
+ Example
34
+ -------
35
+
36
+ This example is just to meant to demonstrate how you could use OpenID authentication. You might well want to add
37
+ salted hash logins instead of plain text passwords and other requirements on top of this. Treat it as a starting point,
38
+ not a destination.
39
+
40
+ 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
41
+ model you are using for authentication.
42
+
43
+ Also of note is the following code block used in the example below:
44
+
45
+ authenticate_with_open_id do |result, identity_url|
46
+ ...
47
+ end
48
+
49
+ 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' -
50
+ If you are storing just 'example.com' with your user, the lookup will fail.
51
+
52
+ There is a handy method in this plugin called 'normalize_url' that will help with validating OpenID URLs.
53
+
54
+ OpenIdAuthentication.normalize_url(user.identity_url)
55
+
56
+ The above will return a standardized version of the OpenID URL - the above called with 'example.com' will return 'http://example.com/'
57
+ It will also raise an InvalidOpenId exception if the URL is determined to not be valid.
58
+ Use the above code in your User model and validate OpenID URLs before saving them.
59
+
60
+ config/routes.rb
61
+
62
+ #config/routes.rb
63
+ root :to => "articles#index"
64
+ resource :session
65
+
66
+ app/views/sessions/new.erb
67
+
68
+ #app/views/sessions/new.erb
69
+ <% form_tag(session_url) do %>
70
+ <p>
71
+ <label for="name">Username:</label>
72
+ <%= text_field_tag "name" %>
73
+ </p>
74
+
75
+ <p>
76
+ <label for="password">Password:</label>
77
+ <%= password_field_tag %>
78
+ </p>
79
+
80
+ <p>
81
+ <!-- ...or use: -->
82
+ </p>
83
+
84
+ <p>
85
+ <label for="openid_identifier">OpenID:</label>
86
+ <%= text_field_tag "openid_identifier" %>
87
+ </p>
88
+
89
+ <p>
90
+ <%= submit_tag 'Sign in', :disable_with => "Signing in&hellip;" %>
91
+ </p>
92
+ <% end %>
93
+
94
+
95
+ app/controllers/sessions_controller.rb
96
+
97
+ #app/controllers/sessions_controller.rb
98
+ class SessionsController < ApplicationController
99
+ def create
100
+ if using_open_id?
101
+ open_id_authentication
102
+ else
103
+ password_authentication(params[:name], params[:password])
104
+ end
105
+ end
106
+
107
+
108
+ protected
109
+ def password_authentication(name, password)
110
+ if @current_user = @account.users.authenticate(params[:name], params[:password])
111
+ successful_login
112
+ else
113
+ failed_login "Sorry, that username/password doesn't work"
114
+ end
115
+ end
116
+
117
+ def open_id_authentication
118
+ authenticate_with_open_id do |result, identity_url|
119
+ if result.successful?
120
+ if @current_user = @account.users.find_by_identity_url(identity_url)
121
+ successful_login
122
+ else
123
+ failed_login "Sorry, no user by that identity URL exists (#{identity_url})"
124
+ end
125
+ else
126
+ failed_login result.message
127
+ end
128
+ end
129
+ end
130
+
131
+
132
+ private
133
+ def successful_login
134
+ session[:user_id] = @current_user.id
135
+ redirect_to(root_url)
136
+ end
137
+
138
+ def failed_login(message)
139
+ flash[:error] = message
140
+ redirect_to(new_session_url)
141
+ end
142
+ end
143
+
144
+
145
+
146
+ If you're fine with the result messages above and don't need individual logic on a per-failure basis,
147
+ you can collapse the case into a mere boolean:
148
+
149
+ def open_id_authentication
150
+ authenticate_with_open_id do |result, identity_url|
151
+ if result.successful? && @current_user = @account.users.find_by_identity_url(identity_url)
152
+ successful_login
153
+ else
154
+ failed_login(result.message || "Sorry, no user by that identity URL exists (#{identity_url})")
155
+ end
156
+ end
157
+ end
158
+
159
+
160
+ Simple Registration OpenID Extension
161
+ ------------------------------------
162
+
163
+ Some OpenID Providers support this lightweight profile exchange protocol. See more: http://www.openidenabled.com/openid/simple-registration-extension
164
+
165
+ You can support it in your app by changing #open_id_authentication
166
+
167
+ def open_id_authentication(identity_url)
168
+ # Pass optional :required and :optional keys to specify what sreg fields you want.
169
+ # Be sure to yield registration, a third argument in the
170
+ # #authenticate_with_open_id block.
171
+ authenticate_with_open_id(identity_url,
172
+ :required => [ :nickname, :email ],
173
+ :optional => :fullname) do |result, identity_url, registration|
174
+ case result.status
175
+ when :missing
176
+ failed_login "Sorry, the OpenID server couldn't be found"
177
+ when :invalid
178
+ failed_login "Sorry, but this does not appear to be a valid OpenID"
179
+ when :canceled
180
+ failed_login "OpenID verification was canceled"
181
+ when :failed
182
+ failed_login "Sorry, the OpenID verification failed"
183
+ when :successful
184
+ if @current_user = @account.users.find_by_identity_url(identity_url)
185
+ assign_registration_attributes!(registration)
186
+
187
+ if current_user.save
188
+ successful_login
189
+ else
190
+ failed_login "Your OpenID profile registration failed: " +
191
+ @current_user.errors.full_messages.to_sentence
192
+ end
193
+ else
194
+ failed_login "Sorry, no user by that identity URL exists"
195
+ end
196
+ end
197
+ end
198
+ end
199
+
200
+ # registration is a hash containing the valid sreg keys given above
201
+ # use this to map them to fields of your user model
202
+ def assign_registration_attributes!(registration)
203
+ model_to_registration_mapping.each do |model_attribute, registration_attribute|
204
+ unless registration[registration_attribute].blank?
205
+ @current_user.send("#{model_attribute}=", registration[registration_attribute])
206
+ end
207
+ end
208
+ end
209
+
210
+ def model_to_registration_mapping
211
+ { :login => 'nickname', :email => 'email', :display_name => 'fullname' }
212
+ end
213
+
214
+ Attribute Exchange OpenID Extension
215
+ -----------------------------------
216
+
217
+ 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
218
+
219
+ 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:
220
+
221
+ authenticate_with_open_id(identity_url,
222
+ :required => [ :email, 'http://schema.openid.net/birthDate' ]) do
223
+ |result, identity_url, registration, ax|
224
+
225
+ This would provide the sreg data for :email via registration, and the AX data for http://schema.openid.net/birthDate via ax.
226
+
227
+ Contributing
228
+ ------------
229
+
230
+ Please see the [contribution guidelines](http://github.com/Velir/open_id_authentication/blob/master/CONTRIBUTION_GUIDELINES.md).
231
+
232
+ Credits
233
+ -------
234
+
235
+ open_id_authentication was written by David Heinemeier Hansson with a number of other [contributors](https://github.com/Velir/open_id_authentication/contributors).
236
+
237
+ open_id_authentication maintenance is funded by [Velir](http://velir.com).
238
+
239
+
240
+ License
241
+ -------
242
+ Copyright (c) 2007-2011 David Heinemeier Hansson, released under the MIT license
@@ -0,0 +1,4 @@
1
+ module OpenIdAuthentication
2
+ VERSION = "1.0.0"
3
+ end
4
+
@@ -0,0 +1,129 @@
1
+ require 'uri'
2
+ require 'openid'
3
+ require 'rack/openid'
4
+
5
+ module OpenIdAuthentication
6
+ def self.new(app)
7
+ store = OpenIdAuthentication.store
8
+ if store.nil?
9
+ Rails.logger.warn "OpenIdAuthentication.store is nil. Using in-memory store."
10
+ end
11
+
12
+ ::Rack::OpenID.new(app, OpenIdAuthentication.store)
13
+ end
14
+
15
+ def self.store
16
+ @@store
17
+ end
18
+
19
+ def self.store=(*store_option)
20
+ store, *parameters = *([ store_option ].flatten)
21
+
22
+ @@store = case store
23
+ when :memory
24
+ require 'openid/store/memory'
25
+ OpenID::Store::Memory.new
26
+ when :file
27
+ require 'openid/store/filesystem'
28
+ OpenID::Store::Filesystem.new(Rails.root.join('tmp/openids'))
29
+ when :memcache
30
+ require 'memcache'
31
+ require 'openid/store/memcache'
32
+ OpenID::Store::Memcache.new(MemCache.new(parameters))
33
+ else
34
+ store
35
+ end
36
+ end
37
+
38
+ self.store = nil
39
+
40
+ class Result
41
+ ERROR_MESSAGES = {
42
+ :missing => "Sorry, the OpenID server couldn't be found",
43
+ :invalid => "Sorry, but this does not appear to be a valid OpenID",
44
+ :canceled => "OpenID verification was canceled",
45
+ :failed => "OpenID verification failed",
46
+ :setup_needed => "OpenID verification needs setup"
47
+ }
48
+
49
+ def self.[](code)
50
+ new(code)
51
+ end
52
+
53
+ def initialize(code)
54
+ @code = code
55
+ end
56
+
57
+ def status
58
+ @code
59
+ end
60
+
61
+ ERROR_MESSAGES.keys.each { |state| define_method("#{state}?") { @code == state } }
62
+
63
+ def successful?
64
+ @code == :successful
65
+ end
66
+
67
+ def unsuccessful?
68
+ ERROR_MESSAGES.keys.include?(@code)
69
+ end
70
+
71
+ def message
72
+ ERROR_MESSAGES[@code]
73
+ end
74
+ end
75
+
76
+ protected
77
+ # The parameter name of "openid_identifier" is used rather than
78
+ # the Rails convention "open_id_identifier" because that's what
79
+ # the specification dictates in order to get browser auto-complete
80
+ # working across sites
81
+ def using_open_id?(identifier = nil) #:doc:
82
+ identifier ||= open_id_identifier
83
+ !identifier.blank? || request.env[Rack::OpenID::RESPONSE]
84
+ end
85
+
86
+ def authenticate_with_open_id(identifier = nil, options = {}, &block) #:doc:
87
+ identifier ||= open_id_identifier
88
+
89
+ if request.env[Rack::OpenID::RESPONSE]
90
+ complete_open_id_authentication(&block)
91
+ else
92
+ begin_open_id_authentication(identifier, options, &block)
93
+ end
94
+ end
95
+
96
+ private
97
+ def open_id_identifier
98
+ params[:openid_identifier] || params[:openid_url]
99
+ end
100
+
101
+ def begin_open_id_authentication(identifier, options = {})
102
+ options[:identifier] = identifier
103
+ value = Rack::OpenID.build_header(options)
104
+ response.headers[Rack::OpenID::AUTHENTICATE_HEADER] = value
105
+ head :unauthorized
106
+ end
107
+
108
+ def complete_open_id_authentication
109
+ response = request.env[Rack::OpenID::RESPONSE]
110
+ identifier = response.display_identifier
111
+
112
+ case response.status
113
+ when OpenID::Consumer::SUCCESS
114
+ yield Result[:successful], identifier,
115
+ OpenID::SReg::Response.from_success_response(response),
116
+ OpenID::AX::FetchResponse.from_success_response(response)
117
+ when :missing
118
+ yield Result[:missing], identifier, nil
119
+ when :invalid
120
+ yield Result[:invalid], identifier, nil
121
+ when OpenID::Consumer::CANCEL
122
+ yield Result[:canceled], identifier, nil
123
+ when OpenID::Consumer::FAILURE
124
+ yield Result[:failed], identifier, nil
125
+ when OpenID::Consumer::SETUP_NEEDED
126
+ yield Result[:setup_needed], response.setup_url, nil
127
+ end
128
+ end
129
+ end
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: open_id_authentication
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 1.0.0
6
+ platform: ruby
7
+ authors:
8
+ - Patrick Robertson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-08-01 00:00:00 -04:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rack-openid
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ~>
23
+ - !ruby/object:Gem::Version
24
+ version: "1.3"
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ description: |-
28
+ open_id_authentication provides a thin wrapper around the excellent rack-openid
29
+ gem.
30
+ email: patrick.robertson@velir.com
31
+ executables: []
32
+
33
+ extensions: []
34
+
35
+ extra_rdoc_files: []
36
+
37
+ files:
38
+ - CHANGELOG
39
+ - CONTRIBUTION_GUIDELINES.md
40
+ - README.md
41
+ - lib/open_id_authentication/version.rb
42
+ - lib/open_id_authentication.rb
43
+ has_rdoc: true
44
+ homepage: https://github.com/Velir/open_id_authentication
45
+ licenses: []
46
+
47
+ post_install_message:
48
+ rdoc_options: []
49
+
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
64
+ requirements: []
65
+
66
+ rubyforge_project:
67
+ rubygems_version: 1.6.2
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: open_id_authentication provides a thin wrapper around the excellent rack-openid gem.
71
+ test_files: []
72
+