tpitale-rack-oauth2-server 2.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +202 -0
- data/Gemfile +16 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +604 -0
- data/Rakefile +90 -0
- data/VERSION +1 -0
- data/bin/oauth2-server +206 -0
- data/lib/rack-oauth2-server.rb +4 -0
- data/lib/rack/oauth2/admin/css/screen.css +347 -0
- data/lib/rack/oauth2/admin/images/loading.gif +0 -0
- data/lib/rack/oauth2/admin/images/oauth-2.png +0 -0
- data/lib/rack/oauth2/admin/js/application.coffee +220 -0
- data/lib/rack/oauth2/admin/js/jquery.js +166 -0
- data/lib/rack/oauth2/admin/js/jquery.tmpl.js +414 -0
- data/lib/rack/oauth2/admin/js/protovis-r3.2.js +277 -0
- data/lib/rack/oauth2/admin/js/sammy.js +5 -0
- data/lib/rack/oauth2/admin/js/sammy.json.js +5 -0
- data/lib/rack/oauth2/admin/js/sammy.oauth2.js +142 -0
- data/lib/rack/oauth2/admin/js/sammy.storage.js +5 -0
- data/lib/rack/oauth2/admin/js/sammy.title.js +5 -0
- data/lib/rack/oauth2/admin/js/sammy.tmpl.js +5 -0
- data/lib/rack/oauth2/admin/js/underscore.js +722 -0
- data/lib/rack/oauth2/admin/views/client.tmpl +58 -0
- data/lib/rack/oauth2/admin/views/clients.tmpl +52 -0
- data/lib/rack/oauth2/admin/views/edit.tmpl +80 -0
- data/lib/rack/oauth2/admin/views/index.html +39 -0
- data/lib/rack/oauth2/admin/views/no_access.tmpl +4 -0
- data/lib/rack/oauth2/models.rb +27 -0
- data/lib/rack/oauth2/models/access_grant.rb +54 -0
- data/lib/rack/oauth2/models/access_token.rb +129 -0
- data/lib/rack/oauth2/models/auth_request.rb +61 -0
- data/lib/rack/oauth2/models/client.rb +93 -0
- data/lib/rack/oauth2/rails.rb +105 -0
- data/lib/rack/oauth2/server.rb +458 -0
- data/lib/rack/oauth2/server/admin.rb +250 -0
- data/lib/rack/oauth2/server/errors.rb +104 -0
- data/lib/rack/oauth2/server/helper.rb +147 -0
- data/lib/rack/oauth2/server/practice.rb +79 -0
- data/lib/rack/oauth2/server/railtie.rb +24 -0
- data/lib/rack/oauth2/server/utils.rb +30 -0
- data/lib/rack/oauth2/sinatra.rb +71 -0
- data/rack-oauth2-server.gemspec +24 -0
- data/rails/init.rb +11 -0
- data/test/admin/api_test.rb +228 -0
- data/test/admin/ui_test.rb +38 -0
- data/test/oauth/access_grant_test.rb +276 -0
- data/test/oauth/access_token_test.rb +311 -0
- data/test/oauth/authorization_test.rb +298 -0
- data/test/oauth/server_methods_test.rb +292 -0
- data/test/rails2/app/controllers/api_controller.rb +40 -0
- data/test/rails2/app/controllers/application_controller.rb +2 -0
- data/test/rails2/app/controllers/oauth_controller.rb +17 -0
- data/test/rails2/config/environment.rb +19 -0
- data/test/rails2/config/environments/test.rb +0 -0
- data/test/rails2/config/routes.rb +13 -0
- data/test/rails3/app/controllers/api_controller.rb +40 -0
- data/test/rails3/app/controllers/application_controller.rb +2 -0
- data/test/rails3/app/controllers/oauth_controller.rb +17 -0
- data/test/rails3/config/application.rb +19 -0
- data/test/rails3/config/environment.rb +2 -0
- data/test/rails3/config/routes.rb +12 -0
- data/test/setup.rb +120 -0
- data/test/sinatra/my_app.rb +69 -0
- metadata +145 -0
data/CHANGELOG
ADDED
@@ -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
data/MIT-LICENSE
ADDED
@@ -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
|
+
|
data/README.rdoc
ADDED
@@ -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.
|