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?