rack-oauth2-server 1.0.beta
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/CHANGELOG +3 -0
- data/Gemfile +17 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +423 -0
- data/Rakefile +60 -0
- data/lib/rack-oauth2-server.rb +1 -0
- data/lib/rack/oauth2/models.rb +37 -0
- data/lib/rack/oauth2/models/access_grant.rb +75 -0
- data/lib/rack/oauth2/models/access_token.rb +65 -0
- data/lib/rack/oauth2/models/auth_request.rb +88 -0
- data/lib/rack/oauth2/models/client.rb +73 -0
- data/lib/rack/oauth2/rails.rb +105 -0
- data/lib/rack/oauth2/server.rb +312 -0
- data/lib/rack/oauth2/server/errors.rb +97 -0
- data/lib/rack/oauth2/server/helper.rb +142 -0
- data/lib/rack/oauth2/server/utils.rb +24 -0
- data/lib/rack/oauth2/server/version.rb +9 -0
- data/lib/rack/oauth2/sinatra.rb +71 -0
- data/rack-oauth2-server.gemspec +25 -0
- data/test/access_grant_test.rb +216 -0
- data/test/access_token_test.rb +237 -0
- data/test/authorization_test.rb +267 -0
- data/test/rails/app/controllers/api_controller.rb +40 -0
- data/test/rails/app/controllers/application_controller.rb +4 -0
- data/test/rails/app/controllers/oauth_controller.rb +14 -0
- data/test/rails/config/environment.rb +12 -0
- data/test/rails/config/environments/test.rb +0 -0
- data/test/rails/config/routes.rb +13 -0
- data/test/rails/log/test.log +14710 -0
- data/test/setup.rb +73 -0
- data/test/sinatra/my_app.rb +67 -0
- metadata +148 -0
@@ -0,0 +1,312 @@
|
|
1
|
+
require "rack/oauth2/models"
|
2
|
+
require "rack/oauth2/server/errors"
|
3
|
+
require "rack/oauth2/server/utils"
|
4
|
+
require "rack/oauth2/server/helper"
|
5
|
+
require "rack/oauth2/server/version"
|
6
|
+
|
7
|
+
|
8
|
+
module Rack
|
9
|
+
module OAuth2
|
10
|
+
|
11
|
+
# Implements an OAuth 2 Authorization Server, based on http://tools.ietf.org/html/draft-ietf-oauth-v2-10
|
12
|
+
class Server
|
13
|
+
|
14
|
+
class << self
|
15
|
+
# Return AuthRequest from authorization request handle.
|
16
|
+
def get_auth_request(authorization)
|
17
|
+
AuthRequest.find(authorization)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns Client from client identifier.
|
21
|
+
def get_client(client_id)
|
22
|
+
Client.find(client_id)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns AccessToken from token.
|
26
|
+
def get_access_token(token)
|
27
|
+
AccessToken.from_token(token)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns all AccessTokens for a resource.
|
31
|
+
def list_access_tokens(resource)
|
32
|
+
AccessToken.from_resource(resource)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(app, options = {}, &authenticator)
|
37
|
+
@app = app
|
38
|
+
@options = options
|
39
|
+
@options[:authenticator] ||= authenticator
|
40
|
+
@options[:access_token_path] ||= "/oauth/access_token"
|
41
|
+
@options[:authorize_path] ||= "/oauth/authorize"
|
42
|
+
@options[:authorization_types] ||= %w{code token}
|
43
|
+
end
|
44
|
+
|
45
|
+
# Options are:
|
46
|
+
# - :access_token_path -- Path for requesting access token. By convention
|
47
|
+
# defaults to /oauth/access_token.
|
48
|
+
# - :authenticator -- For username/password authorization. A block that
|
49
|
+
# receives the credentials and returns resource string (e.g. user ID) or
|
50
|
+
# nil.
|
51
|
+
# - :authorization_types -- Array of supported authorization types.
|
52
|
+
# Defaults to ["code", "token"], and you can change it to just one of
|
53
|
+
# these names.
|
54
|
+
# - :authorize_path -- Path for requesting end-user authorization. By
|
55
|
+
# convention defaults to /oauth/authorize.
|
56
|
+
# - :database -- Mongo::DB instance.
|
57
|
+
# - :realm -- Authorization realm that will show up in 401 responses.
|
58
|
+
# Defaults to use the request host name.
|
59
|
+
# - :scopes -- Array listing all supported scopes, e.g. %w{read write}.
|
60
|
+
# - :logger -- The logger to use. Under Rails, defaults to use the Rails
|
61
|
+
# logger. Will use Rack::Logger if available.
|
62
|
+
attr_reader :options
|
63
|
+
|
64
|
+
def call(env)
|
65
|
+
# Use options[:database] if specified.
|
66
|
+
org_database, Server.database = Server.database, options[:database] || Server.database
|
67
|
+
logger = options[:logger] || env["rack.logger"]
|
68
|
+
request = OAuthRequest.new(env)
|
69
|
+
|
70
|
+
# 3. Obtaining End-User Authorization
|
71
|
+
# Flow starts here.
|
72
|
+
return request_authorization(request, logger) if request.path == options[:authorize_path]
|
73
|
+
# 4. Obtaining an Access Token
|
74
|
+
return respond_with_access_token(request, logger) if request.path == options[:access_token_path]
|
75
|
+
|
76
|
+
# 5. Accessing a Protected Resource
|
77
|
+
if request.authorization
|
78
|
+
# 5.1.1. The Authorization Request Header Field
|
79
|
+
token = request.credentials if request.oauth?
|
80
|
+
else
|
81
|
+
# 5.1.2. URI Query Parameter
|
82
|
+
# 5.1.3. Form-Encoded Body Parameter
|
83
|
+
token = request.GET["oauth_token"] || request.POST["oauth_token"]
|
84
|
+
end
|
85
|
+
|
86
|
+
if token
|
87
|
+
begin
|
88
|
+
access_token = AccessToken.from_token(token)
|
89
|
+
raise InvalidTokenError if access_token.nil? || access_token.revoked
|
90
|
+
raise ExpiredTokenError if access_token.expires_at && access_token.expires_at <= Time.now.utc
|
91
|
+
request.env["oauth.access_token"] = token
|
92
|
+
request.env["oauth.resource"] = access_token.resource
|
93
|
+
logger.info "Authorized #{access_token.resource}" if logger
|
94
|
+
rescue Error=>error
|
95
|
+
# 5.2. The WWW-Authenticate Response Header Field
|
96
|
+
logger.info "HTTP authorization failed #{error.code}" if logger
|
97
|
+
return unauthorized(request, error)
|
98
|
+
rescue =>ex
|
99
|
+
logger.info "HTTP authorization failed #{ex.message}" if logger
|
100
|
+
return unauthorized(request)
|
101
|
+
end
|
102
|
+
|
103
|
+
# We expect application to use 403 if request has insufficient scope,
|
104
|
+
# and return appropriate WWW-Authenticate header.
|
105
|
+
response = @app.call(env)
|
106
|
+
if response[0] == 403
|
107
|
+
scope = response[1]["oauth.no_scope"] || ""
|
108
|
+
scope = scope.join(" ") if scope.respond_to?(:join)
|
109
|
+
challenge = 'OAuth realm="%s", error="insufficient_scope", scope="%s"' % [(options[:realm] || request.host), scope]
|
110
|
+
return [403, { "WWW-Authenticate"=>challenge }, []]
|
111
|
+
else
|
112
|
+
return response
|
113
|
+
end
|
114
|
+
else
|
115
|
+
response = @app.call(env)
|
116
|
+
if response[1] && response[1]["oauth.no_access"]
|
117
|
+
# OAuth access required.
|
118
|
+
return unauthorized(request)
|
119
|
+
elsif response[1] && response[1]["oauth.authorization"]
|
120
|
+
# 3. Obtaining End-User Authorization
|
121
|
+
# Flow ends here.
|
122
|
+
return authorization_response(response, logger)
|
123
|
+
else
|
124
|
+
return response
|
125
|
+
end
|
126
|
+
end
|
127
|
+
ensure
|
128
|
+
Server.database = org_database
|
129
|
+
end
|
130
|
+
|
131
|
+
protected
|
132
|
+
|
133
|
+
# Get here for authorization request. Check the request parameters and
|
134
|
+
# redirect with an error if we find any issue. Otherwise, create a new
|
135
|
+
# authorization request, set in oauth.request and pass control to the
|
136
|
+
# application.
|
137
|
+
def request_authorization(request, logger)
|
138
|
+
# 3. Obtaining End-User Authorization
|
139
|
+
begin
|
140
|
+
redirect_uri = Utils.parse_redirect_uri(request.GET["redirect_uri"])
|
141
|
+
rescue InvalidRequestError=>error
|
142
|
+
logger.error "Authorization request with invalid redirect_uri: #{request.GET["redirect_uri"]} #{error.message}" if logger
|
143
|
+
return bad_request(error.message)
|
144
|
+
end
|
145
|
+
state = request.GET["state"]
|
146
|
+
|
147
|
+
begin
|
148
|
+
# 3. Obtaining End-User Authorization
|
149
|
+
client = get_client(request)
|
150
|
+
raise RedirectUriMismatchError unless client.redirect_uri.nil? || client.redirect_uri == redirect_uri.to_s
|
151
|
+
requested_scope = request.GET["scope"].to_s.split.uniq.join(" ")
|
152
|
+
response_type = request.GET["response_type"].to_s
|
153
|
+
raise UnsupportedResponseTypeError unless options[:authorization_types].include?(response_type)
|
154
|
+
if scopes = options[:scopes]
|
155
|
+
allowed_scope = scopes.respond_to?(:all?) ? scopes : scopes.split
|
156
|
+
raise InvalidScopeError unless requested_scope.split.all? { |v| allowed_scope.include?(v) }
|
157
|
+
end
|
158
|
+
# Create object to track authorization request and let application
|
159
|
+
# handle the rest.
|
160
|
+
auth_request = AuthRequest.create(client.id, requested_scope, redirect_uri.to_s, response_type, state)
|
161
|
+
request.env["oauth.authorization"] = auth_request.id.to_s
|
162
|
+
logger.info "Request #{auth_request.id}: Client #{client.display_name} requested #{response_type} with scope #{requested_scope}" if logger
|
163
|
+
return @app.call(request.env)
|
164
|
+
rescue Error=>error
|
165
|
+
logger.error "Authorization request error: #{error.code} #{error.message}" if logger
|
166
|
+
params = Rack::Utils.parse_query(redirect_uri.query).merge(:error=>error.code, :error_description=>error.message, :state=>state)
|
167
|
+
redirect_uri.query = Rack::Utils.build_query(params)
|
168
|
+
return redirect_to(redirect_uri)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Get here on completion of the authorization. Authorization response in
|
173
|
+
# oauth.response either grants or denies authroization. In either case, we
|
174
|
+
# redirect back with the proper response.
|
175
|
+
def authorization_response(response, logger)
|
176
|
+
status, headers, body = response
|
177
|
+
auth_request = self.class.get_auth_request(headers["oauth.authorization"])
|
178
|
+
redirect_uri = URI.parse(auth_request.redirect_uri)
|
179
|
+
if status == 401
|
180
|
+
auth_request.deny!
|
181
|
+
else
|
182
|
+
auth_request.grant! headers["oauth.resource"]
|
183
|
+
end
|
184
|
+
# 3.1. Authorization Response
|
185
|
+
if auth_request.response_type == "code" && auth_request.grant_code
|
186
|
+
logger.info "Request #{auth_request.id}: Client #{auth_request.client_id} granted access code #{auth_request.grant_code}" if logger
|
187
|
+
params = { :code=>auth_request.grant_code, :scope=>auth_request.scope, :state=>auth_request.state }
|
188
|
+
params = Rack::Utils.parse_query(redirect_uri.query).merge(params)
|
189
|
+
redirect_uri.query = Rack::Utils.build_query(params)
|
190
|
+
return redirect_to(redirect_uri)
|
191
|
+
elsif auth_request.response_type == "token" && auth_request.access_token
|
192
|
+
logger.info "Request #{auth_request.id}: Client #{auth_request.client_id} granted access token #{auth_request.access_token}" if logger
|
193
|
+
params = { :access_token=>auth_request.access_token, :scope=>auth_request.scope, :state=>auth_request.state }
|
194
|
+
redirect_uri.fragment = Rack::Utils.build_query(params)
|
195
|
+
return redirect_to(redirect_uri)
|
196
|
+
else
|
197
|
+
logger.info "Request #{auth_request.id}: Client #{auth_request.client_id} denied authorization" if logger
|
198
|
+
params = Rack::Utils.parse_query(redirect_uri.query).merge(:error=>:access_denied, :state=>auth_request.state)
|
199
|
+
redirect_uri.query = Rack::Utils.build_query(params)
|
200
|
+
return redirect_to(redirect_uri)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# 4. Obtaining an Access Token
|
205
|
+
def respond_with_access_token(request, logger)
|
206
|
+
return [405, { "Content-Type"=>"application/json" }, ["POST only"]] unless request.post?
|
207
|
+
# 4.2. Access Token Response
|
208
|
+
begin
|
209
|
+
client = get_client(request)
|
210
|
+
case request.POST["grant_type"]
|
211
|
+
when "authorization_code"
|
212
|
+
# 4.1.1. Authorization Code
|
213
|
+
grant = AccessGrant.from_code(request.POST["code"])
|
214
|
+
raise InvalidGrantError unless grant && client.id == grant.client_id
|
215
|
+
raise InvalidGrantError unless grant.redirect_uri.nil? || grant.redirect_uri == Utils.parse_redirect_uri(request.POST["redirect_uri"]).to_s
|
216
|
+
access_token = grant.authorize!
|
217
|
+
when "password"
|
218
|
+
raise UnsupportedGrantType unless options[:authenticator]
|
219
|
+
# 4.1.2. Resource Owner Password Credentials
|
220
|
+
username, password = request.POST.values_at("username", "password")
|
221
|
+
requested_scope = request.POST["scope"].to_s.split.uniq.join(" ")
|
222
|
+
raise InvalidGrantError unless username && password
|
223
|
+
resource = options[:authenticator].call(username, password)
|
224
|
+
raise InvalidGrantError unless resource
|
225
|
+
if scopes = options[:scopes]
|
226
|
+
allowed_scope = scopes.respond_to?(:all?) ? scopes : scopes.split
|
227
|
+
raise InvalidScopeError unless requested_scope.split.all? { |v| allowed_scope.include?(v) }
|
228
|
+
end
|
229
|
+
access_token = AccessToken.get_token_for(resource, requested_scope.to_s, client.id)
|
230
|
+
else raise UnsupportedGrantType
|
231
|
+
end
|
232
|
+
logger.info "Access token #{access_token.token} granted to client #{client.display_name}, resource #{access_token.resource}" if logger
|
233
|
+
response = { :access_token=>access_token.token }
|
234
|
+
response[:scope] = access_token.scope unless access_token.scope.empty?
|
235
|
+
return [200, { "Content-Type"=>"application/json", "Cache-Control"=>"no-store" }, response.to_json]
|
236
|
+
# 4.3. Error Response
|
237
|
+
rescue Error=>error
|
238
|
+
logger.error "Access token request error: #{error.code} #{error.message}" if logger
|
239
|
+
return unauthorized(request, error) if InvalidClientError === error && request.basic?
|
240
|
+
return [400, { "Content-Type"=>"application/json", "Cache-Control"=>"no-store" },
|
241
|
+
{ :error=>error.code, :error_description=>error.message }.to_json]
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
# Returns client from request based on credentials. Raises
|
246
|
+
# InvalidClientError if client doesn't exist or secret doesn't match.
|
247
|
+
def get_client(request)
|
248
|
+
# 2.1 Client Password Credentials
|
249
|
+
if request.basic?
|
250
|
+
client_id, client_secret = request.credentials
|
251
|
+
elsif request.form_data?
|
252
|
+
client_id, client_secret = request.POST.values_at("client_id", "client_secret")
|
253
|
+
else
|
254
|
+
client_id, client_secret = request.GET.values_at("client_id", "client_secret")
|
255
|
+
end
|
256
|
+
client = self.class.get_client(client_id)
|
257
|
+
raise InvalidClientError unless client && client.secret == client_secret
|
258
|
+
raise InvalidClientError if client.revoked
|
259
|
+
return client
|
260
|
+
rescue BSON::InvalidObjectId
|
261
|
+
raise InvalidClientError
|
262
|
+
end
|
263
|
+
|
264
|
+
# Rack redirect response. The argument is typically a URI object.
|
265
|
+
def redirect_to(uri)
|
266
|
+
return [302, { "Location"=>uri.to_s }, []]
|
267
|
+
end
|
268
|
+
|
269
|
+
def bad_request(message)
|
270
|
+
return [400, { "Content-Type"=>"text/plain" }, [message]]
|
271
|
+
end
|
272
|
+
|
273
|
+
# Returns WWW-Authenticate header.
|
274
|
+
def unauthorized(request, error = nil)
|
275
|
+
challenge = 'OAuth realm="%s"' % (options[:realm] || request.host)
|
276
|
+
challenge << ', error="%s", error_description="%s"' % [error.code, error.message] if error
|
277
|
+
return [401, { "WWW-Authenticate"=>challenge }, []]
|
278
|
+
end
|
279
|
+
|
280
|
+
# Wraps Rack::Request to expose Basic and OAuth authentication
|
281
|
+
# credentials.
|
282
|
+
class OAuthRequest < Rack::Request
|
283
|
+
|
284
|
+
AUTHORIZATION_KEYS = %w{HTTP_AUTHORIZATION X-HTTP_AUTHORIZATION X_HTTP_AUTHORIZATION}
|
285
|
+
|
286
|
+
# Returns authorization header.
|
287
|
+
def authorization
|
288
|
+
@authorization ||= AUTHORIZATION_KEYS.inject(nil) { |auth, key| auth || @env[key] }
|
289
|
+
end
|
290
|
+
|
291
|
+
# True if authentication scheme is OAuth.
|
292
|
+
def oauth?
|
293
|
+
authorization[/^oauth/i] if authorization
|
294
|
+
end
|
295
|
+
|
296
|
+
# True if authentication scheme is Basic.
|
297
|
+
def basic?
|
298
|
+
authorization[/^basic/i] if authorization
|
299
|
+
end
|
300
|
+
|
301
|
+
# If Basic auth, returns username/password, if OAuth, returns access
|
302
|
+
# token.
|
303
|
+
def credentials
|
304
|
+
basic? ? authorization.gsub(/\n/, "").split[1].unpack("m*").first.split(/:/, 2) :
|
305
|
+
oauth? ? authorization.gsub(/\n/, "").split[1] : nil
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
end
|
310
|
+
|
311
|
+
end
|
312
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Rack
|
2
|
+
module OAuth2
|
3
|
+
class Server
|
4
|
+
|
5
|
+
# Base class for all OAuth errors. These map to error codes in the spec.
|
6
|
+
class Error < StandardError
|
7
|
+
|
8
|
+
def initialize(code, message)
|
9
|
+
super message
|
10
|
+
@code = code.to_sym
|
11
|
+
end
|
12
|
+
|
13
|
+
# The OAuth error code.
|
14
|
+
attr_reader :code
|
15
|
+
end
|
16
|
+
|
17
|
+
# Access token expired, client expected to request new one using refresh
|
18
|
+
# token.
|
19
|
+
class ExpiredTokenError < Error
|
20
|
+
def initialize
|
21
|
+
super :expired_token, "The access token has expired."
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# The client identifier provided is invalid, the client failed to
|
26
|
+
# authenticate, the client did not include its credentials, provided
|
27
|
+
# multiple client credentials, or used unsupported credentials type.
|
28
|
+
class InvalidClientError < Error
|
29
|
+
def initialize
|
30
|
+
super :invalid_client, "Client ID and client secret do not match."
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# The provided access grant is invalid, expired, or revoked (e.g. invalid
|
35
|
+
# assertion, expired authorization token, bad end-user password credentials,
|
36
|
+
# or mismatching authorization code and redirection URI).
|
37
|
+
class InvalidGrantError < Error
|
38
|
+
def initialize
|
39
|
+
super :invalid_grant, "This access grant is no longer valid."
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Invalid_request, the request is missing a required parameter, includes an
|
44
|
+
# unsupported parameter or parameter value, repeats the same parameter, uses
|
45
|
+
# more than one method for including an access token, or is otherwise
|
46
|
+
# malformed.
|
47
|
+
class InvalidRequestError < Error
|
48
|
+
def initialize(message)
|
49
|
+
super :invalid_request, message || "The request has the wrong parameters."
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# The requested scope is invalid, unknown, or malformed.
|
54
|
+
class InvalidScopeError < Error
|
55
|
+
def initialize
|
56
|
+
super :invalid_scope, "The requested scope is not supported."
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Access token expired, client cannot refresh and needs new authorization.
|
61
|
+
class InvalidTokenError < Error
|
62
|
+
def initialize
|
63
|
+
super :invalid_token, "The access token is no longer valid."
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# The redirection URI provided does not match a pre-registered value.
|
68
|
+
class RedirectUriMismatchError < Error
|
69
|
+
def initialize
|
70
|
+
super :redirect_uri_mismatch, "Must use the same redirect URI you registered with us."
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# The authenticated client is not authorized to use the access grant type provided.
|
75
|
+
class UnauthorizedClientError < Error
|
76
|
+
def initialize
|
77
|
+
super :unauthorized_client, "You are not allowed to access this resource."
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# This access grant type is not supported by this server.
|
82
|
+
class UnsupportedGrantType < Error
|
83
|
+
def initialize
|
84
|
+
super :unsupported_grant_type, "This access grant type is not supported by this server."
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# The requested response type is not supported by the authorization server.
|
89
|
+
class UnsupportedResponseTypeError < Error
|
90
|
+
def initialize
|
91
|
+
super :unsupported_response_type, "The requested response type is not supported."
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
module Rack
|
2
|
+
module OAuth2
|
3
|
+
class Server
|
4
|
+
|
5
|
+
# Helper methods that provide access to the OAuth state during the
|
6
|
+
# authorization flow, and from authenticated requests. For example:
|
7
|
+
#
|
8
|
+
# def show
|
9
|
+
# logger.info "#{oauth.client.display_name} accessing #{oauth.scope}"
|
10
|
+
# end
|
11
|
+
class Helper
|
12
|
+
|
13
|
+
def initialize(request, response)
|
14
|
+
@request, @response = request, response
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns the access token. Only applies if client authenticated.
|
18
|
+
#
|
19
|
+
# @return [String, nil] Access token, if authenticated
|
20
|
+
def access_token
|
21
|
+
@access_token ||= @request.env["oauth.access_token"]
|
22
|
+
end
|
23
|
+
|
24
|
+
# True if client authenticated.
|
25
|
+
#
|
26
|
+
# @return [true, false] True if authenticated
|
27
|
+
def authenticated?
|
28
|
+
!!access_token
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns the authenticated resource. Only applies if client
|
32
|
+
# authenticated.
|
33
|
+
#
|
34
|
+
# @return [String, nil] Resource, if authenticated
|
35
|
+
def resource
|
36
|
+
@resource ||= @request.env["oauth.resource"]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns the Client object associated with this request. Available if
|
40
|
+
# client authenticated, or while processing authorization request.
|
41
|
+
#
|
42
|
+
# @return [Client, nil] Client if authenticated, or while authorizing
|
43
|
+
def client
|
44
|
+
if access_token
|
45
|
+
@client ||= Server.get_client(Server.get_access_token(access_token).client_id)
|
46
|
+
elsif authorization
|
47
|
+
@client ||= Server.get_client(Server.get_auth_request(authorization).client_id)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns scope associated with this request. Available if client
|
52
|
+
# authenticated, or while processing authorization request.
|
53
|
+
#
|
54
|
+
# @return [Array<String>, nil] Scope names, e.g ["read, "write"]
|
55
|
+
def scope
|
56
|
+
if access_token
|
57
|
+
@scope ||= Server.get_access_token(access_token).scope.split
|
58
|
+
elsif authorization
|
59
|
+
@scope ||= Server.get_auth_request(authorization).scope.split
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Rejects the request and returns 401 (Unauthorized). You can just
|
64
|
+
# return 401, but this also sets the WWW-Authenticate header the right
|
65
|
+
# value.
|
66
|
+
#
|
67
|
+
# @return 401
|
68
|
+
def no_access!
|
69
|
+
@response["oauth.no_access"] = true
|
70
|
+
@response.status = 401
|
71
|
+
end
|
72
|
+
|
73
|
+
# Rejects the request and returns 403 (Forbidden). You can just
|
74
|
+
# return 403, but this also sets the WWW-Authenticate header the right
|
75
|
+
# value. Indicates which scope the client needs to make this request.
|
76
|
+
#
|
77
|
+
# @param [String] scope The missing scope, e.g. "read"
|
78
|
+
# @return 403
|
79
|
+
def no_scope!(scope)
|
80
|
+
@response["oauth.no_scope"] = scope
|
81
|
+
@response.status = 403
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns the authorization request handle. Available when starting an
|
85
|
+
# authorization request (i.e. /oauth/authorize).
|
86
|
+
#
|
87
|
+
# @return [String] Authorization handle
|
88
|
+
def authorization
|
89
|
+
@request_id ||= @request.env["oauth.authorization"]
|
90
|
+
end
|
91
|
+
|
92
|
+
# Sets the authorization request handle. Use this during the
|
93
|
+
# authorization flow.
|
94
|
+
#
|
95
|
+
# @param [String] authorization handle
|
96
|
+
def authorization=(authorization)
|
97
|
+
@scope, @client = nil
|
98
|
+
@request_id = authorization
|
99
|
+
end
|
100
|
+
|
101
|
+
# Grant authorization request. Call this at the end of the authorization
|
102
|
+
# flow to signal that the user has authorized the client to access the
|
103
|
+
# specified resource. Don't render anything else.
|
104
|
+
#
|
105
|
+
# @param [String] authorization Authorization handle
|
106
|
+
# @param [String] resource Resource string
|
107
|
+
# @return 200
|
108
|
+
def grant!(authorization, resource)
|
109
|
+
@response["oauth.authorization"] = authorization
|
110
|
+
@response["oauth.resource"] = resource.to_s
|
111
|
+
@response.status = 200
|
112
|
+
end
|
113
|
+
|
114
|
+
# Deny authorization request. Call this at the end of the authorization
|
115
|
+
# flow to signal that the user has not authorized the client. Don't
|
116
|
+
# render anything else.
|
117
|
+
#
|
118
|
+
# @param [String] authorization Authorization handle
|
119
|
+
# @return 401
|
120
|
+
def deny!(authorization)
|
121
|
+
@response["oauth.authorization"] = authorization
|
122
|
+
@response.status = 401
|
123
|
+
end
|
124
|
+
|
125
|
+
# Returns all access tokens associated with this resource.
|
126
|
+
#
|
127
|
+
# @param [String] resource Resource string
|
128
|
+
# @return [Array<AccessToken>]
|
129
|
+
def list_access_tokens(resource)
|
130
|
+
Rack::OAuth2::Server.list_access_tokens(resource)
|
131
|
+
end
|
132
|
+
|
133
|
+
def inspect
|
134
|
+
authorization ? "Authorization request for #{scope.join(",")} on behalf of #{client.display_name}" :
|
135
|
+
authenticated? ? "Authenticated as #{resource}" : nil
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|