oauth2-provider-jonrowe 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +314 -0
- data/example/README.rdoc +11 -0
- data/example/application.rb +151 -0
- data/example/config.ru +3 -0
- data/example/environment.rb +11 -0
- data/example/models/connection.rb +9 -0
- data/example/models/note.rb +4 -0
- data/example/models/user.rb +6 -0
- data/example/public/style.css +78 -0
- data/example/schema.rb +27 -0
- data/example/views/authorize.erb +28 -0
- data/example/views/create_user.erb +3 -0
- data/example/views/home.erb +25 -0
- data/example/views/layout.erb +25 -0
- data/example/views/login.erb +20 -0
- data/example/views/new_client.erb +25 -0
- data/example/views/new_user.erb +22 -0
- data/example/views/show_client.erb +15 -0
- data/lib/oauth2/model.rb +17 -0
- data/lib/oauth2/model/authorization.rb +113 -0
- data/lib/oauth2/model/client.rb +55 -0
- data/lib/oauth2/model/client_owner.rb +13 -0
- data/lib/oauth2/model/hashing.rb +27 -0
- data/lib/oauth2/model/resource_owner.rb +26 -0
- data/lib/oauth2/model/schema.rb +42 -0
- data/lib/oauth2/provider.rb +117 -0
- data/lib/oauth2/provider/access_token.rb +66 -0
- data/lib/oauth2/provider/authorization.rb +168 -0
- data/lib/oauth2/provider/error.rb +29 -0
- data/lib/oauth2/provider/exchange.rb +212 -0
- data/lib/oauth2/router.rb +60 -0
- data/spec/factories.rb +27 -0
- data/spec/oauth2/model/authorization_spec.rb +216 -0
- data/spec/oauth2/model/client_spec.rb +55 -0
- data/spec/oauth2/model/resource_owner_spec.rb +55 -0
- data/spec/oauth2/provider/access_token_spec.rb +125 -0
- data/spec/oauth2/provider/authorization_spec.rb +323 -0
- data/spec/oauth2/provider/exchange_spec.rb +330 -0
- data/spec/oauth2/provider_spec.rb +531 -0
- data/spec/request_helpers.rb +46 -0
- data/spec/spec_helper.rb +44 -0
- data/spec/test_app/helper.rb +33 -0
- data/spec/test_app/provider/application.rb +61 -0
- data/spec/test_app/provider/views/authorize.erb +19 -0
- metadata +220 -0
data/README.rdoc
ADDED
@@ -0,0 +1,314 @@
|
|
1
|
+
= OAuth2::Provider
|
2
|
+
|
3
|
+
This gem provides a toolkit for adding OAuth2 provider capabilities to a Ruby
|
4
|
+
web app. It handles most of the protocol for you: it is designed to provide
|
5
|
+
a sufficient level of abstraction that it can implement updates to the protocol
|
6
|
+
without affecting your application code at all. All you have to deal with is
|
7
|
+
authenticating your users and letting them grant access to client apps.
|
8
|
+
|
9
|
+
It is also designed to be usable within any web frontend, at least those of
|
10
|
+
Rails and Sinatra. It assumes very little about the request objects in your
|
11
|
+
environment, namely they:
|
12
|
+
|
13
|
+
* respond to <tt>#params</tt> with a <tt>Hash</tt> of request parameters
|
14
|
+
* respond to <tt>#get?</tt> and <tt>#post?</tt>
|
15
|
+
* respond to <tt>#url</tt> with the full URL string of the request
|
16
|
+
* respond to <tt>#env</tt>, returning the HTTP environment <tt>Hash</tt>
|
17
|
+
|
18
|
+
It stores the clients and authorizations using ActiveRecord; the schema is in
|
19
|
+
<tt>lib/oauth2/model/schema.rb</tt>. Run it to update your database to
|
20
|
+
store OAuth data:
|
21
|
+
|
22
|
+
OAuth2::Model::Schema.up
|
23
|
+
|
24
|
+
The current imeplementation is based on draft-10[http://tools.ietf.org/html/draft-ietf-oauth-v2-10].
|
25
|
+
|
26
|
+
|
27
|
+
== Usage
|
28
|
+
|
29
|
+
A basic example is in <tt>example/application.rb</tt>. To implement OAuth, you
|
30
|
+
need to provide four things:
|
31
|
+
|
32
|
+
* Some UI to register client applications
|
33
|
+
* The OAuth request endpoint
|
34
|
+
* A flow for logged-in users to grant access to clients
|
35
|
+
* Resources protected by access tokens
|
36
|
+
|
37
|
+
|
38
|
+
=== Configuration
|
39
|
+
|
40
|
+
<tt>OAuth2::Provider</tt> requires very little configuration. The only thing it
|
41
|
+
needs to know about your app is its name, which is used in the headers for some
|
42
|
+
authentication errors. To load the library, just do this:
|
43
|
+
|
44
|
+
require 'oauth2/provider'
|
45
|
+
OAuth2::Provider.realm = 'My OAuth app'
|
46
|
+
|
47
|
+
You may also need to configure assertion handlers if your application supports
|
48
|
+
third-party access credentials. See 'Using Assertions' below.
|
49
|
+
|
50
|
+
|
51
|
+
=== Registering client applications
|
52
|
+
|
53
|
+
Clients are modelled by the <tt>OAuth2::Model::Client</tt> class, which is an
|
54
|
+
ActiveRecord model. You just need to implement a UI for creating them, for
|
55
|
+
example in a Sinatra app:
|
56
|
+
|
57
|
+
get '/oauth/apps/new' do
|
58
|
+
@client = OAuth2::Model::Client.new
|
59
|
+
erb :new_client
|
60
|
+
end
|
61
|
+
|
62
|
+
post '/oauth/apps' do
|
63
|
+
@client = OAuth2::Model::Client.new(params)
|
64
|
+
@client.save ? erb(:show_client) : erb(:new_client)
|
65
|
+
end
|
66
|
+
|
67
|
+
Client applications must have a <tt>name</tt> and a <tt>redirect_uri</tt>:
|
68
|
+
provide fields for editing these but do not allow the other fields to be edited,
|
69
|
+
since they are the client's access credentials. When you've created the client,
|
70
|
+
you should show its details to the user registering the client: its <tt>name</tt>,
|
71
|
+
<tt>redirect_uri</tt>, <tt>client_id</tt> and <tt>client_secret</tt> (the last
|
72
|
+
two are generated for you). <tt>client_secret</tt> is not stored in plain text
|
73
|
+
so you can only read it when you initially create the client object.
|
74
|
+
|
75
|
+
|
76
|
+
=== OAuth request endpoint
|
77
|
+
|
78
|
+
This is a path that your application exposes in order for clients to communicate
|
79
|
+
with your application. It is also the page that the client will send users to
|
80
|
+
so they can authenticate and grant access. Many requests to this endpoint will
|
81
|
+
be protocol-level requests that do not involve the user, and <tt>OAuth2::Provider</tt>
|
82
|
+
gives you a generic way to handle all that.
|
83
|
+
|
84
|
+
You should use this to get the right response, status code and headers to send to
|
85
|
+
the client. In the event that <tt>OAuth2::Provider</tt> does not provide a response,
|
86
|
+
you should render a page that lets the user begin to authenticate and grant access.
|
87
|
+
|
88
|
+
This endpoint must be accessible via GET and POST. In this example we will expose
|
89
|
+
the OAuth service through the path <tt>/oauth/authorize</tt>. We check if there is
|
90
|
+
a logged-in resource owner and give this to <tt>OAuth::Provider</tt>, since we
|
91
|
+
may be able to immediately redirect if the user has already authorized the client:
|
92
|
+
|
93
|
+
[:get, :post].each do |method|
|
94
|
+
__send__ method, '/oauth/authorize' do
|
95
|
+
@owner = User.find_by_id(session[:user_id])
|
96
|
+
@oauth2 = OAuth2::Provider.parse(@owner, request)
|
97
|
+
|
98
|
+
redirect @oauth2.redirect_uri if @oauth2.redirect?
|
99
|
+
|
100
|
+
headers @oauth2.response_headers
|
101
|
+
status @oauth2.response_status
|
102
|
+
|
103
|
+
@oauth2.response_body || erb(:login)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
There is a set of parameters that you will need to hold on to for when your app
|
108
|
+
needs to redirect back to the client. You could store them in the session, or
|
109
|
+
pass them through forms as the user completes the flow. For example to embed
|
110
|
+
them in the login form, do this:
|
111
|
+
|
112
|
+
<% @oauth2.params.each do |key, value| %>
|
113
|
+
<input type="hidden" name="<%= key %>" value="<%= value %>">
|
114
|
+
<% end %>
|
115
|
+
|
116
|
+
You may also want to use scopes to provide granular access to your domain using
|
117
|
+
<i>scopes</i>. The <tt>@oauth2</tt> object exposes the scopes the client has asked
|
118
|
+
for so you can display them to the user:
|
119
|
+
|
120
|
+
<p>The application <%= @oauth2.client.name %> wants the following permissions:</p>
|
121
|
+
|
122
|
+
<ul>
|
123
|
+
<% @oauth2.scopes.each do |scope| %>
|
124
|
+
<li><%= PERMISSION_UI_STRINGS[scope] %></li>
|
125
|
+
<% end %>
|
126
|
+
</ul>
|
127
|
+
|
128
|
+
You can also use the method <tt>@oauth2.unauthorized_scopes</tt> to get the list
|
129
|
+
of scopes the user has not already granted to the client, in the case where the
|
130
|
+
client already has some authorization. If no prior authorization exists between
|
131
|
+
the user and the client, <tt>@oauth2.unauthorized_scopes</tt> just returns all
|
132
|
+
the scopes the client has asked for.
|
133
|
+
|
134
|
+
|
135
|
+
=== Granting access to clients
|
136
|
+
|
137
|
+
Your application will probably have some concept of a user, or a <i>resource
|
138
|
+
owner</i> in OAuth lingo. Add this mixin to the model that represents your
|
139
|
+
users:
|
140
|
+
|
141
|
+
class User < ActiveRecord::Base
|
142
|
+
include OAuth2::Model::ResourceOwner
|
143
|
+
end
|
144
|
+
|
145
|
+
This just adds a couple of relations and methods to the model to let it interact
|
146
|
+
with the <tt>OAuth2</tt> models.
|
147
|
+
|
148
|
+
Once the user has authenticated you should show them a page to let them grant
|
149
|
+
or deny access to the client application. This is straightforward; let's say
|
150
|
+
the user checks a box before posting a form to indicate their intent:
|
151
|
+
|
152
|
+
post '/oauth/allow' do
|
153
|
+
@user = User.find_by_id(session[:user_id])
|
154
|
+
@auth = OAuth2::Provider::Authorization.new(@user, params)
|
155
|
+
|
156
|
+
if params['allow'] == '1'
|
157
|
+
@auth.grant_access!
|
158
|
+
else
|
159
|
+
@auth.deny_access!
|
160
|
+
end
|
161
|
+
redirect @auth.redirect_uri
|
162
|
+
end
|
163
|
+
|
164
|
+
After granting or denying access, we just redirect back to the client using a
|
165
|
+
URI that <tt>OAuth2::Provider</tt> will provide for you.
|
166
|
+
|
167
|
+
|
168
|
+
=== Using password credentials
|
169
|
+
|
170
|
+
If you like, OAuth lets you use a user's login credentials to authenticate with
|
171
|
+
a provider. In this case the client application must request these credentials
|
172
|
+
directly from the user and then post them to the exchange endpoint. On the
|
173
|
+
provider side you can handle this using the <tt>handle_passwords</tt> and
|
174
|
+
<tt>grant_access!</tt> API methods, for example:
|
175
|
+
|
176
|
+
OAuth2::Provider.handle_passwords do |client, username, password|
|
177
|
+
user = User.find_by_username(username)
|
178
|
+
if user.authenticate?(password)
|
179
|
+
user.grant_access!(client)
|
180
|
+
else
|
181
|
+
nil
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
The block must return <tt>user.grant_access!(client)</tt> if you want to allow
|
186
|
+
access, otherwise it should return <tt>nil</tt>.
|
187
|
+
|
188
|
+
|
189
|
+
=== Using assertions
|
190
|
+
|
191
|
+
Assertions provide a way to access your OAuth services using user credentials
|
192
|
+
from another service. When using assertions, the user will not authenticate on
|
193
|
+
your web site; the OAuth client will authenticate the user using some other
|
194
|
+
framework and obtain a token, then exchange this token for an access token on
|
195
|
+
your domain.
|
196
|
+
|
197
|
+
For example, a client application may let a user authenticate using Facebook,
|
198
|
+
so the application obtains a Facebook access token from the user. The client
|
199
|
+
would then pass this token to your OAuth endpoint and exchange it for an
|
200
|
+
access token from your site. You will typically create an account in your
|
201
|
+
database to represent this, then have that new account grant access to the
|
202
|
+
client.
|
203
|
+
|
204
|
+
To use assertions, you must tell <tt>OAuth2::Provider</tt> how to handle
|
205
|
+
assertions based on their type. An assertion type must be a valid URI. For
|
206
|
+
the Facebook example we'd do the following. The block yields the <tt>Client</tt>
|
207
|
+
object making the exchange request, and the value of the assertion, which in
|
208
|
+
this example will be a Facebook access token.
|
209
|
+
|
210
|
+
OAuth2::Provider.handle_assertions 'https://graph.facebook.com/me' do |client, assertion|
|
211
|
+
facebook = URI.parse('https://graph.facebook.com/me?access_token=' + assertion)
|
212
|
+
response = Net::HTTP.get_response(facebook)
|
213
|
+
|
214
|
+
user_data = JSON.parse(response.body)
|
215
|
+
account = User.from_facebook_data(user_data)
|
216
|
+
|
217
|
+
account.grant_access!(client)
|
218
|
+
end
|
219
|
+
|
220
|
+
This code should run when your app boots, not during a request handler - think
|
221
|
+
of it as configuration for <tt>OAuth2::Provider</tt>. The framework will invoke
|
222
|
+
it when a client attempts to use assertions with your OAuth endpoint.
|
223
|
+
|
224
|
+
The final call in your handler should be to <tt>grant_access!</tt>; this returns
|
225
|
+
an <tt>Authorization</tt> object that the framework then uses to complete the
|
226
|
+
response to the client. If you want to deny the request for whatever reason, the
|
227
|
+
block must return <tt>nil</tt>. If a client tries to use an assertion type you
|
228
|
+
have no handler for, the client will get an error response.
|
229
|
+
|
230
|
+
|
231
|
+
=== Protecting resources with access tokens
|
232
|
+
|
233
|
+
To protect the user's resources you need to check for access tokens. This is
|
234
|
+
simple, for example a call to get a user's notes:
|
235
|
+
|
236
|
+
get '/user/:username/notes' do
|
237
|
+
user = User.find_by_username(params[:username])
|
238
|
+
token = OAuth2::Provider.access_token(user, ['read_notes'], request)
|
239
|
+
|
240
|
+
headers token.response_headers
|
241
|
+
status token.response_status
|
242
|
+
|
243
|
+
if token.valid?
|
244
|
+
JSON.unparse('notes' => user.notes)
|
245
|
+
else
|
246
|
+
JSON.unparse('error' => 'No notes for you!')
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
<tt>OAuth2::Provider.access_token()</tt> takes a <tt>ResourceOwner</tt>, a list
|
251
|
+
of scopes required to access the resource, and a request object. If the token
|
252
|
+
was not granted for the required scopes, has expired or is simply invalid,
|
253
|
+
headers and a status code are set to indicate this to the client. <tt>token.valid?</tt>
|
254
|
+
is the call you should use to determine whether to server the request or not.
|
255
|
+
|
256
|
+
It is also common to provide a dynamic resource for getting some basic data
|
257
|
+
about a user by supplying their access token. This can be done by passing
|
258
|
+
<tt>nil</tt> as the resource owner:
|
259
|
+
|
260
|
+
get '/me' do
|
261
|
+
token = OAuth2::Provider.access_token(nil, [], request)
|
262
|
+
if token.valid?
|
263
|
+
JSON.unparse('username' => token.owner.username)
|
264
|
+
else
|
265
|
+
JSON.unparse('error' => 'Keep out!')
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
<tt>token.owner</tt> returns the <tt>ResourceOwner</tt> that issued the token.
|
270
|
+
A token represents the fact that a single owner gave a single client a set of
|
271
|
+
permissions.
|
272
|
+
|
273
|
+
|
274
|
+
=== Transport security
|
275
|
+
|
276
|
+
Your application should ensure that any endpoint that receives or returns OAuth
|
277
|
+
data is only accessible over a secure transport such as the <tt>https:</tt>
|
278
|
+
protocol. <tt>OAuth2::Provider</tt> can enforces this to make it easier to keep
|
279
|
+
your users' data secure. If you want to enable these behaviours, set
|
280
|
+
<tt>OAuth2::Provider.enforce_ssl = true</tt>.
|
281
|
+
|
282
|
+
* The <tt>OAuth2::Provider.parse</tt> method will produce error responses and
|
283
|
+
will not process the incoming request unless the request was made using the
|
284
|
+
<tt>https:</tt> protocol.
|
285
|
+
* An access token constructed using <tt>OAuth2::Provider.access_token</tt> will
|
286
|
+
return <tt>false</tt> for <tt>#valid?</tt> unless the request was made using the
|
287
|
+
<tt>https:</tt> protocol.
|
288
|
+
* Any access token received over an insecure connection is immediately destroyed
|
289
|
+
to prevent eavesdroppers getting access to the user's resources. A client
|
290
|
+
making an insecure request will have to send the user through the authorization
|
291
|
+
process again to get a new token.
|
292
|
+
|
293
|
+
|
294
|
+
== License
|
295
|
+
|
296
|
+
Copyright (c) 2010-2011 Songkick.com
|
297
|
+
|
298
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
299
|
+
of this software and associated documentation files (the "Software"), to deal
|
300
|
+
in the Software without restriction, including without limitation the rights
|
301
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
302
|
+
copies of the Software, and to permit persons to whom the Software is
|
303
|
+
furnished to do so, subject to the following conditions:
|
304
|
+
|
305
|
+
The above copyright notice and this permission notice shall be included in
|
306
|
+
all copies or substantial portions of the Software.
|
307
|
+
|
308
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
309
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
310
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
311
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
312
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
313
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
314
|
+
THE SOFTWARE.
|
data/example/README.rdoc
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
dir = File.expand_path(File.dirname(__FILE__))
|
2
|
+
require dir + '/environment'
|
3
|
+
|
4
|
+
require 'sinatra'
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
set :static, true
|
8
|
+
set :public, dir + '/public'
|
9
|
+
set :views, dir + '/views'
|
10
|
+
enable :sessions
|
11
|
+
|
12
|
+
PERMISSIONS = {
|
13
|
+
'read_notes' => 'Read all your notes'
|
14
|
+
}
|
15
|
+
|
16
|
+
ERROR_RESPONSE = JSON.unparse('error' => 'No soup for you!')
|
17
|
+
|
18
|
+
get('/') { erb(:home) }
|
19
|
+
|
20
|
+
|
21
|
+
get '/users/new' do
|
22
|
+
@user = User.new
|
23
|
+
erb :new_user
|
24
|
+
end
|
25
|
+
|
26
|
+
post '/users/create' do
|
27
|
+
@user = User.create(params)
|
28
|
+
if @user.save
|
29
|
+
erb :create_user
|
30
|
+
else
|
31
|
+
erb :new_user
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
#================================================================
|
36
|
+
# Register applications
|
37
|
+
|
38
|
+
get '/oauth/apps/new' do
|
39
|
+
@client = OAuth2::Model::Client.new
|
40
|
+
erb :new_client
|
41
|
+
end
|
42
|
+
|
43
|
+
post '/oauth/apps' do
|
44
|
+
@client = OAuth2::Model::Client.new(params)
|
45
|
+
if @client.save
|
46
|
+
session[:client_secret] = @client.client_secret
|
47
|
+
redirect("/oauth/apps/#{@client.id}")
|
48
|
+
else
|
49
|
+
erb :new_client
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
get '/oauth/apps/:id' do
|
54
|
+
@client = OAuth2::Model::Client.find_by_id(params[:id])
|
55
|
+
@client_secret = session[:client_secret]
|
56
|
+
erb :show_client
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
#================================================================
|
61
|
+
# OAuth 2.0 flow
|
62
|
+
|
63
|
+
# Initial request exmample:
|
64
|
+
# /oauth/authorize?response_type=token&client_id=7uljxxdgsksmecn5cycvug46v&redirect_uri=http%3A%2F%2Fexample.com%2Fcb&scope=read_notes
|
65
|
+
[:get, :post].each do |method|
|
66
|
+
__send__ method, '/oauth/authorize' do
|
67
|
+
@user = User.find_by_id(session[:user_id])
|
68
|
+
@oauth2 = OAuth2::Provider.parse(@user, request)
|
69
|
+
redirect @oauth2.redirect_uri if @oauth2.redirect?
|
70
|
+
|
71
|
+
headers @oauth2.response_headers
|
72
|
+
status @oauth2.response_status
|
73
|
+
|
74
|
+
@oauth2.response_body || erb(:login)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
post '/login' do
|
79
|
+
@user = User.find_by_username(params[:username])
|
80
|
+
@oauth2 = OAuth2::Provider.parse(@user, request)
|
81
|
+
session[:user_id] = @user.id
|
82
|
+
erb(@user ? :authorize : :login)
|
83
|
+
end
|
84
|
+
|
85
|
+
post '/oauth/allow' do
|
86
|
+
@user = User.find_by_id(session[:user_id])
|
87
|
+
@auth = OAuth2::Provider::Authorization.new(@user, params)
|
88
|
+
if params['allow'] == '1'
|
89
|
+
@auth.grant_access!
|
90
|
+
else
|
91
|
+
@auth.deny_access!
|
92
|
+
end
|
93
|
+
redirect @auth.redirect_uri
|
94
|
+
end
|
95
|
+
|
96
|
+
#================================================================
|
97
|
+
# Domain API
|
98
|
+
|
99
|
+
get '/me' do
|
100
|
+
authorization = OAuth2::Provider.access_token(nil, [], request)
|
101
|
+
headers authorization.response_headers
|
102
|
+
status authorization.response_status
|
103
|
+
|
104
|
+
if authorization.valid?
|
105
|
+
user = authorization.owner
|
106
|
+
JSON.unparse('username' => user.username)
|
107
|
+
else
|
108
|
+
ERROR_RESPONSE
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
get '/users/:username/notes' do
|
113
|
+
verify_access :read_notes do |user|
|
114
|
+
notes = user.notes.map do |n|
|
115
|
+
{:note_id => n.id, :url => "#{host}/users/#{user.username}/notes/#{n.id}"}
|
116
|
+
end
|
117
|
+
JSON.unparse(:notes => notes)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
get '/users/:username/notes/:note_id' do
|
122
|
+
verify_access :read_notes do |user|
|
123
|
+
note = user.notes.find_by_id(params[:note_id])
|
124
|
+
note ? note.to_json : JSON.unparse(:error => 'No such note')
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
|
130
|
+
helpers do
|
131
|
+
#================================================================
|
132
|
+
# Check for OAuth access before rendering a resource
|
133
|
+
def verify_access(scope)
|
134
|
+
user = User.find_by_username(params[:username])
|
135
|
+
token = OAuth2::Provider.access_token(user, [scope.to_s], request)
|
136
|
+
|
137
|
+
headers token.response_headers
|
138
|
+
status token.response_status
|
139
|
+
|
140
|
+
return ERROR_RESPONSE unless token.valid?
|
141
|
+
|
142
|
+
yield user
|
143
|
+
end
|
144
|
+
|
145
|
+
#================================================================
|
146
|
+
# Return the full app domain
|
147
|
+
def host
|
148
|
+
request.scheme + '://' + request.host_with_port
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|