tpitale-rack-oauth2-server 2.2.1

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 (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.