ruby-openid 1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. data/COPYING +21 -0
  2. data/INSTALL +34 -0
  3. data/README +67 -0
  4. data/TODO +9 -0
  5. data/examples/README +54 -0
  6. data/examples/cacert.pem +7815 -0
  7. data/examples/consumer.rb +285 -0
  8. data/examples/openid-store/associations/http-localhost_3A3000_2Fserver-EMQbAy3NnHVzA.s0u5KAcplKGzo +6 -0
  9. data/examples/openid-store/auth_key +1 -0
  10. data/examples/rails_active_record_store/README +59 -0
  11. data/examples/rails_active_record_store/XX_add_openidstore.rb +30 -0
  12. data/examples/rails_active_record_store/models/openid_association.rb +12 -0
  13. data/examples/rails_active_record_store/models/openid_nonce.rb +3 -0
  14. data/examples/rails_active_record_store/models/openid_setting.rb +2 -0
  15. data/examples/rails_active_record_store/openid_helper.rb +91 -0
  16. data/examples/rails_active_record_store/openidstore_test.rb +15 -0
  17. data/examples/rails_active_record_store/schema.mysql.sql +22 -0
  18. data/examples/rails_active_record_store/schema.postgresql.sql +21 -0
  19. data/examples/rails_active_record_store/schema.sqlite.sql +21 -0
  20. data/examples/rails_openid_login_generator/USAGE +23 -0
  21. data/examples/rails_openid_login_generator/openid_login_generator.rb +36 -0
  22. data/examples/rails_openid_login_generator/templates/README +116 -0
  23. data/examples/rails_openid_login_generator/templates/controller.rb +116 -0
  24. data/examples/rails_openid_login_generator/templates/controller_test.rb +0 -0
  25. data/examples/rails_openid_login_generator/templates/helper.rb +2 -0
  26. data/examples/rails_openid_login_generator/templates/openid_login_system.rb +87 -0
  27. data/examples/rails_openid_login_generator/templates/user.rb +14 -0
  28. data/examples/rails_openid_login_generator/templates/user_test.rb +0 -0
  29. data/examples/rails_openid_login_generator/templates/users.yml +0 -0
  30. data/examples/rails_openid_login_generator/templates/view_login.rhtml +15 -0
  31. data/examples/rails_openid_login_generator/templates/view_logout.rhtml +10 -0
  32. data/examples/rails_openid_login_generator/templates/view_welcome.rhtml +9 -0
  33. data/examples/rails_server/README +153 -0
  34. data/examples/rails_server/Rakefile +10 -0
  35. data/examples/rails_server/app/controllers/application.rb +4 -0
  36. data/examples/rails_server/app/controllers/login_controller.rb +35 -0
  37. data/examples/rails_server/app/controllers/server_controller.rb +185 -0
  38. data/examples/rails_server/app/helpers/application_helper.rb +3 -0
  39. data/examples/rails_server/app/helpers/login_helper.rb +2 -0
  40. data/examples/rails_server/app/helpers/server_helper.rb +9 -0
  41. data/examples/rails_server/app/views/layouts/server.rhtml +61 -0
  42. data/examples/rails_server/app/views/login/index.rhtml +32 -0
  43. data/examples/rails_server/app/views/server/decide.rhtml +11 -0
  44. data/examples/rails_server/config/boot.rb +19 -0
  45. data/examples/rails_server/config/database.yml +85 -0
  46. data/examples/rails_server/config/environment.rb +53 -0
  47. data/examples/rails_server/config/environments/development.rb +19 -0
  48. data/examples/rails_server/config/environments/production.rb +19 -0
  49. data/examples/rails_server/config/environments/test.rb +19 -0
  50. data/examples/rails_server/config/routes.rb +23 -0
  51. data/examples/rails_server/db/openid-store/associations/http-localhost_2F_7Cnormal-YU.tkND1J4fEZhnuAoT5Zc0yCA0 +6 -0
  52. data/examples/rails_server/doc/README_FOR_APP +2 -0
  53. data/examples/rails_server/log/development.log +6059 -0
  54. data/examples/rails_server/log/production.log +0 -0
  55. data/examples/rails_server/log/server.log +0 -0
  56. data/examples/rails_server/log/test.log +0 -0
  57. data/examples/rails_server/public/404.html +8 -0
  58. data/examples/rails_server/public/500.html +8 -0
  59. data/examples/rails_server/public/dispatch.cgi +12 -0
  60. data/examples/rails_server/public/dispatch.fcgi +26 -0
  61. data/examples/rails_server/public/dispatch.rb +12 -0
  62. data/examples/rails_server/public/favicon.ico +0 -0
  63. data/examples/rails_server/public/images/rails.png +0 -0
  64. data/examples/rails_server/public/javascripts/controls.js +750 -0
  65. data/examples/rails_server/public/javascripts/dragdrop.js +584 -0
  66. data/examples/rails_server/public/javascripts/effects.js +854 -0
  67. data/examples/rails_server/public/javascripts/prototype.js +1785 -0
  68. data/examples/rails_server/public/robots.txt +1 -0
  69. data/examples/rails_server/script/about +3 -0
  70. data/examples/rails_server/script/breakpointer +3 -0
  71. data/examples/rails_server/script/console +3 -0
  72. data/examples/rails_server/script/destroy +3 -0
  73. data/examples/rails_server/script/generate +3 -0
  74. data/examples/rails_server/script/performance/benchmarker +3 -0
  75. data/examples/rails_server/script/performance/profiler +3 -0
  76. data/examples/rails_server/script/plugin +3 -0
  77. data/examples/rails_server/script/process/reaper +3 -0
  78. data/examples/rails_server/script/process/spawner +3 -0
  79. data/examples/rails_server/script/process/spinner +3 -0
  80. data/examples/rails_server/script/runner +3 -0
  81. data/examples/rails_server/script/server +3 -0
  82. data/examples/rails_server/test/functional/login_controller_test.rb +18 -0
  83. data/examples/rails_server/test/functional/server_controller_test.rb +18 -0
  84. data/examples/rails_server/test/test_helper.rb +28 -0
  85. data/lib/hmac-md5.rb +11 -0
  86. data/lib/hmac-rmd160.rb +11 -0
  87. data/lib/hmac-sha1.rb +11 -0
  88. data/lib/hmac-sha2.rb +25 -0
  89. data/lib/hmac.rb +112 -0
  90. data/lib/openid/association.rb +109 -0
  91. data/lib/openid/consumer.rb +928 -0
  92. data/lib/openid/dh.rb +48 -0
  93. data/lib/openid/discovery.rb +89 -0
  94. data/lib/openid/fetchers.rb +119 -0
  95. data/lib/openid/filestore.rb +315 -0
  96. data/lib/openid/htmltokenizer.rb +355 -0
  97. data/lib/openid/parse.rb +23 -0
  98. data/lib/openid/server.rb +951 -0
  99. data/lib/openid/service.rb +135 -0
  100. data/lib/openid/stores.rb +178 -0
  101. data/lib/openid/trustroot.rb +100 -0
  102. data/lib/openid/util.rb +273 -0
  103. data/test/assoc.rb +38 -0
  104. data/test/consumer.rb +384 -0
  105. data/test/dh.rb +20 -0
  106. data/test/extensions.rb +30 -0
  107. data/test/linkparse.rb +305 -0
  108. data/test/runtests.rb +11 -0
  109. data/test/server2.rb +1053 -0
  110. data/test/storetestcase.rb +172 -0
  111. data/test/teststore.rb +23 -0
  112. data/test/trustroot.rb +113 -0
  113. data/test/util.rb +56 -0
  114. metadata +218 -0
@@ -0,0 +1,285 @@
1
+ #!/usr/bin/env ruby
2
+ require "cgi"
3
+ require "uri"
4
+ require "pathname"
5
+
6
+ require "webrick"
7
+ include WEBrick
8
+
9
+ require "openid/consumer"
10
+ require "openid/filestore"
11
+ require "openid/util"
12
+
13
+ ################ start config ##########################
14
+ # use your desired store implementation here
15
+ store_dir = Pathname.new(Dir.pwd).join("openid-store")
16
+ store = OpenID::FilesystemStore.new(store_dir)
17
+
18
+ $host = "localhost"
19
+ $port = 2000
20
+ ################ end config ############################
21
+
22
+ if $port.nil?
23
+ $base_url = "http://#{$host}/"
24
+ else
25
+ $base_url = "http://#{$host}:#{$port}/"
26
+ end
27
+
28
+ # NOTE: Please note that a Hash is not a valid session storage type, it is just
29
+ # used here to get something that works. In a production environment this
30
+ # should be an object representing the CURRENT USER's session, NOT a global
31
+ # hash. Every user visiting this running consumer.rb will write into this
32
+ # same hash.
33
+ $session = {}
34
+
35
+ $trust_root = $base_url
36
+ $consumer = OpenID::Consumer.new($session, store)
37
+
38
+ server = HTTPServer.new(:Port=>$port)
39
+ class SimpleServlet < HTTPServlet::AbstractServlet
40
+
41
+ def do_GET(req, res)
42
+ @req = req
43
+ @res = res
44
+
45
+ begin
46
+ case req.path
47
+ when "", "/", "/start"
48
+ self.render
49
+ when "/begin"
50
+ self.do_begin
51
+ when "/complete"
52
+ self.do_complete
53
+ when '/policy'
54
+ self.do_policy
55
+ else
56
+ self.redirect(self.build_url("/"))
57
+ end
58
+ ensure
59
+ @req = nil
60
+ @res = nil
61
+ end
62
+ end
63
+
64
+ def do_begin
65
+ # First make sure the user entered something
66
+ openid_url = @req.query.fetch("openid_url", "")
67
+ if openid_url.empty?
68
+ self.render("Enter an identity URL to verify",
69
+ css_class="error", form_contents=openid_url)
70
+ return HTTPStatus::Success
71
+ end
72
+
73
+ # Then ask the openid library to begin the authorization
74
+ request = $consumer.begin(openid_url)
75
+
76
+ # If the URL was unusable (either because of network conditions,
77
+ # a server error, or that the response returned was not an OpenID
78
+ # identity page), the library will return HTTP_FAILURE or PARSE_ERROR.
79
+ # Let the user know that the URL is unusable.
80
+ case request.status
81
+ when OpenID::FAILURE
82
+ self.render("Unable to find openid server for <q>#{openid_url}</q>",
83
+ css_class="error", form_contents=openid_url)
84
+ return HTTPStatus::Success
85
+
86
+ when OpenID::SUCCESS
87
+ # The URL was a valid identity URL. Now we just need to send a redirect
88
+ # to the server using the redirect_url the library created for us.
89
+
90
+ # check to see if we want to make an SREG request. Generally this will
91
+ # not take the form of a checkbox, but will be part of your site policy.
92
+ # For example, you may perform an sreg request if the user appears
93
+ # to be new to the site. The checkbox is here for convenience of
94
+ # testing.
95
+ do_sreg = @req.query.fetch('sreg', nil)
96
+
97
+ if do_sreg and request.uses_extension?('http://openid.net/sreg/1.0')
98
+ policy_url = self.build_url('/policy')
99
+ request.add_extension_arg('sreg','policy_url', policy_url)
100
+ request.add_extension_arg('sreg','required','email,nickname')
101
+ request.add_extension_arg('sreg','optional','fullname,dob,gender,postcode,country')
102
+ end
103
+
104
+ if do_sreg
105
+ extra = {'did_sreg' => 'true'}
106
+ else
107
+ extra = {}
108
+ end
109
+
110
+ return_to = self.build_url("/complete", extra)
111
+
112
+ # build the redirect
113
+ redirect_url = request.redirect_url($trust_root, return_to)
114
+
115
+ # send redirect to the server
116
+ self.redirect(redirect_url)
117
+ else
118
+ # Should never get here
119
+ raise "Not Reached"
120
+ end
121
+ end
122
+
123
+ # handle the redirect from the OpenID server
124
+ def do_complete
125
+ # Ask the library to check the response that the server sent
126
+ # us. Status is a code indicating the response type. info is
127
+ # either nil or a string containing more information about
128
+ # the return type.
129
+ response = $consumer.complete(@req.query)
130
+
131
+ css_class = "error"
132
+
133
+ did_sreg = @req.query.fetch('did_sreg', nil)
134
+ sreg_checked = did_sreg ? 'checked="checked"' : ''
135
+
136
+ if response.status == OpenID::FAILURE
137
+ # In the case of failure, if info is non-nil, it is the
138
+ # URL that we were verifying. We include it in the error
139
+ # message to help the user figure out what happened.
140
+ if response.identity_url
141
+ message = "Verification of #{response.identity_url} failed"
142
+ else
143
+ message = 'Verification failed.'
144
+ end
145
+
146
+ # add on the failure message for a little debug info
147
+ message += ' '+response.msg.to_s
148
+
149
+ elsif response.status == OpenID::SUCCESS
150
+ # Success means that the transaction completed without
151
+ # error. If info is nil, it means that the user cancelled
152
+ # the verification.
153
+ css_class = "alert"
154
+
155
+ message = "You have successfully verified #{response.identity_url} as your identity."
156
+
157
+ # get the signed extension sreg arguments
158
+ sreg = response.extension_response('sreg')
159
+ if sreg.length > 0
160
+ message += "<hr/> With simple registration fields:<br/>"
161
+ sreg.keys.sort.each {|k| message += "<br/><b>#{k}</b>: #{sreg[k]}"}
162
+ elsif did_sreg
163
+ message += "<hr/> But the server does not support simple registration."
164
+ end
165
+
166
+ elsif response.status == OpenID::CANCEL
167
+ message = "Verification cancelled."
168
+
169
+ else
170
+ message = "Unknown response status: #{response.status}"
171
+
172
+ end
173
+ self.render(message, css_class, response.identity_url, sreg_checked)
174
+ end
175
+
176
+ def do_policy
177
+ @res.body = <<END
178
+ <html>
179
+ <head></head>
180
+ <body>
181
+ <h3>Ruby Consumer Simple Registration Policy</h3>
182
+ <p>This consumer makes a simple registration request for the following fields:<br/><br/>
183
+ <b>Required:</b> email, nickname<br/>
184
+ <b>Optional:</b> fullname, dob, gender, postcode, country<br/><br/>
185
+ Nothing is actually done with the data provided, it simply exists to illustrate the simple registration protocol.
186
+ </p>
187
+ </body>
188
+ </html>
189
+
190
+ END
191
+ end
192
+
193
+ # build a URL relative to the server base URL, with the given query
194
+ # parameters added.
195
+ def build_url(action, query=nil)
196
+ url = URI.parse($base_url).merge(action).to_s
197
+ url = OpenID::Util.append_args(url, query) unless query.nil?
198
+ return url
199
+ end
200
+
201
+ def redirect(url)
202
+ @res.set_redirect(HTTPStatus::TemporaryRedirect, url)
203
+ end
204
+
205
+ def render(message=nil, css_class="alert", form_contents="", checked="")
206
+ @res.body = self.page_header
207
+ unless message.nil?
208
+ @res.body << "<div class=\"#{css_class}\">#{message}</div>"
209
+ end
210
+ @res.body << self.page_footer(form_contents, checked)
211
+ end
212
+
213
+ def page_header(title="Ruby OpenID WEBrick example")
214
+ header = <<END_OF_STRING
215
+ <html>
216
+ <head><title>#{title}</title></head>
217
+ <style type="text/css">
218
+ * {
219
+ font-family: verdana,sans-serif;
220
+ }
221
+ body {
222
+ width: 50em;
223
+ margin: 1em;
224
+ }
225
+ div {
226
+ padding: .5em;
227
+ }
228
+ table {
229
+ margin: none;
230
+ padding: none;
231
+ }
232
+ .alert {
233
+ border: 1px solid #e7dc2b;
234
+ background: #fff888;
235
+ }
236
+ .error {
237
+ border: 1px solid #ff0000;
238
+ background: #ffaaaa;
239
+ }
240
+ #verify-form {
241
+ border: 1px solid #777777;
242
+ background: #dddddd;
243
+ margin-top: 1em;
244
+ padding-bottom: 0em;
245
+ }
246
+ </style>
247
+ <body>
248
+ <h1>#{title}</h1>
249
+ <p>
250
+ This example consumer uses the <a href="http://openidenabled.com/openid/libraries/ruby">Ruby OpenID</a> library
251
+ on a WEBrick platform. The example just verifies that the URL that
252
+ you enter is your identity URL.
253
+ </p>
254
+ END_OF_STRING
255
+ end
256
+
257
+
258
+ def page_footer(form_contents="", checked="")
259
+ form_contents = "" if form_contents == "/"
260
+ footer = <<END_OF_STRING
261
+ <div id="verify-form">
262
+ <form method="get" action="#{self.build_url("/begin")}">
263
+ Identity&nbsp;URL:
264
+ <input type="text" name="openid_url" value="#{form_contents}" />
265
+ <input type="submit" value="Verify" />
266
+ <input type="checkbox" id="sregbox" name="sreg" #{checked} />
267
+ <label for="sregbox">with simple registration</label>
268
+ <a href="http://www.openidenabled.com/openid/simple-registration-extension" target="_blank">?</a>
269
+ </form>
270
+ </div>
271
+ </body>
272
+ </html>
273
+ END_OF_STRING
274
+ end
275
+
276
+
277
+
278
+ end
279
+
280
+ # Bootstrap the example
281
+ server.mount("/", SimpleServlet)
282
+ trap("INT") {server.shutdown}
283
+ print "\nVisit http://#{$host}:#{$port}/ in your browser.\n\n"
284
+ server.start
285
+
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ handle: {HMAC-SHA1}{4462794a}{fcagVg==}
3
+ secret: GIQrSk+6Upv9EYB6A9EJ1P8jcLY=
4
+ issued: 1147304267
5
+ lifetime: 120960
6
+ assoc_type: HMAC-SHA1
@@ -0,0 +1 @@
1
+ _eK��2�X,�|RG+
@@ -0,0 +1,59 @@
1
+ =Example SQL store using ActiveRecord
2
+
3
+ A store is required by an OpenID server and optionally by the consumer to
4
+ store associations, nonces, and auth key information across requests and
5
+ threads. This directory contains code useful in implementing an
6
+ ActiveRecord based store as an alternative to using the standard
7
+ OpenID::FilesystemOpenIDStore in Ruby on Rails applications.
8
+
9
+ ==Setting up your database
10
+
11
+ You'll need to add three tables to your databases. There are two
12
+ different ways to do this outlined below.
13
+
14
+ ===Starting from scratch
15
+
16
+ This directory contains three SQL schema files with the proper SQL for
17
+ adding the necessary OpenID tables in SQLite, MySQL, and Postgresql.
18
+ See the schema.type.sql files for more information.
19
+
20
+ ===Upgrading with a rails migration script
21
+
22
+ Rails has built-in database migration functionality. If you are trying
23
+ to add OpenID support into your existing rails application, then
24
+ you'll probably want to use the enclosed migration script,
25
+ XX_add_openidstore.rb. Put this file in rails app's db/migrate
26
+ directory and replace the XX with the next migration number. If there
27
+ aren't migrations, the number will be 0.
28
+
29
+ ==The OpenID Models
30
+
31
+ From the models dir, copy openid_association.rb, openid_nonce.rb, and
32
+ openid_setting.rb into the app/models dir of your rails app.
33
+
34
+ ==The OpenIDStoreHelper
35
+
36
+ You'll probably have controller for handling openid operations in your
37
+ application (see the other examples). The control flow of the store
38
+ is handled in a helper. Look at OpenidHelper in openid_helper.rb,
39
+ which implements the OpenID::OpenIDStore interface.
40
+
41
+ Copy the openid_helper.rb into your app/helpers and then include the
42
+ OpenidHelper module in your openid controller. When instantiating
43
+ your OpenIDServer or OpenIDConsumer instance, you'll simply be able to
44
+ pass in self as the store.
45
+
46
+ ==Unit testing your ActiveRecord OpenID store
47
+
48
+ The Ruby OpenID library comes with a generic StoreTestCase module that
49
+ can be mixed into any store test case. Copy openidstore_test.rb into
50
+ your rails' app test/unit dir. Change the line that contains "include
51
+ OpenidHelper" to include your openid helper if it has a different
52
+ name.
53
+
54
+ You may also need to change the require at the top to get at
55
+ storetestcase.rb. You won't need to change it if you are using
56
+ the openid library in the vendor directory of your rails' app.
57
+
58
+ ==Questions?
59
+ Contact Brian Ellin a line at brian at janrain dot com
@@ -0,0 +1,30 @@
1
+ class AddOpenidstore < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :openid_settings do |t|
4
+ t.column :setting, :string
5
+ t.column :value, :binary
6
+ end
7
+
8
+ create_table :openid_associations do |t|
9
+ # server_url is blob, because URLs could be longer
10
+ # than db can handle as a string
11
+ t.column :server_url, :binary
12
+ t.column :handle, :string
13
+ t.column :secret, :binary
14
+ t.column :issued, :integer
15
+ t.column :lifetime, :integer
16
+ t.column :assoc_type, :string
17
+ end
18
+
19
+ create_table :openid_nonces do |t|
20
+ t.column :nonce, :string
21
+ t.column :created, :integer
22
+ end
23
+ end
24
+
25
+ def self.down
26
+ drop_table :openid_settings
27
+ drop_table :openid_associations
28
+ drop_table :openid_nonces
29
+ end
30
+ end
@@ -0,0 +1,12 @@
1
+ require 'openid/association'
2
+
3
+ class OpenidAssociation < ActiveRecord::Base
4
+
5
+ def from_record
6
+ OpenID::Association.new(handle,
7
+ secret,
8
+ issued,
9
+ lifetime,
10
+ assoc_type)
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ class OpenidNonce < ActiveRecord::Base
2
+ end
3
+
@@ -0,0 +1,2 @@
1
+ class OpenidSetting < ActiveRecord::Base
2
+ end
@@ -0,0 +1,91 @@
1
+ require 'openid/association'
2
+
3
+ module OpenidHelper
4
+
5
+ def get_auth_key
6
+ setting = OpenidSetting.find :first, :conditions => "setting = 'auth_key'"
7
+ if setting.nil?
8
+ auth_key = OpenID::Util.random_string(20)
9
+ setting = OpenidSetting.create :setting => 'auth_key', :value => auth_key
10
+ end
11
+ setting.value
12
+ end
13
+
14
+ def store_association(server_url, assoc)
15
+ remove_association(server_url, assoc.handle)
16
+ OpenidAssociation.create(:server_url => server_url,
17
+ :handle => assoc.handle,
18
+ :secret => assoc.secret,
19
+ :issued => assoc.issued,
20
+ :lifetime => assoc.lifetime,
21
+ :assoc_type => assoc.assoc_type)
22
+ end
23
+
24
+ def get_association(server_url, handle=nil)
25
+
26
+ unless handle.nil?
27
+ assocs = OpenidAssociation.find(:all, :conditions => ["server_url = ? AND handle = ?", server_url, handle])
28
+ else
29
+ assocs = OpenidAssociation.find(:all, :conditions => ["server_url = ?", server_url])
30
+ end
31
+
32
+ return nil if assocs.nil?
33
+
34
+ assocs.reverse!
35
+
36
+ assocs.each do |assoc|
37
+ a = assoc.from_record
38
+ if a.expired?
39
+ assoc.destroy
40
+ else
41
+ return a
42
+ end
43
+ end
44
+
45
+ return nil
46
+ end
47
+
48
+ def remove_association(server_url, handle)
49
+ assoc = OpenidAssociation.find(:first, :conditions => ["server_url = ? AND handle = ?", server_url, handle])
50
+ unless assoc.nil?
51
+ assoc.destroy
52
+ return true
53
+ end
54
+ return false
55
+ end
56
+
57
+ def store_nonce(nonce)
58
+ use_nonce(nonce)
59
+ OpenidNonce.create :nonce => nonce, :created => Time.now.to_i
60
+ end
61
+
62
+ def use_nonce(nonce)
63
+ nonce = OpenidNonce.find(:first, :conditions => ["nonce = ?", nonce])
64
+ return false if nonce.nil?
65
+
66
+ age = Time.now.to_i - nonce.created
67
+ nonce.destroy
68
+
69
+ return false if age > (6*60*60) # max nonce age of 6 hours
70
+ return true
71
+ end
72
+
73
+ def dumb?
74
+ return false
75
+ end
76
+
77
+ # not part of the api, but useful
78
+ def gc
79
+ now = Time.now.to_i
80
+
81
+ # remove old nonces
82
+ nonces = OpenidNonce.find(:all)
83
+ nonces.each {|n| n.destroy if now - n.created > (6*60*60)} unless nonces.nil?
84
+
85
+ # remove expired assocs
86
+ assocs = OpenidAssociation.find(:all)
87
+ assocs.each { |a| a.destroy if a.from_record.expired? } unless assocs.nil?
88
+ end
89
+
90
+
91
+ end