tpitale-rack-oauth2-server 2.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/CHANGELOG +202 -0
  2. data/Gemfile +16 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +604 -0
  5. data/Rakefile +90 -0
  6. data/VERSION +1 -0
  7. data/bin/oauth2-server +206 -0
  8. data/lib/rack-oauth2-server.rb +4 -0
  9. data/lib/rack/oauth2/admin/css/screen.css +347 -0
  10. data/lib/rack/oauth2/admin/images/loading.gif +0 -0
  11. data/lib/rack/oauth2/admin/images/oauth-2.png +0 -0
  12. data/lib/rack/oauth2/admin/js/application.coffee +220 -0
  13. data/lib/rack/oauth2/admin/js/jquery.js +166 -0
  14. data/lib/rack/oauth2/admin/js/jquery.tmpl.js +414 -0
  15. data/lib/rack/oauth2/admin/js/protovis-r3.2.js +277 -0
  16. data/lib/rack/oauth2/admin/js/sammy.js +5 -0
  17. data/lib/rack/oauth2/admin/js/sammy.json.js +5 -0
  18. data/lib/rack/oauth2/admin/js/sammy.oauth2.js +142 -0
  19. data/lib/rack/oauth2/admin/js/sammy.storage.js +5 -0
  20. data/lib/rack/oauth2/admin/js/sammy.title.js +5 -0
  21. data/lib/rack/oauth2/admin/js/sammy.tmpl.js +5 -0
  22. data/lib/rack/oauth2/admin/js/underscore.js +722 -0
  23. data/lib/rack/oauth2/admin/views/client.tmpl +58 -0
  24. data/lib/rack/oauth2/admin/views/clients.tmpl +52 -0
  25. data/lib/rack/oauth2/admin/views/edit.tmpl +80 -0
  26. data/lib/rack/oauth2/admin/views/index.html +39 -0
  27. data/lib/rack/oauth2/admin/views/no_access.tmpl +4 -0
  28. data/lib/rack/oauth2/models.rb +27 -0
  29. data/lib/rack/oauth2/models/access_grant.rb +54 -0
  30. data/lib/rack/oauth2/models/access_token.rb +129 -0
  31. data/lib/rack/oauth2/models/auth_request.rb +61 -0
  32. data/lib/rack/oauth2/models/client.rb +93 -0
  33. data/lib/rack/oauth2/rails.rb +105 -0
  34. data/lib/rack/oauth2/server.rb +458 -0
  35. data/lib/rack/oauth2/server/admin.rb +250 -0
  36. data/lib/rack/oauth2/server/errors.rb +104 -0
  37. data/lib/rack/oauth2/server/helper.rb +147 -0
  38. data/lib/rack/oauth2/server/practice.rb +79 -0
  39. data/lib/rack/oauth2/server/railtie.rb +24 -0
  40. data/lib/rack/oauth2/server/utils.rb +30 -0
  41. data/lib/rack/oauth2/sinatra.rb +71 -0
  42. data/rack-oauth2-server.gemspec +24 -0
  43. data/rails/init.rb +11 -0
  44. data/test/admin/api_test.rb +228 -0
  45. data/test/admin/ui_test.rb +38 -0
  46. data/test/oauth/access_grant_test.rb +276 -0
  47. data/test/oauth/access_token_test.rb +311 -0
  48. data/test/oauth/authorization_test.rb +298 -0
  49. data/test/oauth/server_methods_test.rb +292 -0
  50. data/test/rails2/app/controllers/api_controller.rb +40 -0
  51. data/test/rails2/app/controllers/application_controller.rb +2 -0
  52. data/test/rails2/app/controllers/oauth_controller.rb +17 -0
  53. data/test/rails2/config/environment.rb +19 -0
  54. data/test/rails2/config/environments/test.rb +0 -0
  55. data/test/rails2/config/routes.rb +13 -0
  56. data/test/rails3/app/controllers/api_controller.rb +40 -0
  57. data/test/rails3/app/controllers/application_controller.rb +2 -0
  58. data/test/rails3/app/controllers/oauth_controller.rb +17 -0
  59. data/test/rails3/config/application.rb +19 -0
  60. data/test/rails3/config/environment.rb +2 -0
  61. data/test/rails3/config/routes.rb +12 -0
  62. data/test/setup.rb +120 -0
  63. data/test/sinatra/my_app.rb +69 -0
  64. metadata +145 -0
@@ -0,0 +1,202 @@
1
+ Pending Version 2.2.2
2
+
3
+ Fix for unknown [] for NilClass when database not setup (epinault-ttc)
4
+
5
+
6
+ 2011-04-11 version 2.2.1
7
+
8
+ Content type header on redirects (Marc Schwieterman)
9
+
10
+
11
+ 2011-02-02 version 2.2.0
12
+
13
+ Don't require client_secret when requesting authorization (George Ogata).
14
+
15
+ Don't check the redirect_uri if the client does not have one set (George Ogata).
16
+
17
+ Look for post params if request is a POST (George Ogata).
18
+
19
+
20
+ 2010-12-22 version 2.1.0
21
+
22
+ Added support for two-legged OAuth flow (Brian Ploetz)
23
+
24
+ Fixed query parameter authorization and allowed access_token to be defined
25
+ (Ari)
26
+
27
+
28
+ 2010-11-30 version 2.0.1
29
+
30
+ Change: username/password authentication with no scope results in access token
31
+ with default scope. Makes like easier for everyone.
32
+
33
+
34
+ 2010-11-23 version 2.0.0
35
+
36
+ MAJOR CHANGE:
37
+ Keeping with OAuth 2.0 spec terminology, we'll call it scope all around. Some
38
+ places in the API that previously used "scopes" have been changed to "scope".
39
+
40
+ OTOH, the scope is not consistently stored and returned as array of names,
41
+ previous was stored as comma-separated string, and often returned as such.
42
+ Whatever you have stored with pre 2.0 will probably not work with 2.0 and
43
+ forward.
44
+
45
+ Clients now store their scope, and only those names are allowed in access
46
+ tokens. The global setting oauth.scope is no longer in use. Forget about it.
47
+
48
+ To migrate from 1.4.x to 2.0:
49
+
50
+ oauth2-server migrate --db <db name>
51
+
52
+ Application client registrations will change from having no scope to having an
53
+ empty scope, so you would want to update their registration, either using the
54
+ Web console, or from your code:
55
+
56
+ Client.all.each { |client| client.update(:scope=>%w{read write}) }
57
+
58
+
59
+ Use Rack::OAuth2::Server token_for and access_grant to generate access tokens
60
+ and access grants, respectively. These are mighty useful if you're using the
61
+ OAuth 2.0 infrastructure, but have different ways for authorizing, e.g. using
62
+ access tokens instead of cookies.
63
+
64
+ Rack::OAuth2::Server method register to register new client applications and
65
+ update existing records. This method is idempotent, so you can use it in rake
66
+ db:seed, deploy scripts, migrations, etc.
67
+
68
+ If your authenticator accepts four arguments, it will receive, in addition to
69
+ username and password, also the client identifier and requested scopes.
70
+
71
+ Web console now allows you to set/unset individual scopes for each client
72
+ application, and store a note on each client.
73
+
74
+ Added Sammy.js OAuth 2.0 plugin.
75
+
76
+
77
+ 2010-11-12 version 1.4.6
78
+
79
+ Added Railtie support for Rails 3.x and now running tests against both Rails
80
+ 2.x and 3.x.
81
+
82
+
83
+ 2010-11-11 version 1.4.5
84
+
85
+ Cosmetic changes to UI. Added throbber and error messages when AJAX requests go
86
+ foul. Header on the left, sign-out on the right, as most people expect it.
87
+ Client name is no longer a link to the site, site link shown separately.
88
+
89
+
90
+ 2010-11-10 version 1.4.4
91
+
92
+ Added a practice server. You can use it to test your OAuth 2.0 client library.
93
+ To fire up the practice server: oauth2-server practice
94
+
95
+ Bumped up dependencies on Rack 1.1 or later, Sinatra 1.1 or later.
96
+
97
+
98
+ 2010-11-09 version 1.4.3
99
+
100
+ Renamed Rack::OAuth2::Server::Admin to just Rack::OAuth2::Admin.
101
+
102
+ Checked in config.ru, I use this for testing the Web console.
103
+
104
+
105
+ 2010-11-09 version 1.4.2
106
+
107
+ Fix to commend line tool to properly do authentication.
108
+
109
+ Added Sinatra as dependency.
110
+
111
+
112
+ 2010-11-09 version 1.4.1
113
+
114
+ Fix to command line tool when accessing MongoDB with username/password.
115
+
116
+
117
+ 2010-11-09 version 1.4.0
118
+
119
+ If authorization handle is passed as request parameter (the recommended way),
120
+ then you can call oauth.grant! with a single argument and oauth.deny! with no
121
+ arguments.
122
+
123
+ You can now call oauth.deny! at any point during the authorization flow, e.g.
124
+ automatically deny all requests based on scope and client.
125
+
126
+ To deny access, return status code 403 (was, incorrectly 401). Or just use
127
+ oauth.deny!.
128
+
129
+ Web console gets template_url setting you can use to map access token identity
130
+ into a URL in your application. The substitution variable is "{id}".
131
+
132
+ Added error page when authorization attempt fails (instead of endless
133
+ redirect).
134
+
135
+ Fixed mounting of Web console on Rails. If it failed you before, try again.
136
+
137
+ Fixed documentation for configuration under Rails, clarify that all the
138
+ interesting stuff happens in after_initialize.
139
+
140
+ Fixed error responses for response_type=token to use fragment identifier.
141
+
142
+
143
+ 2010-11-08 version 1.3.1
144
+
145
+ Added command line tool, helps you get started and setup:
146
+ $ oauth2-server setup --db my_db
147
+
148
+ Added a touch of color to the UI and ability to delete a client.
149
+
150
+ You can not sign out of the Web console.
151
+
152
+
153
+ 2010-11-07 version 1.3.0
154
+
155
+ Added OAuth authorization console.
156
+
157
+ Added param_authentication option: turn this on if you need to support
158
+ oauth_token query parameter or form field. Disabled by default.
159
+
160
+ Added host option: only check requests sent to that host (e.g. only check
161
+ requests to api.example.com).
162
+
163
+ Added path option: only check requests under this path (e.g. only check
164
+ requests for /api/...).
165
+
166
+
167
+ 2010-11-03 version 1.2.2
168
+
169
+ Store ObjectId references in database.
170
+
171
+
172
+ 2010-11-03 version 1.2.1
173
+
174
+ Make sure order of scope no longer important for access token lookup.
175
+
176
+
177
+ 2010-11-02 version 1.2.0
178
+
179
+ You can now redirect to /oauth/authorize with authorization query parameter and
180
+ it will do the right thing.
181
+
182
+
183
+ 2010-11-02 version 1.1.1
184
+
185
+ Fixed missing rails/init.rb.
186
+
187
+
188
+ 2010-11-02 version 1.1.0
189
+
190
+ Renamed oauth.resource as oauth.identity to remove confusion, besides, it's
191
+ more often identity than anything else.
192
+
193
+ Added automagic loading under Rails, no need to require special path.
194
+
195
+ Added Rack::OAuth2::Server::Options class, easier to user than Hash.
196
+
197
+ Added indexes for speedier queries.
198
+
199
+
200
+ 2010-11-02 version 1.0.0
201
+
202
+ World premiere.
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source :rubygems
2
+ gemspec
3
+
4
+ group :development do
5
+ gem "rake"
6
+ gem "thin"
7
+ gem "yard"
8
+ end
9
+
10
+ group :test do
11
+ gem "awesome_print"
12
+ gem "json"
13
+ gem "rack-test"
14
+ gem "shoulda"
15
+ gem "timecop"
16
+ end
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2010 Flowtown, Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
@@ -0,0 +1,604 @@
1
+ = Rack::OAuth2::Server
2
+
3
+ OAuth 2.0 Authorization Server as a Rack module. Because you don't allow
4
+ strangers into your app, and OAuth 2.0 is the new awesome.
5
+
6
+ http://tools.ietf.org/html/draft-ietf-oauth-v2-10
7
+
8
+
9
+ == Adding OAuth 2.0 To Your Application
10
+
11
+ === Step 1: Setup Your Database
12
+
13
+ The authorization server needs to keep track of clients, authorization
14
+ requests, access grants and access tokens. That could only mean one thing: a
15
+ database.
16
+
17
+ The current release uses MongoDB[http://www.mongodb.org/]. You're going to need
18
+ a running server and open connection in the form of a +Mongo::DB+ object.
19
+ Because MongoDB is schema-less, there's no need to run migrations.
20
+
21
+ If MongoDB is not your flavor, you can easily change the models to support a
22
+ different database engine. All the persistence logic is located in
23
+ +lib/rack/oauth2/models+ and kept simple by design. And if you did the work to
24
+ support a different database engine, send us a pull request.
25
+
26
+
27
+ === Step 2: Use The Server
28
+
29
+ For Rails 2.3/3.0, Rack::OAuth2::Server automatically adds itself as middleware
30
+ when required, but you do need to configure it from within
31
+ +config/environment.rb+ (or one of the specific environment files). For example:
32
+
33
+ Rails::Initializer.run do |config|
34
+ . . .
35
+ config.after_initialize do
36
+ config.oauth.database = Mongo::Connection.new["my_db"]
37
+ config.oauth.authenticator = lambda do |username, password|
38
+ user = User.find(username)
39
+ user if user && user.authenticated?(password)
40
+ end
41
+ end
42
+ end
43
+
44
+ For Sinatra and Padrino, first require +rack/oauth2/sinatra+ and register
45
+ Rack::OAuth2::Sinatra into your application. For example:
46
+
47
+ require "rack/oauth2/sinatra"
48
+
49
+ class MyApp < Sinatra::Base
50
+ register Rack::OAuth2::Sinatra
51
+
52
+ oauth.database = Mongo::Connection.new["my_db"]
53
+ oauth.scope = %w{read write}
54
+ oauth.authenticator = lambda do |username, password|
55
+ user = User.find(username)
56
+ user if user && user.authenticated?(password)
57
+ end
58
+
59
+ . . .
60
+ end
61
+
62
+ With any other Rack server, you can +use Rack::OAuth2::Server+ and pass your
63
+ own {Rack::OAuth2::Server::Options} object.
64
+
65
+ The configuration options are:
66
+
67
+ - +:access_token_path+ -- Path for requesting access token. By convention
68
+ defaults to +/oauth/access_token+.
69
+ - +:authenticator+ -- For username/password authorization. A block that
70
+ receives the credentials and returns identity string (e.g. user ID) or nil.
71
+ - +:authorization_types+ -- Array of supported authorization types. Defaults to
72
+ ["code", "token"], and you can change it to just one of these names.
73
+ - +:authorize_path+ -- Path for requesting end-user authorization. By
74
+ convention defaults to +/oauth/authorize+.
75
+ - +:database+ -- +Mongo::DB+ instance.
76
+ - +:host+ -- Only check requests sent to this host.
77
+ - +:path+ -- Only check requests for resources under this path.
78
+ - +:param_authentication+ -- If true, supports authentication using query/form
79
+ parameters.
80
+ - +:realm+ -- Authorization realm that will show up in 401 responses. Defaults
81
+ to use the request host name.
82
+ - +:logger+ -- The logger to use. Under Rails, defaults to use the Rails
83
+ logger. Will use +Rack::Logger+ if available.
84
+
85
+ If you only intend to use the UI authorization flow, you don't need to worry
86
+ about the authenticator. If you want to allow client applications to create
87
+ access tokens by passing the end-user's username/password, then you need an
88
+ authenticator. This feature is necessary for some client applications, and
89
+ quite handy during development/testing.
90
+
91
+ The authenticator is a block that receives either two or four parameters. The
92
+ first two are username and password. The other two are the client identifier
93
+ and scope. It authenticated, it returns an identity, otherwise it can return
94
+ nil or false. For example:
95
+
96
+ oauth.authenticator = lambda do |username, password|
97
+ user = User.find_by_username(username)
98
+ user if user && user.authenticated?(password)
99
+ end
100
+
101
+
102
+ === Step 3: Let Users Authorize
103
+
104
+ Authorization requests go to +/oauth/authorize+. Rack::OAuth2::Server
105
+ intercepts these requests and validates the client ID, redirect URI,
106
+ authorization type and scope. If the request fails validation, the user is
107
+ redirected back to the client application with a suitable error code.
108
+
109
+ If the request passes validation, Rack::OAuth2::Server sets the request header
110
+ +oauth.authorization+ to the authorization handle, and passes control to your
111
+ application. Your application will ask the user to grant or deny the
112
+ authorization request.
113
+
114
+ Once granted, your application signals the grant by setting the response header
115
+ +oauth.authorization+ to the authorization handle it got before, and setting
116
+ the response header +oauth.identity+ to the authorized identity. This is
117
+ typicaly the user ID or account ID, but can be anything you want, as long as
118
+ it's a string. Rack::OAuth2::Server intercepts this response and redirects the
119
+ user back to the client application with an authorization code or access token.
120
+
121
+ To signal that the user denied the authorization requests your application sets
122
+ the response header oauth.authorization as before, and returns the status code
123
+ 403 (Forbidden). Rack::OAuth2::Server will then redirect the user back to the
124
+ client application with a suitable error code.
125
+
126
+ In Rails, the entire flow would look something like this:
127
+
128
+ class OauthController < ApplicationController
129
+ def authorize
130
+ if current_user
131
+ render :action=>"authorize"
132
+ else
133
+ redirect_to :action=>"login", :authorization=>oauth.authorization
134
+ end
135
+ end
136
+
137
+ def grant
138
+ head oauth.grant!(current_user.id)
139
+ end
140
+
141
+ def deny
142
+ head oauth.deny!
143
+ end
144
+ end
145
+
146
+ Rails actions must render something. The oauth method returns a helper object
147
+ ({Rack::OAuth2::Server::Helper}) that cannot render anything, but can set the right
148
+ response headers and return a status code, which we then pass on to the +head+
149
+ method.
150
+
151
+ In Sinatra/Padrino, it would look something like this:
152
+
153
+ get "/oauth/authorize" do
154
+ if current_user
155
+ render "oauth/authorize"
156
+ else
157
+ redirect "/oauth/login?authorization=#{oauth.authorization}"
158
+ end
159
+ end
160
+
161
+ post "/oauth/grant" do
162
+ oauth.grant! "Superman"
163
+ end
164
+
165
+ post "/oauth/deny" do
166
+ oauth.deny!
167
+ end
168
+
169
+ The view would look something like this:
170
+
171
+ <h2>The application <% link_to h(oauth.client.display_name), oauth.client.link %>
172
+ is requesting to <%= oauth.scope.to_sentence %> your account.</h2>
173
+ <form action="/oauth/grant">
174
+ <button>Grant</button>
175
+ <input type="hidden" name="authorization" value="<%= oauth.authorization %>">
176
+ </form>
177
+ <form action="/oauth/deny">
178
+ <button>Deny</button>
179
+ <input type="hidden" name="authorization" value="<%= oauth.authorization %>">
180
+ </form>
181
+
182
+
183
+ === Step 4: Protect Your Path
184
+
185
+ Rack::OAuth2::Server intercepts all incoming requests and looks for an
186
+ Authorization header that uses OAuth authentication scheme, like so:
187
+
188
+ Authorization: OAuth e57807eb99f8c29f60a27a75a80fec6e
189
+
190
+ It can also support the +oauth_token+ query parameter or form field, if you set
191
+ +param_authentication+ to true. This option is off by default to prevent
192
+ conflict with OAuth 1.0 callback.
193
+
194
+ If Rack::OAuth2::Server finds a valid access token in the request, it sets the
195
+ request header +oauth.identity+ to the value you supplied during authorization
196
+ (step 3). You can use +oauth.identity+ to resolve the access token back to
197
+ user, account or whatever you put there.
198
+
199
+ If the access token is invalid or revoked, it returns 401 (Unauthorized) to the
200
+ client. However, if there's no access token, the request goes through. You
201
+ might want to protect some URLs but not others, or allow authenticated and
202
+ unauthenticated access, the former returning more data or having higher rate
203
+ limit, etc.
204
+
205
+ It is up to you to reject requests that must be authenticated but are not. You
206
+ can always just return status code 401, but it's better to include a proper
207
+ +WWW-Authenticate+ header, which you can do by setting the response header
208
+ +oauth.no_access+ to true, or using +oauth_required+ to setup a filter.
209
+
210
+ You may also want to reject requests that don't have the proper scope. You can
211
+ return status code 403, but again it's better to include a proper
212
+ +WWW-Authenticate+ header with the required scope. You can do that by setting
213
+ the response header +oauth.no_scope+ to the scope name, or using
214
+ +oauth_required+ with the scope option.
215
+
216
+ In Rails, it would look something like this:
217
+
218
+ class MyController < ApplicationController
219
+
220
+ before_filter :set_current_user
221
+ oauth_required :only=>:private
222
+ oauth_required :only=>:calc, :scope=>"math"
223
+
224
+ # Authenticated/un-authenticated get different responses.
225
+ def public
226
+ if oauth.authenticated?
227
+ render :action=>"more-details"
228
+ else
229
+ render :action=>"less-details"
230
+ end
231
+ end
232
+
233
+ # Must authenticate to retrieve this.
234
+ def private
235
+ render
236
+ end
237
+
238
+ # Must authenticate with scope math to do this.
239
+ def calc
240
+ render :text=>"2+2=4"
241
+ end
242
+
243
+ protected
244
+
245
+ def set_current_user
246
+ @current_user = User.find(oauth.identity) if oauth.authenticated?
247
+ end
248
+
249
+ end
250
+
251
+ In Sinatra/Padrino, it would look something like this:
252
+
253
+ before do
254
+ @current_user = User.find(oauth.identity) if oauth.authenticated?
255
+ end
256
+
257
+ oauth_required "/private"
258
+ oauth_required "/calc", :scope=>"math"
259
+
260
+ # Authenticated/un-authenticated get different responses.
261
+ get "/public" do
262
+ if oauth.authenticated?
263
+ render "more-details"
264
+ else
265
+ render "less-details"
266
+ end
267
+ end
268
+
269
+ # Must authenticate to retrieve this.
270
+ get "/private" do
271
+ render "secrets"
272
+ end
273
+
274
+ # Must authenticate with scope math to do this.
275
+ get "/calc" do
276
+ render "2 + 2 = 4"
277
+ end
278
+
279
+
280
+ === Step 5: Register Some Clients
281
+
282
+ Before a client application can request access, there must be a client record
283
+ in the database. Registration provides the client application with a client
284
+ ID and secret. The client uses these to authenticate itself.
285
+
286
+ The client provides its display name, site URL and image URL. These should be
287
+ shown to the end-user to let them know which client application they're
288
+ granting access to.
289
+
290
+ Clients can also register a redirect URL. This is optional but highly
291
+ recommended for better security, preventing other applications from hijacking
292
+ the client's ID/secret.
293
+
294
+ You can register clients using the command line tool +oauth2-server+:
295
+
296
+ $ oauth2-server register --db my_db
297
+
298
+ Or you can register clients using the Web-based OAuth console, see below.
299
+
300
+ Programatically, registering a new client is as simple as:
301
+
302
+ $ ./script/console
303
+ Loading development environment (Rails 2.3.8)
304
+ > Rack::OAuth2::Server.register(:display_name=>"UberClient",
305
+ :link=>"http://example.com/",
306
+ :image_url=>"http://farm5.static.flickr.com/4122/4890273282_58f7c345f4.jpg",
307
+ :scope=>%{read write},
308
+ :redirect_uri=>"http://example.com/oauth/callback")
309
+ > puts "Your client identifier: #{client.id}"
310
+ > puts "Your client secret: #{client.secret}"
311
+
312
+ You may want your application to register its own client application, always
313
+ with the same client ID and secret, which are also stored in a configuration
314
+ file. For example, your +db/seed.rb+ may contain:
315
+
316
+ oauth2 = YAML.load_file(Rails.root + "config/oauth2.yml")
317
+ Rack::OAuth2::Server.register(id: oauth2["client_id"], secret: oauth2["client_secret"],
318
+ display_name: "UberClient", link: "http://example.com",
319
+ redirect_uri: "http://example.com/oauth/callback", scope: oauth2["scope"].split)
320
+
321
+ When you call +register+ with +id+ and +secret+ parameters it either registers a
322
+ new client with these specific ID and sceret, or if a client already exists,
323
+ updates its other properties.
324
+
325
+
326
+ === Step 6: Pimp Your API
327
+
328
+ I'll let you figure that one for yourself.
329
+
330
+
331
+ == Two-legged OAuth flow
332
+
333
+ Rack::OAuth2::Server also supports the so-called "two-legged" OAuth flow, which does
334
+ not require the end user authorization process. This is typically used in server to server
335
+ scenarios where no user is involved. To utilize the two-legged flow, send the grant_type of
336
+ "none" along with the client_id and client_secret to the access token path, and a new access
337
+ token will be generated (assuming the client_id and client_secret check out).
338
+
339
+
340
+ == OAuth Web Admin
341
+
342
+ We haz it, and it's pretty rad:
343
+
344
+ http://labnotes.org/wp-content/uploads/2010/11/OAuth-Admin-All-Clients.png
345
+
346
+ To get the Web admin running, you'll need to do the following. First, you'll
347
+ need to register a new client application that can access the OAuth Web admin,
348
+ with the scope +oauth-scope+ and redirect_uri that points to where you plan the
349
+ Web admin to live. This URL must end with "/admin", for example,
350
+ "http://example.com/oauth/admin".
351
+
352
+ The easiest way to do this is to run the +oauth2-sever+ command line tool:
353
+
354
+ $ oauth2-server setup --db my_db
355
+
356
+ Next, in your application, make sure to ONLY AUTHORIZE ADMINISTRATORS to access
357
+ the Web admin, by granting them access to the +oauth-admin+ scope. For example:
358
+
359
+ def grant
360
+ # Only admins allowed to authorize the scope oauth-admin
361
+ if oauth.scope.include?("oauth-admin") && !current_user.admin?
362
+ head oauth.deny!
363
+ else
364
+ head oauth.grant!(current_user.id)
365
+ end
366
+ end
367
+
368
+ Make sure you do that, or you'll allow anyone access to the OAuth Web admin.
369
+
370
+ Next, mount the OAuth Web admin as part of your application, and feed it the
371
+ client ID/secret. For example, for Rails 2.3.x add this to
372
+ +config/environment.rb+:
373
+
374
+ Rails::Initializer.run do |config|
375
+ . . .
376
+ config.after_initialize do
377
+ config.middleware.use Rack::OAuth2::Server::Admin.mount
378
+ Rack::OAuth2::Server::Admin.set :client_id, "4dca20453e4859cb000007"
379
+ Rack::OAuth2::Server::Admin.set :client_secret, "981fa734e110496fcf667cbf52fbaf03"
380
+ Rack::OAuth2::Server::Admin.set :scope, %w{read write}
381
+ end
382
+ end
383
+
384
+ For Rails 3.0.x, add this to you +config/application.rb+:
385
+
386
+ module MyApp
387
+ class Application < Rails::Application
388
+ config.after_initialize do
389
+ Rack::OAuth2::Server::Admin.set :client_id, "4dca20453e4859cb000007"
390
+ Rack::OAuth2::Server::Admin.set :client_secret, "981fa734e110496fcf667cbf52fbaf03"
391
+ Rack::OAuth2::Server::Admin.set :scope, %w{read write}
392
+ end
393
+ end
394
+ end
395
+
396
+ And add the follownig to +config/routes.rb+:
397
+
398
+ mount Rack::OAuth2::Server::Admin=>"/oauth/admin"
399
+
400
+ For Sinatra, Padrino and other Rack-based applications, you'll want to mount
401
+ like so (e.g. in +config.ru+):
402
+
403
+ Rack::Builder.new do
404
+ map("/oauth/admin") { run Rack::OAuth2::Server::Admin }
405
+ map("/") { run MyApp }
406
+ end
407
+ Rack::OAuth2::Server::Admin.set :client_id, "4dca20453e4859cb000007"
408
+ Rack::OAuth2::Server::Admin.set :client_secret, "981fa734e110496fcf667cbf52fbaf03"
409
+ Rack::OAuth2::Server::Admin.set :scope, %w{read write}
410
+
411
+ Next, open your browser to http://example.com/oauth/admin, or wherever you
412
+ mounted the Web admin.
413
+
414
+ === Web Admin Options
415
+
416
+ You can set the following options:
417
+
418
+ - +client_id+ -- Client application identified, require to authenticate.
419
+ - +client_secret+ -- Client application secret, required to authenticate.
420
+ - +authorize+ -- Endpoint for requesing authorization, defaults to /oauth/admin.
421
+ - +template_url+ -- Will map an access token identity into a URL in your
422
+ application, using the substitution value "{id}", e.g.
423
+ "http://example.com/users/#{id}")
424
+ - +force_ssl+ -- Forces all requests to use HTTPS (true by default except in
425
+ development mode).
426
+ - +scope+ -- Common scope shown and added by default to new clients (array of
427
+ names, e.g. ["read", "write"]).
428
+
429
+ === Web Admin API
430
+
431
+ The OAuth Web admin is a single-page client application that operates by
432
+ accessing the OAuth API. The API is mounted at /oauth/admin/api (basically /api
433
+ relative to the UI), you can access it yourself if you have an access token with
434
+ the scope +oauth-admin+.
435
+
436
+ The API is undocumented, but between the very simple Sinatra code that provides
437
+ he API, and just as simple Sammy.js code that consumes it, it should be easy to
438
+ piece together.
439
+
440
+
441
+ == OAuth 2.0 With Curl
442
+
443
+ The premise of OAuth 2.0 is that you can use it straight from the command line.
444
+ Let's start by creating an access token. Aside from the UI authorization flow,
445
+ OAuth 2.0 allows you to authenticate with username/password. You'll need to
446
+ register an authenticator, see step 2 above for details.
447
+
448
+ Now make a request using the client credentials and your account
449
+ username/password, e.g.:
450
+
451
+ $ curl -i http://localhost:3000/oauth/access_token \
452
+ -F grant_type=password \
453
+ -F client_id=4dca20453e4859cb000007 \
454
+ -F client_secret=981fa734e110496fcf667cbf52fbaf03 \
455
+ -F "scope=read write" \
456
+ -F username=assaf@labnotes.org \
457
+ -F password=not.telling
458
+
459
+ This will spit out a JSON document, something like this:
460
+
461
+ { "scope":"import discover contacts lists",
462
+ "access_token":"e57807eb99f8c29f60a27a75a80fec6e" }
463
+
464
+ Grab the +access_token+ value and use it. The access token is good until you
465
+ delete it from the database. Making a request using the access token:
466
+
467
+ $ curl -i http://localhost:3000/api/read -H "Authorization: OAuth e57807eb99f8c29f60a27a75a80fec6e"
468
+
469
+ Although not recommended, you can also pass the token as a query parameter, or
470
+ when making POST request, as a form field:
471
+
472
+ $ curl -i http://localhost:3000/api/read?oauth_token=e57807eb99f8c29f60a27a75a80fec6e
473
+ $ curl -i http://localhost:3000/api/update -F name=Superman -F oauth_token=e57807eb99f8c29f60a27a75a80fec6e
474
+
475
+ You'll need to set the option +param_authentication+ to true. Watch out, since
476
+ this query parameter could conflict with OAuth 1.0 authorization responses that
477
+ also use +oauth_token+ for a different purpose.
478
+
479
+ Here's a neat trick. You can create a +.curlrc+ file and load it using the +-K+ option:
480
+
481
+ $ cat .curlrc
482
+ header = "Authorization: OAuth e57807eb99f8c29f60a27a75a80fec6e"
483
+ $ curl -i http://localhost:3000/api/read -K .curlrc
484
+
485
+ If you create +.curlrc+ in your home directory, +curl+ will automatically load it.
486
+ Convenient, but dangerous, you might end up sending the access token to any
487
+ server you +curl+. Useful for development, testing, just don't use it with any
488
+ production access tokens.
489
+
490
+
491
+ == Methods You'll Want To Use From Your App
492
+
493
+ You can use the Server module to create, fetch and otherwise work with access
494
+ tokens and grants. Available methods include:
495
+
496
+ - access_grant -- Creates and returns a new access grant. You can use that for
497
+ one-time token, e.g. users who forgot their password and need to login using
498
+ an email message.
499
+ - token_for -- Returns access token for particular identity. You can use that to
500
+ give access tokens to clients other than through the OAuth 2.0 protocol, e.g.
501
+ if you let users authenticate using Facebook Connect or Twitter OAuth.
502
+ - get_access_token -- Resolves access token (string) into access token
503
+ (AccessToken object).
504
+ - list_access_tokens -- Returns all access tokens for a given identity, which
505
+ you'll need if you offer a UI for uses to review and revoke access tokens they
506
+ previously granted.
507
+ - get_client -- Resolves client identifier into a Client object.
508
+ - register -- Registers a new client application. Can also be used to change
509
+ existing registration (if you know the client's ID and secret). Idempotent, so
510
+ perfect for running during setup and migration.
511
+ - get_auth_request -- Resolves authorization request handle into an AuthRequest
512
+ object. Could be useful during the authorization flow.
513
+
514
+
515
+ == Mandatory ASCII Diagram
516
+
517
+ This is briefly what the authorization flow looks like, how the workload is
518
+ split between Rack::OAuth2::Server and your application, and the protocol the
519
+ two use to control the authorization flow:
520
+
521
+ Rack::OAuth2::Server
522
+ ----------------------- -----------------------
523
+ Client app | /oauth/authorize | | Set request.env |
524
+ redirect -> | | -> | | ->
525
+ | authenticate client | | oauth.authorization |
526
+ ----------------------- -----------------------
527
+
528
+ Your code
529
+ -------------------- ---------------------- -----------------------
530
+ | Authenticate user | | Ask user to grant/ | | Set response |
531
+ -> | | -> | deny client access | -> | | ->
532
+ | | | to their account | | oauth.authorization |
533
+ | | | | | oauth.identity |
534
+ -------------------- ---------------------- -----------------------
535
+
536
+ Rack::OAuth2::Server
537
+ -----------------------
538
+ | Create access grant |
539
+ -> | or access token for | -> Redirect back
540
+ | oauth.identity | to client app
541
+ -----------------------
542
+
543
+
544
+ == Understanding the Models
545
+
546
+ === Client
547
+
548
+ The {Rack::OAuth2::Server::Client} model represents the credentials of a client
549
+ application. There are two pairs: the client identifier and secret, which the
550
+ client uses to identify itself to the authorization server, and the display
551
+ name and URL, which the client uses to identify itself to the end user.
552
+
553
+ The client application is not tied to a single Client record. Specifically, if
554
+ the client credentials are compromised, you'll want to revoke it and create a
555
+ new Client with new pair of identifier/secret. You can leave the revoked
556
+ instance around.
557
+
558
+ Calling +revoke!+ on the client revokes access using these credential pair, and
559
+ also revokes any outstanding authorization requests, access grants and access
560
+ tokens created using these credentials.
561
+
562
+ You may also want to register a redirect URI. If registered, the client is only
563
+ able to request authorization that redirect back to that redirect URI.
564
+
565
+ === Authorization Request
566
+
567
+ The authorization process may involve multiple requests, and the application
568
+ must maintain the authorization request details from beginning to end.
569
+
570
+ To keep the application simple, all the necessary information for a single
571
+ authorization request is stored in the {Rack::OAuth2::Server::AuthRequest}
572
+ model. The application only needs to keep track of the authorization request
573
+ identifier.
574
+
575
+ Granting an authorization request (by calling +grant!+) creates an access grant or
576
+ access token, depending on the requested response type, and associates it with
577
+ the identity.
578
+
579
+ === Access Grant
580
+
581
+ An access grant ({Rack::OAuth2::Server::AccessGrant}) is a nonce use to
582
+ generate access token. This model keeps track of the nonce (the "authorization
583
+ code") and all the data it needs to create an access token.
584
+
585
+ === Access Token
586
+
587
+ An access token allows the client to access the resource with the given scope
588
+ on behalf of a given identity. It keeps track of the account identifier
589
+ (supplied by the application), client identifier and scope (both supplied by
590
+ the client).
591
+
592
+ An {Rack::OAuth2::Server::AccessToken} is created by copying values from an
593
+ +AuthRequest+ or +AccessGrant+, and remains in effect until revoked. (OAuth 2.0
594
+ access tokens can also expire, but we don't support expiration at the moment)
595
+
596
+
597
+ == Credits
598
+
599
+ Rack::OAuth2::Server was written to provide authorization/authentication for
600
+ the new Flowtown API[http://developer.flowtown.com]. Thanks to
601
+ Flowtown[http://flowtown.com] for making it happen and allowing it to be open
602
+ sourced.
603
+
604
+ Rack::OAuth2::Server is available under the MIT license.