lib/rack/oauth2/server.rb in rack-oauth2-server-1.4.6 vs lib/rack/oauth2/server.rb in rack-oauth2-server-2.0.0.beta
- old
+ new
@@ -1,24 +1,21 @@
+require "rack"
require "rack/oauth2/models"
require "rack/oauth2/server/errors"
require "rack/oauth2/server/utils"
require "rack/oauth2/server/helper"
-require "rack/oauth2/admin"
module Rack
module OAuth2
# Implements an OAuth 2 Authorization Server, based on http://tools.ietf.org/html/draft-ietf-oauth-v2-10
class Server
# Same as gem version number.
- VERSION = IO.read(::File.expand_path("../../../VERSION", ::File.dirname(__FILE__)))
+ VERSION = IO.read(::File.expand_path("../../../VERSION", ::File.dirname(__FILE__))).strip
- # Backward compatible.
- Admin = Rack::OAuth2::Admin
-
class << self
# Return AuthRequest from authorization request handle.
def get_auth_request(authorization)
AuthRequest.find(authorization)
end
@@ -56,15 +53,14 @@
# - :path -- Only check requests for resources under this path.
# - :param_authentication -- If true, supports authentication using
# query/form parameters.
# - :realm -- Authorization realm that will show up in 401 responses.
# Defaults to use the request host name.
- # - :scopes -- Array listing all supported scopes, e.g. %w{read write}.
# - :logger -- The logger to use. Under Rails, defaults to use the Rails
# logger. Will use Rack::Logger if available.
Options = Struct.new(:access_token_path, :authenticator, :authorization_types,
- :authorize_path, :database, :host, :param_authentication, :path, :realm, :scopes, :logger)
+ :authorize_path, :database, :host, :param_authentication, :path, :realm, :logger)
def initialize(app, options = Options.new, &authenticator)
@app = app
@options = options
@options.authenticator ||= authenticator
@@ -122,13 +118,12 @@
# We expect application to use 403 if request has insufficient scope,
# and return appropriate WWW-Authenticate header.
response = @app.call(env)
if response[0] == 403
- scope = response[1]["oauth.no_scope"] || ""
- scope = scope.join(" ") if scope.respond_to?(:join)
- challenge = 'OAuth realm="%s", error="insufficient_scope", scope="%s"' % [(options.realm || request.host), scope]
+ scopes = Utils.normalize_scopes(response[1]["oauth.no_scope"])
+ challenge = 'OAuth realm="%s", error="insufficient_scope", scope="%s"' % [(options.realm || request.host), scopes.join(" ")]
response[1]["WWW-Authenticate"] = challenge
return response
else
return response
end
@@ -166,10 +161,16 @@
logger.error "Invalid authorization request #{auth_request}" if logger
return bad_request("Invalid authorization request")
end
response_type = auth_request.response_type # Needed for error handling
client = self.class.get_client(auth_request.client_id)
+ # Pass back to application, watch for 403 (deny!)
+ logger.info "Request #{auth_request.id}: Client #{client.display_name} requested #{auth_request.response_type} with scope #{auth_request.scope}" if logger
+ request.env["oauth.authorization"] = auth_request.id.to_s
+ response = @app.call(request.env)
+ raise AccessDeniedError if response[0] == 403
+ return response
else
# 3. Obtaining End-User Authorization
begin
@@ -181,26 +182,21 @@
# 3. Obtaining End-User Authorization
response_type = request.GET["response_type"].to_s # Need this first, for error handling
client = get_client(request)
raise RedirectUriMismatchError unless client.redirect_uri.nil? || client.redirect_uri == redirect_uri.to_s
- requested_scope = request.GET["scope"].to_s.split.uniq.join(" ")
raise UnsupportedResponseTypeError unless options.authorization_types.include?(response_type)
- if scopes = options.scopes
- allowed_scope = scopes.respond_to?(:all?) ? scopes : scopes.split
- raise InvalidScopeError unless requested_scope.split.all? { |v| allowed_scope.include?(v) }
- end
+ requested_scope = Utils.normalize_scopes(request.GET["scope"])
+ allowed_scopes = client.scopes
+ raise InvalidScopeError unless (requested_scope - allowed_scopes).empty?
# Create object to track authorization request and let application
# handle the rest.
auth_request = AuthRequest.create(client.id, requested_scope, redirect_uri.to_s, response_type, state)
- request.env["oauth.authorization"] = auth_request.id.to_s
+ uri = URI.parse(request.url)
+ uri.query = "authorization=#{auth_request.id.to_s}"
+ return [303, { "Location"=>uri.to_s }, []]
end
- # Pass back to application, watch for 403 (deny!)
- logger.info "Request #{auth_request.id}: Client #{client.display_name} requested #{auth_request.response_type} with scope #{auth_request.scope}" if logger
- response = @app.call(request.env)
- raise AccessDeniedError if response[0] == 403
- return response
rescue OAuthError=>error
logger.error "Authorization request error: #{error.code} #{error.message}" if logger
params = { :error=>error.code, :error_description=>error.message, :state=>state }
if response_type == "token"
redirect_uri.fragment = Rack::Utils.build_query(params)
@@ -226,21 +222,21 @@
end
# 3.1. Authorization Response
if auth_request.response_type == "code"
if auth_request.grant_code
logger.info "Request #{auth_request.id}: Client #{auth_request.client_id} granted access code #{auth_request.grant_code}" if logger
- params = { :code=>auth_request.grant_code, :scope=>auth_request.scope, :state=>auth_request.state }
+ params = { :code=>auth_request.grant_code, :scope=>auth_request.scope.join(" "), :state=>auth_request.state }
else
logger.info "Request #{auth_request.id}: Client #{auth_request.client_id} denied authorization" if logger
params = { :error=>:access_denied, :state=>auth_request.state }
end
params = Rack::Utils.parse_query(redirect_uri.query).merge(params)
redirect_uri.query = Rack::Utils.build_query(params)
else # response type if token
if auth_request.access_token
logger.info "Request #{auth_request.id}: Client #{auth_request.client_id} granted access token #{auth_request.access_token}" if logger
- params = { :access_token=>auth_request.access_token, :scope=>auth_request.scope, :state=>auth_request.state }
+ params = { :access_token=>auth_request.access_token, :scope=>auth_request.scope.join(" "), :state=>auth_request.state }
else
logger.info "Request #{auth_request.id}: Client #{auth_request.client_id} denied authorization" if logger
params = { :error=>:access_denied, :state=>auth_request.state }
end
redirect_uri.fragment = Rack::Utils.build_query(params)
@@ -263,23 +259,21 @@
access_token = grant.authorize!
when "password"
raise UnsupportedGrantType unless options.authenticator
# 4.1.2. Resource Owner Password Credentials
username, password = request.POST.values_at("username", "password")
- requested_scope = request.POST["scope"].to_s.split.uniq.join(" ")
raise InvalidGrantError unless username && password
identity = options.authenticator.call(username, password)
raise InvalidGrantError unless identity
- if scopes = options.scopes
- allowed_scope = scopes.respond_to?(:all?) ? scopes : scopes.split
- raise InvalidScopeError unless requested_scope.split.all? { |v| allowed_scope.include?(v) }
- end
- access_token = AccessToken.get_token_for(identity, requested_scope.to_s, client.id)
+ requested_scope = Utils.normalize_scopes(request.POST["scope"])
+ allowed_scopes = client.scopes
+ raise InvalidScopeError unless (requested_scope - allowed_scopes).empty?
+ access_token = AccessToken.get_token_for(identity, requested_scope, client.id)
else raise UnsupportedGrantType
end
logger.info "Access token #{access_token.token} granted to client #{client.display_name}, identity #{access_token.identity}" if logger
response = { :access_token=>access_token.token }
- response[:scope] = access_token.scope unless access_token.scope.empty?
+ response[:scope] = access_token.scope.join(" ")
return [200, { "Content-Type"=>"application/json", "Cache-Control"=>"no-store" }, response.to_json]
# 4.3. Error Response
rescue OAuthError=>error
logger.error "Access token request error: #{error.code} #{error.message}" if logger
return unauthorized(request, error) if InvalidClientError === error && request.basic?