pax_global_header00006660000000000000000000000064145427527670014535gustar00rootroot0000000000000052 comment=70da234801c53a116c2d8e126bb09f237824af5a rack-oauth2-2.2.1/000077500000000000000000000000001454275276700136575ustar00rootroot00000000000000rack-oauth2-2.2.1/.document000066400000000000000000000000741454275276700154770ustar00rootroot00000000000000README.rdoc lib/**/*.rb bin/* features/**/*.feature LICENSE rack-oauth2-2.2.1/.github/000077500000000000000000000000001454275276700152175ustar00rootroot00000000000000rack-oauth2-2.2.1/.github/FUNDING.yml000066400000000000000000000000731454275276700170340ustar00rootroot00000000000000# These are supported funding model platforms github: nov rack-oauth2-2.2.1/.github/workflows/000077500000000000000000000000001454275276700172545ustar00rootroot00000000000000rack-oauth2-2.2.1/.github/workflows/spec.yml000066400000000000000000000011031454275276700207240ustar00rootroot00000000000000name: Spec on: push: branches: - main pull_request: permissions: contents: read jobs: spec: strategy: matrix: os: ['ubuntu-20.04', 'ubuntu-22.04'] ruby-version: ['3.1', '3.2', '3.3'] include: - os: 'ubuntu-20.04' ruby-version: '3.0' runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby-version }} bundler-cache: true - name: Run Specs run: bundle exec rake spec rack-oauth2-2.2.1/.gitignore000066400000000000000000000002401454275276700156430ustar00rootroot00000000000000## MAC OS .DS_Store ## TEXTMATE *.tmproj tmtags ## EMACS *~ \#* .\#* ## VIM *.swp ## PROJECT::GENERAL coverage* rdoc pkg Gemfile.lock ## PROJECT::SPECIFIC rack-oauth2-2.2.1/.rspec000066400000000000000000000000371454275276700147740ustar00rootroot00000000000000--color --format=documentation rack-oauth2-2.2.1/CHANGELOG.md000066400000000000000000000015611454275276700154730ustar00rootroot00000000000000## [Unreleased] ## [2.2.0] - 2022-10-11 ### Changed - automatic json response decoding, and remove legacy token support by @nov in https://github.com/nov/rack-oauth2/pull/95 ## [2.1.0] - 2022-10-10 ### Added - accept local_http_config on Rack::OAuth2::Client#access_token! & revoke! to support custom headers etc. by @nov in https://github.com/nov/rack-oauth2/pull/93 ## [2.0.1] - 2022-10-09 ### Fixed - changes for mTLS on faraday by @nov in https://github.com/nov/rack-oauth2/pull/92 ## [2.0.0] - 2022-10-09 ### Added - start recording CHANGELOG ### Changed - Switch from httpclient to faraday v2 https://github.com/nov/rack-oauth2/pull/91 - make url-encoded the default https://github.com/nov/rack-oauth2/commit/98faf139be4f5bf9ec6134d31f65a298259d8a8b - let faraday encode params https://github.com/nov/rack-oauth2/commit/f399b3afb8facb3635b8842baee8584bc4d3ce73rack-oauth2-2.2.1/Gemfile000066400000000000000000000001401454275276700151450ustar00rootroot00000000000000source 'https://rubygems.org' platforms :jruby do gem 'jruby-openssl', '>= 0.7' end gemspec rack-oauth2-2.2.1/LICENSE000066400000000000000000000020361454275276700146650ustar00rootroot00000000000000Copyright (c) 2010 nov matake Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. rack-oauth2-2.2.1/README.rdoc000066400000000000000000000027651454275276700154770ustar00rootroot00000000000000= rack-oauth2 OAuth 2.0 Server & Client Library. Both Bearer token type are supported. The OAuth 2.0 Authorization Framework (RFC 6749) http://www.rfc-editor.org/rfc/rfc6749.txt The OAuth 2.0 Authorization Framework: Bearer Token Usage (RFC 6750) http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-06 == Installation gem install rack-oauth2 == Resources * View Source on GitHub (https://github.com/nov/rack-oauth2) * Docs on GitHub (https://github.com/nov/rack-oauth2/wiki) * Report Issues on GitHub (https://github.com/nov/rack-oauth2/issues) == Sample Server Application (Rails3) === Bearer Source on GitHub https://github.com/nov/rack-oauth2-sample == Sample Client Authorization Request (request_type: 'code' and 'token') https://gist.github.com/862393 Token Request (grant_type: 'client_credentials', 'password', 'authorization_code' and 'refresh_token') https://gist.github.com/883541 Resource Request (request both for resource owner resource and for client resource) https://gist.github.com/883575 == Note on Patches/Pull Requests * Fork the project. * Make your feature addition or bug fix. * Add tests for it. This is important so I don't break it in a future version unintentionally. * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull) * Send me a pull request. Bonus points for topic branches. == Copyright Copyright (c) 2010 nov matake. See LICENSE for details. rack-oauth2-2.2.1/Rakefile000066400000000000000000000006201454275276700153220ustar00rootroot00000000000000require 'bundler' Bundler::GemHelper.install_tasks require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) namespace :coverage do desc "Open coverage report" task :report do require 'simplecov' `open "#{File.join SimpleCov.coverage_path, 'index.html'}"` end end task :spec do Rake::Task[:'coverage:report'].invoke unless ENV['TRAVIS_RUBY_VERSION'] end task default: :spec rack-oauth2-2.2.1/VERSION000066400000000000000000000000051454275276700147220ustar00rootroot000000000000002.2.1rack-oauth2-2.2.1/lib/000077500000000000000000000000001454275276700144255ustar00rootroot00000000000000rack-oauth2-2.2.1/lib/rack/000077500000000000000000000000001454275276700153455ustar00rootroot00000000000000rack-oauth2-2.2.1/lib/rack/oauth2.rb000066400000000000000000000031411454275276700170730ustar00rootroot00000000000000require 'rack' require 'faraday' require 'faraday/follow_redirects' require 'logger' require 'active_support' require 'active_support/core_ext' require 'attr_required' require 'attr_optional' module Rack module OAuth2 VERSION = ::File.read( ::File.join(::File.dirname(__FILE__), '../../VERSION') ).strip def self.logger @@logger end def self.logger=(logger) @@logger = logger end self.logger = ::Logger.new(STDOUT) self.logger.progname = 'Rack::OAuth2' def self.debugging? @@debugging end def self.debugging=(boolean) @@debugging = boolean end def self.debug! self.debugging = true end def self.debug(&block) original = self.debugging? self.debugging = true yield ensure self.debugging = original end self.debugging = false def self.http_client(agent_name = "Rack::OAuth2 (#{VERSION})", &local_http_config) Faraday.new(headers: {user_agent: agent_name}) do |faraday| faraday.request :url_encoded faraday.request :json faraday.response :json faraday.adapter Faraday.default_adapter local_http_config&.call(faraday) http_config&.call(faraday) faraday.response :logger, Rack::OAuth2.logger, bodies: true if debugging? end end def self.http_config(&block) @@http_config ||= block end def self.reset_http_config! @@http_config = nil end end end require 'rack/oauth2/urn' require 'rack/oauth2/util' require 'rack/oauth2/server' require 'rack/oauth2/client' require 'rack/oauth2/access_token' rack-oauth2-2.2.1/lib/rack/oauth2/000077500000000000000000000000001454275276700165475ustar00rootroot00000000000000rack-oauth2-2.2.1/lib/rack/oauth2/access_token.rb000066400000000000000000000023301454275276700215330ustar00rootroot00000000000000module Rack module OAuth2 class AccessToken include AttrRequired, AttrOptional attr_required :access_token, :token_type attr_optional :refresh_token, :expires_in, :scope attr_accessor :raw_attributes delegate :get, :patch, :post, :put, :delete, to: :http_client alias_method :to_s, :access_token def initialize(attributes = {}) (required_attributes + optional_attributes).each do |key| self.send :"#{key}=", attributes[key] end @raw_attributes = attributes @token_type = self.class.name.demodulize.underscore.to_sym attr_missing! end def http_client @http_client ||= Rack::OAuth2.http_client("#{self.class} (#{VERSION})") do |faraday| Authenticator.new(self).authenticate(faraday) end end def token_response(options = {}) { access_token: access_token, refresh_token: refresh_token, token_type: token_type, expires_in: expires_in, scope: Array(scope).join(' ') } end end end end require 'rack/oauth2/access_token/authenticator' require 'rack/oauth2/access_token/bearer' require 'rack/oauth2/access_token/mtls' rack-oauth2-2.2.1/lib/rack/oauth2/access_token/000077500000000000000000000000001454275276700212105ustar00rootroot00000000000000rack-oauth2-2.2.1/lib/rack/oauth2/access_token/authenticator.rb000066400000000000000000000004001454275276700244010ustar00rootroot00000000000000module Rack module OAuth2 class AccessToken class Authenticator def initialize(token) @token = token end def authenticate(request) @token.authenticate(request) end end end end endrack-oauth2-2.2.1/lib/rack/oauth2/access_token/bearer.rb000066400000000000000000000006621454275276700230010ustar00rootroot00000000000000module Rack module OAuth2 class AccessToken class Bearer < AccessToken def authenticate(request) request.headers["Authorization"] = "Bearer #{access_token}" end def to_mtls(attributes = {}) (required_attributes + optional_attributes).each do |key| attributes[key] = self.send(key) end MTLS.new attributes end end end end end rack-oauth2-2.2.1/lib/rack/oauth2/access_token/mtls.rb000066400000000000000000000005511454275276700225150ustar00rootroot00000000000000module Rack module OAuth2 class AccessToken class MTLS < Bearer attr_required :private_key, :certificate def initialize(attributes = {}) super self.token_type = :bearer http_client.ssl.client_key = private_key http_client.ssl.client_cert = certificate end end end end end rack-oauth2-2.2.1/lib/rack/oauth2/client.rb000066400000000000000000000161171454275276700203600ustar00rootroot00000000000000module Rack module OAuth2 class Client include AttrRequired, AttrOptional attr_required :identifier attr_optional :secret, :private_key, :certificate, :redirect_uri, :scheme, :host, :port, :authorization_endpoint, :token_endpoint, :revocation_endpoint def initialize(attributes = {}) (required_attributes + optional_attributes).each do |key| self.send :"#{key}=", attributes[key] end @grant = Grant::ClientCredentials.new @authorization_endpoint ||= '/oauth2/authorize' @token_endpoint ||= '/oauth2/token' attr_missing! end def authorization_uri(params = {}) params[:redirect_uri] ||= self.redirect_uri params[:response_type] ||= :code params[:response_type] = Array(params[:response_type]).join(' ') params[:scope] = Array(params[:scope]).join(' ') Util.redirect_uri absolute_uri_for(authorization_endpoint), :query, params.merge( client_id: self.identifier ) end def authorization_code=(code) @grant = Grant::AuthorizationCode.new( code: code, redirect_uri: self.redirect_uri ) end def resource_owner_credentials=(credentials) @grant = Grant::Password.new( username: credentials.first, password: credentials.last ) end def refresh_token=(token) @grant = Grant::RefreshToken.new( refresh_token: token ) end def jwt_bearer=(assertion) @grant = Grant::JWTBearer.new( assertion: assertion ) end def saml2_bearer=(assertion) @grant = Grant::SAML2Bearer.new( assertion: assertion ) end def subject_token=(subject_token, subject_token_type = URN::TokenType::JWT) @grant = Grant::TokenExchange.new( subject_token: subject_token, subject_token_type: subject_token_type ) end def force_token_type!(token_type) @forced_token_type = token_type.to_s end def access_token!(*args) headers, params, http_client, options = authenticated_context_from(*args) params[:scope] = Array(options.delete(:scope)).join(' ') if options[:scope].present? params.merge! @grant.as_json params.merge! options handle_response do http_client.post( absolute_uri_for(token_endpoint), Util.compact_hash(params), headers ) do |req| yield req if block_given? end end end def revoke!(*args) headers, params, http_client, options = authenticated_context_from(*args) params.merge! case when access_token = options.delete(:access_token) { token: access_token, token_type_hint: :access_token } when refresh_token = options.delete(:refresh_token) { token: refresh_token, token_type_hint: :refresh_token } when @grant.is_a?(Grant::RefreshToken) { token: @grant.refresh_token, token_type_hint: :refresh_token } when options[:token].blank? raise ArgumentError, 'One of "token", "access_token" and "refresh_token" is required' end params.merge! options handle_revocation_response do http_client.post( absolute_uri_for(revocation_endpoint), Util.compact_hash(params), headers ) do |req| yield req if block_given? end end end private def absolute_uri_for(endpoint) _endpoint_ = Util.parse_uri endpoint _endpoint_.scheme ||= self.scheme || 'https' _endpoint_.host ||= self.host _endpoint_.port ||= self.port raise 'No Host Info' unless _endpoint_.host _endpoint_.to_s end def authenticated_context_from(*args) headers, params = {}, {} http_client = Rack::OAuth2.http_client # NOTE: # Using Array#extract_options! for backward compatibility. # Until v1.0.5, the first argument was 'client_auth_method' in scalar. options = args.extract_options! client_auth_method = args.first || options.delete(:client_auth_method)&.to_sym || :basic case client_auth_method when :basic cred = Base64.strict_encode64 [ Util.www_form_url_encode(identifier), Util.www_form_url_encode(secret) ].join(':') headers.merge!( 'Authorization' => "Basic #{cred}" ) when :basic_without_www_form_urlencode cred = ["#{identifier}:#{secret}"].pack('m').tr("\n", '') headers.merge!( 'Authorization' => "Basic #{cred}" ) when :jwt_bearer params.merge!( client_assertion_type: URN::ClientAssertionType::JWT_BEARER ) # NOTE: optionally auto-generate client_assertion. params[:client_assertion] = if options[:client_assertion].present? options.delete(:client_assertion) else require 'json/jwt' JSON::JWT.new( iss: identifier, sub: identifier, aud: absolute_uri_for(token_endpoint), jti: SecureRandom.hex(16), iat: Time.now, exp: 3.minutes.from_now ).sign(private_key || secret).to_s end when :saml2_bearer params.merge!( client_assertion_type: URN::ClientAssertionType::SAML2_BEARER ) when :mtls params.merge!( client_id: identifier ) http_client.ssl.client_key = private_key http_client.ssl.client_cert = certificate else params.merge!( client_id: identifier, client_secret: secret ) end [headers, params, http_client, options] end def handle_response response = yield case response.status when 200..201 handle_success_response response else handle_error_response response end end def handle_revocation_response response = yield case response.status when 200..201 :success else handle_error_response response end end def handle_success_response(response) token_hash = response.body.with_indifferent_access case (@forced_token_type || token_hash[:token_type])&.downcase when 'bearer' AccessToken::Bearer.new(token_hash) else raise 'Unknown Token Type' end end def handle_error_response(response) error = response.body.with_indifferent_access raise Error.new(response.status, error) rescue Faraday::ParsingError, NoMethodError raise Error.new(response.status, error: 'Unknown', error_description: response.body) end end end end require 'rack/oauth2/client/error' require 'rack/oauth2/client/grant' rack-oauth2-2.2.1/lib/rack/oauth2/client/000077500000000000000000000000001454275276700200255ustar00rootroot00000000000000rack-oauth2-2.2.1/lib/rack/oauth2/client/error.rb000066400000000000000000000005651454275276700215110ustar00rootroot00000000000000module Rack module OAuth2 class Client class Error < StandardError attr_accessor :status, :response def initialize(status, response) @status = status @response = response message = [response[:error], response[:error_description]].compact.join(' :: ') super message end end end end end rack-oauth2-2.2.1/lib/rack/oauth2/client/grant.rb000066400000000000000000000017631454275276700214740ustar00rootroot00000000000000module Rack module OAuth2 class Client class Grant include AttrRequired, AttrOptional def initialize(attributes = {}) (required_attributes + optional_attributes).each do |key| self.send "#{key}=", attributes[key] end attr_missing! end def grant_type self.class.name.demodulize.underscore.to_sym end def as_json(options = {}) (required_attributes + optional_attributes).inject({ grant_type: grant_type }) do |hash, key| hash.merge! key => self.send(key) end end end end end end require 'rack/oauth2/client/grant/authorization_code' require 'rack/oauth2/client/grant/password' require 'rack/oauth2/client/grant/client_credentials' require 'rack/oauth2/client/grant/refresh_token' require 'rack/oauth2/client/grant/jwt_bearer' require 'rack/oauth2/client/grant/saml2_bearer' require 'rack/oauth2/client/grant/token_exchange'rack-oauth2-2.2.1/lib/rack/oauth2/client/grant/000077500000000000000000000000001454275276700211405ustar00rootroot00000000000000rack-oauth2-2.2.1/lib/rack/oauth2/client/grant/authorization_code.rb000066400000000000000000000003221454275276700253540ustar00rootroot00000000000000module Rack module OAuth2 class Client class Grant class AuthorizationCode < Grant attr_required :code attr_optional :redirect_uri end end end end endrack-oauth2-2.2.1/lib/rack/oauth2/client/grant/client_credentials.rb000066400000000000000000000002161454275276700253170ustar00rootroot00000000000000module Rack module OAuth2 class Client class Grant class ClientCredentials < Grant end end end end endrack-oauth2-2.2.1/lib/rack/oauth2/client/grant/jwt_bearer.rb000066400000000000000000000003701454275276700236110ustar00rootroot00000000000000module Rack module OAuth2 class Client class Grant class JWTBearer < Grant attr_required :assertion def grant_type URN::GrantType::JWT_BEARER end end end end end endrack-oauth2-2.2.1/lib/rack/oauth2/client/grant/password.rb000066400000000000000000000002621454275276700233270ustar00rootroot00000000000000module Rack module OAuth2 class Client class Grant class Password < Grant attr_required :username, :password end end end end endrack-oauth2-2.2.1/lib/rack/oauth2/client/grant/refresh_token.rb000066400000000000000000000002601454275276700243210ustar00rootroot00000000000000module Rack module OAuth2 class Client class Grant class RefreshToken < Grant attr_required :refresh_token end end end end endrack-oauth2-2.2.1/lib/rack/oauth2/client/grant/saml2_bearer.rb000066400000000000000000000003741454275276700240270ustar00rootroot00000000000000module Rack module OAuth2 class Client class Grant class SAML2Bearer < Grant attr_required :assertion def grant_type URN::GrantType::SAML2_BEARER end end end end end endrack-oauth2-2.2.1/lib/rack/oauth2/client/grant/token_exchange.rb000066400000000000000000000004311454275276700244450ustar00rootroot00000000000000module Rack module OAuth2 class Client class Grant class TokenExchange < Grant attr_required :subject_token, :subject_token_type def grant_type URN::GrantType::TOKEN_EXCHANGE end end end end end endrack-oauth2-2.2.1/lib/rack/oauth2/server.rb000066400000000000000000000003401454275276700203770ustar00rootroot00000000000000require 'rack/oauth2/server/abstract' require 'rack/oauth2/server/extension' require 'rack/oauth2/server/authorize' require 'rack/oauth2/server/token' require 'rack/oauth2/server/resource' require 'rack/oauth2/server/rails' rack-oauth2-2.2.1/lib/rack/oauth2/server/000077500000000000000000000000001454275276700200555ustar00rootroot00000000000000rack-oauth2-2.2.1/lib/rack/oauth2/server/abstract.rb000066400000000000000000000002661454275276700222110ustar00rootroot00000000000000require 'rack/oauth2/server/abstract/handler' require 'rack/oauth2/server/abstract/request' require 'rack/oauth2/server/abstract/response' require 'rack/oauth2/server/abstract/error'rack-oauth2-2.2.1/lib/rack/oauth2/server/abstract/000077500000000000000000000000001454275276700216605ustar00rootroot00000000000000rack-oauth2-2.2.1/lib/rack/oauth2/server/abstract/error.rb000066400000000000000000000042001454275276700233320ustar00rootroot00000000000000module Rack module OAuth2 module Server module Abstract class Error < StandardError attr_accessor :status, :error, :description, :uri, :realm def initialize(status, error, description = nil, options = {}) @status = status @error = error @description = description @uri = options[:uri] @realm = options[:realm] super [error, description].compact.join(' :: ') end def protocol_params { error: error, error_description: description, error_uri: uri } end def finish response = Rack::Response.new response.status = status yield response if block_given? unless response.redirect? response.headers['Content-Type'] = 'application/json' response.write Util.compact_hash(protocol_params).to_json end response.finish end end class BadRequest < Error def initialize(error = :bad_request, description = nil, options = {}) super 400, error, description, options end end class Unauthorized < Error def initialize(error = :unauthorized, description = nil, options = {}) @skip_www_authenticate = options[:skip_www_authenticate] super 401, error, description, options end end class Forbidden < Error def initialize(error = :forbidden, description = nil, options = {}) super 403, error, description, options end end class ServerError < Error def initialize(error = :server_error, description = nil, options = {}) super 500, error, description, options end end class TemporarilyUnavailable < Error def initialize(error = :temporarily_unavailable, description = nil, options = {}) super 503, error, description, options end end end end end end rack-oauth2-2.2.1/lib/rack/oauth2/server/abstract/handler.rb000066400000000000000000000014561454275276700236300ustar00rootroot00000000000000module Rack module OAuth2 module Server module Abstract class Handler attr_accessor :authenticator, :request, :response def initialize(&authenticator) @authenticator = authenticator end def call(env) # NOTE: # Rack middleware is initialized only on the first request of the process. # So any instance variables are acts like class variables, and modifying them in call() isn't thread-safe. # ref.) http://stackoverflow.com/questions/23028226/rack-middleware-and-thread-safety dup._call(env) end def _call(env) @authenticator.call(@request, @response) if @authenticator @response end end end end end endrack-oauth2-2.2.1/lib/rack/oauth2/server/abstract/request.rb000066400000000000000000000014271454275276700237010ustar00rootroot00000000000000module Rack module OAuth2 module Server module Abstract class Request < Rack::Request include AttrRequired, AttrOptional attr_required :client_id attr_optional :scope def initialize(env) super @client_id ||= params['client_id'] @scope = Array(params['scope'].to_s.split(' ')) end def attr_missing! if params['client_id'].present? && @client_id != params['client_id'] invalid_request! 'Multiple client credentials are provided.' end super rescue AttrRequired::AttrMissing => e invalid_request! e.message, state: @state, redirect_uri: @redirect_uri end end end end end end rack-oauth2-2.2.1/lib/rack/oauth2/server/abstract/response.rb000066400000000000000000000004201454275276700240370ustar00rootroot00000000000000module Rack module OAuth2 module Server module Abstract class Response < Rack::Response include AttrRequired, AttrOptional def initialize(request) super([], 200, {}) end end end end end endrack-oauth2-2.2.1/lib/rack/oauth2/server/authorize.rb000066400000000000000000000071201454275276700224140ustar00rootroot00000000000000module Rack module OAuth2 module Server class Authorize < Abstract::Handler def _call(env) request = Request.new(env) response_type_for(request).new(&@authenticator)._call(env).finish rescue Rack::OAuth2::Server::Abstract::Error => e e.finish end private def response_type_for(request) response_type = request.params['response_type'].to_s case response_type when 'code' Code when 'token' Token when '' request.attr_missing! else extensions.detect do |extension| extension.response_type_for? response_type end || request.unsupported_response_type! end end def extensions Extension.constants.sort.collect do |key| Extension.const_get key end end class Request < Abstract::Request include Server::Extension::ResponseMode::AuthorizationRequest attr_required :response_type attr_optional :redirect_uri, :state attr_accessor :verified_redirect_uri def initialize(env) super # NOTE: Raise before redirect_uri is saved not to redirect back to unverified redirect_uri. invalid_request! '"client_id" missing' if client_id.blank? @redirect_uri = Util.parse_uri(params['redirect_uri']) if params['redirect_uri'] @response_mode = params['response_mode'] @state = params['state'] end def verify_redirect_uri!(pre_registered, allow_partial_match = false) @verified_redirect_uri = if redirect_uri.present? verified = Array(pre_registered).any? do |_pre_registered_| if allow_partial_match Util.uri_match?(_pre_registered_, redirect_uri) else _pre_registered_.to_s == redirect_uri.to_s end end if verified redirect_uri else invalid_request! '"redirect_uri" mismatch' end elsif pre_registered.present? && Array(pre_registered).size == 1 && !allow_partial_match Array(pre_registered).first else invalid_request! '"redirect_uri" missing' end self.verified_redirect_uri.to_s end def error_params_location nil # => All errors are raised immediately and no error response are returned to client. end end class Response < Abstract::Response attr_required :redirect_uri attr_optional :state, :session_state, :approval def initialize(request) @state = request.state super end def approved? @approval end def approve! @approval = true end def protocol_params {state: state, session_state: session_state} end def redirect_uri_with_credentials Util.redirect_uri(redirect_uri, protocol_params_location, protocol_params) end def finish if approved? attr_missing! redirect redirect_uri_with_credentials end super end end end end end end require 'rack/oauth2/server/authorize/code' require 'rack/oauth2/server/authorize/token' require 'rack/oauth2/server/authorize/extension' require 'rack/oauth2/server/authorize/error' rack-oauth2-2.2.1/lib/rack/oauth2/server/authorize/000077500000000000000000000000001454275276700220675ustar00rootroot00000000000000rack-oauth2-2.2.1/lib/rack/oauth2/server/authorize/code.rb000066400000000000000000000015651454275276700233350ustar00rootroot00000000000000module Rack module OAuth2 module Server class Authorize class Code < Abstract::Handler def _call(env) @request = Request.new env @response = Response.new request super end class Request < Authorize::Request include Server::Extension::PKCE::AuthorizationRequest def initialize(env) super @response_type = :code attr_missing! end def error_params_location :query end end class Response < Authorize::Response attr_required :code def protocol_params super.merge(code: code) end def protocol_params_location :query end end end end end end end rack-oauth2-2.2.1/lib/rack/oauth2/server/authorize/error.rb000066400000000000000000000063151454275276700235520ustar00rootroot00000000000000module Rack module OAuth2 module Server class Authorize module ErrorHandler def self.included(klass) klass.send :attr_accessor, :redirect_uri, :state, :protocol_params_location end def protocol_params super.merge(state: state) end def redirect? redirect_uri.present? && protocol_params_location.present? end def finish if redirect? super do |response| response.redirect Util.redirect_uri(redirect_uri, protocol_params_location, protocol_params) end else raise self end end end class BadRequest < Abstract::BadRequest include ErrorHandler end class ServerError < Abstract::ServerError include ErrorHandler end class TemporarilyUnavailable < Abstract::TemporarilyUnavailable include ErrorHandler end module ErrorMethods DEFAULT_DESCRIPTION = { invalid_request: "The request is missing a required parameter, includes an unsupported parameter or parameter value, or is otherwise malformed.", unauthorized_client: "The client is not authorized to use the requested response type.", access_denied: "The end-user or authorization server denied the request.", unsupported_response_type: "The requested response type is not supported by the authorization server.", invalid_scope: "The requested scope is invalid, unknown, or malformed.", server_error: "Internal Server Error", temporarily_unavailable: "Service Unavailable" } def self.included(klass) DEFAULT_DESCRIPTION.each do |error, default_description| case error when :server_error, :temporarily_unavailable # NOTE: defined below else klass.class_eval <<-ERROR def #{error}!(description = "#{default_description}", options = {}) bad_request! :#{error}, description, options end ERROR end end end def bad_request!(error = :bad_request, description = nil, options = {}) error! BadRequest, error, description, options end def server_error!(description = DEFAULT_DESCRIPTION[:server_error], options = {}) error! ServerError, :server_error, description, options end def temporarily_unavailable!(description = DEFAULT_DESCRIPTION[:temporarily_unavailable], options = {}) error! TemporarilyUnavailable, :temporarily_unavailable, description, options end private def error!(klass, error, description, options) exception = klass.new error, description, options exception.protocol_params_location = error_params_location exception.state = state exception.redirect_uri = verified_redirect_uri raise exception end end Request.send :include, ErrorMethods end end end end rack-oauth2-2.2.1/lib/rack/oauth2/server/authorize/extension.rb000066400000000000000000000004271454275276700244330ustar00rootroot00000000000000module Rack module OAuth2 module Server class Authorize module Extension # Define your extension in this namespace and load it explicitly. # extension/code_and_token.rb would be good example for you. end end end end endrack-oauth2-2.2.1/lib/rack/oauth2/server/authorize/extension/000077500000000000000000000000001454275276700241035ustar00rootroot00000000000000rack-oauth2-2.2.1/lib/rack/oauth2/server/authorize/extension/code_and_token.rb000066400000000000000000000017701454275276700273710ustar00rootroot00000000000000module Rack module OAuth2 module Server class Authorize module Extension class CodeAndToken < Abstract::Handler class << self def response_type_for?(response_type) response_type.split.sort == ['code', 'token'] end end def _call(env) @request = Request.new env @response = Response.new request super end class Request < Authorize::Token::Request include Server::Extension::PKCE::AuthorizationRequest def initialize(env) super @response_type = [:code, :token] attr_missing! end end class Response < Authorize::Token::Response attr_required :code def protocol_params super.merge(code: code) end end end end end end end end rack-oauth2-2.2.1/lib/rack/oauth2/server/authorize/token.rb000066400000000000000000000017001454275276700235320ustar00rootroot00000000000000module Rack module OAuth2 module Server class Authorize class Token < Abstract::Handler def _call(env) @request = Request.new env @response = Response.new request super end class Request < Authorize::Request def initialize(env) super @response_type = :token attr_missing! end def error_params_location :fragment end end class Response < Authorize::Response attr_required :access_token def protocol_params super.merge( access_token.token_response.delete_if do |k, v| k == :refresh_token end ) end def protocol_params_location :fragment end end end end end end endrack-oauth2-2.2.1/lib/rack/oauth2/server/extension.rb000066400000000000000000000001401454275276700224110ustar00rootroot00000000000000require 'rack/oauth2/server/extension/pkce' require 'rack/oauth2/server/extension/response_mode'rack-oauth2-2.2.1/lib/rack/oauth2/server/extension/000077500000000000000000000000001454275276700220715ustar00rootroot00000000000000rack-oauth2-2.2.1/lib/rack/oauth2/server/extension/pkce.rb000066400000000000000000000025651454275276700233500ustar00rootroot00000000000000module Rack module OAuth2 module Server module Extension module PKCE module AuthorizationRequest def self.included(klass) klass.send :attr_optional, :code_challenge, :code_challenge_method end def initialize(env) super @code_challenge = params['code_challenge'] @code_challenge_method = params['code_challenge_method'] end end module TokenRequest def self.included(klass) klass.send :attr_optional, :code_verifier end def initialize(env) super @code_verifier = params['code_verifier'] end def verify_code_verifier!(code_challenge, code_challenge_method = :S256) if code_verifier.present? || code_challenge.present? case code_challenge_method&.to_sym when :S256 code_challenge == Util.urlsafe_base64_encode( OpenSSL::Digest::SHA256.digest(code_verifier.to_s) ) or invalid_grant! when :plain code_challenge == code_verifier or invalid_grant! else invalid_grant! end end end end end end end end endrack-oauth2-2.2.1/lib/rack/oauth2/server/extension/response_mode.rb000066400000000000000000000006451454275276700252650ustar00rootroot00000000000000module Rack module OAuth2 module Server module Extension module ResponseMode module AuthorizationRequest def self.included(klass) klass.send :attr_optional, :response_mode end def initialize(env) super @response_mode = params['response_mode'] end end end end end end endrack-oauth2-2.2.1/lib/rack/oauth2/server/rails.rb000066400000000000000000000004651454275276700215210ustar00rootroot00000000000000module Rack module OAuth2 module Server module Rails REQUEST = 'rack_oauth2.request' RESPONSE = 'rack_oauth2.response' ERROR = 'rack_oauth2.error' end end end end require 'rack/oauth2/server/rails/response_ext' require 'rack/oauth2/server/rails/authorize' rack-oauth2-2.2.1/lib/rack/oauth2/server/rails/000077500000000000000000000000001454275276700211675ustar00rootroot00000000000000rack-oauth2-2.2.1/lib/rack/oauth2/server/rails/authorize.rb000066400000000000000000000020301454275276700235210ustar00rootroot00000000000000module Rack module OAuth2 module Server module Rails class Authorize < Server::Authorize def initialize(app) super() @app = app end def _call(env) prepare_oauth_env env @app.call env rescue Rack::OAuth2::Server::Abstract::Error => e e.finish end private def prepare_oauth_env(env) response_type = response_type_for( Server::Authorize::Request.new(env) ).new response_type._call(env) response_type.response.extend ResponseExt env[REQUEST] = response_type.request env[RESPONSE] = response_type.response rescue Rack::OAuth2::Server::Abstract::Error => e env[ERROR] = e end module ResponseExt include Rails::ResponseExt def approve! super finish end end end end end end end rack-oauth2-2.2.1/lib/rack/oauth2/server/rails/response_ext.rb000066400000000000000000000014771454275276700242430ustar00rootroot00000000000000module Rack module OAuth2 module Server module Rails module ResponseExt def redirect? ensure_finish do super end end def location ensure_finish do super end end def json ensure_finish do @body end end def headers ensure_finish do @headers end end def finish @finished = true super end private def finished? !!@finished end def ensure_finish @status, @headers, @body = finish unless finished? yield end end end end end end rack-oauth2-2.2.1/lib/rack/oauth2/server/resource.rb000066400000000000000000000023531454275276700222340ustar00rootroot00000000000000module Rack module OAuth2 module Server class Resource < Abstract::Handler ACCESS_TOKEN = 'rack.oauth2.access_token' DEFAULT_REALM = 'Protected by OAuth 2.0' attr_accessor :realm, :request def initialize(app, realm = nil, &authenticator) @app = app @realm = realm super(&authenticator) end def _call(env) if request.oauth2? access_token = authenticate! request.setup! env[ACCESS_TOKEN] = access_token end @app.call(env) rescue Rack::OAuth2::Server::Abstract::Error => e e.realm ||= realm e.finish end private def authenticate!(request) @authenticator.call(request) end class Request < Rack::Request attr_reader :access_token def initialize(env) @env = env @auth_header = Rack::Auth::AbstractRequest.new(env) end def setup! raise 'Define me!' end def oauth2? raise 'Define me!' end end end end end end require 'rack/oauth2/server/resource/error' require 'rack/oauth2/server/resource/bearer' rack-oauth2-2.2.1/lib/rack/oauth2/server/resource/000077500000000000000000000000001454275276700217045ustar00rootroot00000000000000rack-oauth2-2.2.1/lib/rack/oauth2/server/resource/bearer.rb000066400000000000000000000023041454275276700234700ustar00rootroot00000000000000module Rack module OAuth2 module Server class Resource class Bearer < Resource def _call(env) self.request = Request.new(env) super end private class Request < Resource::Request def setup! tokens = [access_token_in_header, access_token_in_payload].compact @access_token = case Array(tokens).size when 1 tokens.first else invalid_request!('Both Authorization header and payload includes access token.') end self end def oauth2? (access_token_in_header || access_token_in_payload).present? end def access_token_in_header if @auth_header.provided? && !@auth_header.parts.first.nil? && @auth_header.scheme.to_s == 'bearer' @auth_header.params else nil end end def access_token_in_payload params['access_token'] end end end end end end end require 'rack/oauth2/server/resource/bearer/error' rack-oauth2-2.2.1/lib/rack/oauth2/server/resource/bearer/000077500000000000000000000000001454275276700231445ustar00rootroot00000000000000rack-oauth2-2.2.1/lib/rack/oauth2/server/resource/bearer/error.rb000066400000000000000000000010451454275276700246220ustar00rootroot00000000000000module Rack module OAuth2 module Server class Resource class Bearer class Unauthorized < Resource::Unauthorized def scheme :Bearer end end module ErrorMethods include Resource::ErrorMethods def unauthorized!(error = nil, description = nil, options = {}) raise Unauthorized.new(error, description, options) end end Request.send :include, ErrorMethods end end end end endrack-oauth2-2.2.1/lib/rack/oauth2/server/resource/error.rb000066400000000000000000000053241454275276700233660ustar00rootroot00000000000000module Rack module OAuth2 module Server class Resource class BadRequest < Abstract::BadRequest end class Unauthorized < Abstract::Unauthorized def scheme raise 'Define me!' end def finish super do |response| self.realm ||= DEFAULT_REALM headers = response.headers['WWW-Authenticate'] = "#{scheme} realm=\"#{realm}\"" if ErrorMethods::DEFAULT_DESCRIPTION.keys.include?(error) headers << ", error=\"#{error}\"" headers << ", error_description=\"#{description}\"" if description.present? headers << ", error_uri=\"#{uri}\"" if uri.present? end end end end class Forbidden < Abstract::Forbidden attr_accessor :scope def initialize(error = :forbidden, description = nil, options = {}) super @scope = options[:scope] end def protocol_params super.merge(scope: Array(scope).join(' ')) end end module ErrorMethods DEFAULT_DESCRIPTION = { invalid_request: "The request is missing a required parameter, includes an unsupported parameter or parameter value, repeats the same parameter, uses more than one method for including an access token, or is otherwise malformed.", invalid_token: "The access token provided is expired, revoked, malformed or invalid for other reasons.", insufficient_scope: "The request requires higher privileges than provided by the access token." } def self.included(klass) DEFAULT_DESCRIPTION.each do |error, default_description| error_method = case error when :invalid_request :bad_request! when :insufficient_scope :forbidden! else :unauthorized! end klass.class_eval <<-ERROR def #{error}!(description = "#{default_description}", options = {}) #{error_method} :#{error}, description, options end ERROR end end def bad_request!(error, description = nil, options = {}) raise BadRequest.new(error, description, options) end def unauthorized!(error = nil, description = nil, options = {}) raise 'Define me!' end def forbidden!(error, description = nil, options = {}) raise Forbidden.new(error, description, options) end end Request.send :include, ErrorMethods end end end end rack-oauth2-2.2.1/lib/rack/oauth2/server/token.rb000066400000000000000000000061031454275276700215220ustar00rootroot00000000000000require 'rack/auth/basic' module Rack module OAuth2 module Server class Token < Abstract::Handler def _call(env) request = Request.new(env) grant_type_for(request).new(&@authenticator)._call(env).finish rescue Rack::OAuth2::Server::Abstract::Error => e e.finish end private def grant_type_for(request) case request.grant_type when 'authorization_code' AuthorizationCode when 'password' Password when 'client_credentials' ClientCredentials when 'refresh_token' RefreshToken when URN::GrantType::JWT_BEARER JWTBearer when URN::GrantType::SAML2_BEARER SAML2Bearer when '' request.attr_missing! else extensions.detect do |extension| extension.grant_type_for? request.grant_type end || request.unsupported_grant_type! end end def extensions Extension.constants.sort.collect do |key| Extension.const_get key end end class Request < Abstract::Request attr_required :grant_type attr_optional :client_secret, :client_assertion, :client_assertion_type def initialize(env) auth = Rack::Auth::Basic::Request.new(env) if auth.provided? && auth.basic? @client_id, @client_secret = auth.credentials.map do |cred| Util.www_form_url_decode cred end super else super @client_secret = params['client_secret'] @client_assertion = params['client_assertion'] @client_assertion_type = params['client_assertion_type'] if client_assertion.present? && client_assertion_type == URN::ClientAssertionType::JWT_BEARER require 'json/jwt' @client_id = JSON::JWT.decode( client_assertion, :skip_verification )[:sub] rescue nil end end @grant_type = params['grant_type'].to_s end end class Response < Abstract::Response attr_required :access_token def protocol_params access_token.token_response end def finish attr_missing! write Util.compact_hash(protocol_params).to_json headers['Content-Type'] = 'application/json' headers['Cache-Control'] = 'no-store' headers['Pragma'] = 'no-cache' super end end end end end end require 'rack/oauth2/server/token/authorization_code' require 'rack/oauth2/server/token/password' require 'rack/oauth2/server/token/client_credentials' require 'rack/oauth2/server/token/refresh_token' require 'rack/oauth2/server/token/jwt_bearer' require 'rack/oauth2/server/token/saml2_bearer' require 'rack/oauth2/server/token/extension' require 'rack/oauth2/server/token/error' rack-oauth2-2.2.1/lib/rack/oauth2/server/token/000077500000000000000000000000001454275276700211755ustar00rootroot00000000000000rack-oauth2-2.2.1/lib/rack/oauth2/server/token/authorization_code.rb000066400000000000000000000013331454275276700254140ustar00rootroot00000000000000module Rack module OAuth2 module Server class Token class AuthorizationCode < Abstract::Handler def _call(env) @request = Request.new(env) @response = Response.new(request) super end class Request < Token::Request include Server::Extension::PKCE::TokenRequest attr_required :code attr_optional :redirect_uri def initialize(env) super @grant_type = :authorization_code @code = params['code'] @redirect_uri = params['redirect_uri'] attr_missing! end end end end end end endrack-oauth2-2.2.1/lib/rack/oauth2/server/token/client_credentials.rb000066400000000000000000000007631454275276700253630ustar00rootroot00000000000000module Rack module OAuth2 module Server class Token class ClientCredentials < Abstract::Handler def _call(env) @request = Request.new(env) @response = Response.new(request) super end class Request < Token::Request def initialize(env) super @grant_type = :client_credentials attr_missing! end end end end end end endrack-oauth2-2.2.1/lib/rack/oauth2/server/token/error.rb000066400000000000000000000047271454275276700226650ustar00rootroot00000000000000module Rack module OAuth2 module Server class Token class BadRequest < Abstract::BadRequest end class Unauthorized < Abstract::Unauthorized def finish super do |response| unless @skip_www_authenticate response.headers['WWW-Authenticate'] = 'Basic realm="OAuth2 Token Endpoint"' end end end end module ErrorMethods DEFAULT_DESCRIPTION = { invalid_request: "The request is missing a required parameter, includes an unsupported parameter or parameter value, repeats a parameter, includes multiple credentials, utilizes more than one mechanism for authenticating the client, or is otherwise malformed.", invalid_client: "The client identifier provided is invalid, the client failed to authenticate, the client did not include its credentials, provided multiple client credentials, or used unsupported credentials type.", invalid_grant: "The provided access grant is invalid, expired, or revoked (e.g. invalid assertion, expired authorization token, bad end-user password credentials, or mismatching authorization code and redirection URI).", unauthorized_client: "The authenticated client is not authorized to use the access grant type provided.", unsupported_grant_type: "The access grant included - its type or another attribute - is not supported by the authorization server.", invalid_scope: "The requested scope is invalid, unknown, malformed, or exceeds the previously granted scope." } def self.included(klass) DEFAULT_DESCRIPTION.each do |error, default_description| error_method = if error == :invalid_client :unauthorized! else :bad_request! end klass.class_eval <<-ERROR def #{error}!(description = "#{default_description}", options = {}) #{error_method} :#{error}, description, options end ERROR end end def bad_request!(error, description = nil, options = {}) raise BadRequest.new(error, description, options) end def unauthorized!(error, description = nil, options = {}) raise Unauthorized.new(error, description, options) end end Request.send :include, ErrorMethods end end end end rack-oauth2-2.2.1/lib/rack/oauth2/server/token/extension.rb000066400000000000000000000004261454275276700235400ustar00rootroot00000000000000module Rack module OAuth2 module Server class Token module Extension # Define your extension in this namespace and load it explicitly. # extension/assertion/example.rb would be good example for you. end end end end endrack-oauth2-2.2.1/lib/rack/oauth2/server/token/extension/000077500000000000000000000000001454275276700232115ustar00rootroot00000000000000rack-oauth2-2.2.1/lib/rack/oauth2/server/token/extension/example.rb000066400000000000000000000016041454275276700251720ustar00rootroot00000000000000module Rack module OAuth2 module Server class Token module Extension class Example < Abstract::Handler GRANT_TYPE_URN = 'urn:ietf:params:oauth:grant-type:example' class << self def grant_type_for?(grant_type) grant_type == GRANT_TYPE_URN end end def _call(env) @request = Request.new env @response = Response.new request super end class Request < Token::Request attr_required :assertion attr_optional :client_id def initialize(env) super @grant_type = GRANT_TYPE_URN @assertion = params['assertion'] attr_missing! end end end end end end end endrack-oauth2-2.2.1/lib/rack/oauth2/server/token/jwt_bearer.rb000066400000000000000000000011521454275276700236450ustar00rootroot00000000000000module Rack module OAuth2 module Server class Token class JWTBearer < Abstract::Handler def _call(env) @request = Request.new env @response = Response.new request super end class Request < Token::Request attr_required :assertion attr_optional :client_id def initialize(env) super @grant_type = URN::GrantType::JWT_BEARER @assertion = params['assertion'] attr_missing! end end end end end end endrack-oauth2-2.2.1/lib/rack/oauth2/server/token/password.rb000066400000000000000000000011561454275276700233670ustar00rootroot00000000000000module Rack module OAuth2 module Server class Token class Password < Abstract::Handler def _call(env) @request = Request.new(env) @response = Response.new(request) super end class Request < Token::Request attr_required :username, :password def initialize(env) super @grant_type = :password @username = params['username'] @password = params['password'] attr_missing! end end end end end end endrack-oauth2-2.2.1/lib/rack/oauth2/server/token/refresh_token.rb000066400000000000000000000011151454275276700243560ustar00rootroot00000000000000module Rack module OAuth2 module Server class Token class RefreshToken < Abstract::Handler def _call(env) @request = Request.new(env) @response = Response.new(request) super end class Request < Token::Request attr_required :refresh_token def initialize(env) super @grant_type = :refresh_token @refresh_token = params['refresh_token'] attr_missing! end end end end end end endrack-oauth2-2.2.1/lib/rack/oauth2/server/token/saml2_bearer.rb000066400000000000000000000011561454275276700240630ustar00rootroot00000000000000module Rack module OAuth2 module Server class Token class SAML2Bearer < Abstract::Handler def _call(env) @request = Request.new env @response = Response.new request super end class Request < Token::Request attr_required :assertion attr_optional :client_id def initialize(env) super @grant_type = URN::GrantType::SAML2_BEARER @assertion = params['assertion'] attr_missing! end end end end end end endrack-oauth2-2.2.1/lib/rack/oauth2/urn.rb000066400000000000000000000015331454275276700177020ustar00rootroot00000000000000module Rack module OAuth2 module URN module TokenType JWT = 'urn:ietf:params:oauth:token-type:jwt' # RFC7519 ACCESS_TOKEN = 'urn:ietf:params:oauth:token-type:access_token' # RFC8693 REFRESH_TOKEN = 'urn:ietf:params:oauth:token-type:refresh_token' # RFC8693 end module GrantType JWT_BEARER = 'urn:ietf:params:oauth:grant-type:jwt-bearer' # RFC7523 SAML2_BEARER = 'urn:ietf:params:oauth:grant-type:saml2-bearer' # RFC7522 TOKEN_EXCHANGE = 'urn:ietf:params:oauth:grant-type:token-exchange' # RFC8693 end module ClientAssertionType JWT_BEARER = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer' # RFC7523 SAML2_BEARER = 'urn:ietf:params:oauth:client-assertion-type:saml2-bearer' # RFC7522 end end end endrack-oauth2-2.2.1/lib/rack/oauth2/util.rb000066400000000000000000000032601454275276700200520ustar00rootroot00000000000000require 'base64' module Rack module OAuth2 module Util class << self def www_form_url_encode(text) URI.encode_www_form_component(text) end def www_form_url_decode(text) URI.decode_www_form_component(text) end def base64_encode(text) Base64.encode64(text).delete("\n") end def urlsafe_base64_encode(text) Base64.urlsafe_encode64(text, padding: false) end def compact_hash(hash) hash.reject do |key, value| value.blank? end end def parse_uri(uri) case uri when URI::Generic uri when String URI.parse(uri) else raise "Invalid format of URI is given." end end def redirect_uri(base_uri, location, params) redirect_uri = parse_uri base_uri encoded_response_params = Util.compact_hash(params).to_query.gsub('+', '%20') case location when :query redirect_uri.query = [redirect_uri.query, encoded_response_params].compact.join('&') when :fragment redirect_uri.fragment = encoded_response_params end redirect_uri.to_s end def uri_match?(base, given) base = parse_uri(base) given = parse_uri(given) base.path = '/' if base.path.blank? given.path = '/' if given.path.blank? [:scheme, :host, :port].all? do |key| base.send(key) == given.send(key) end && !!(/^#{base.path}/ =~ given.path) rescue false end end end end end rack-oauth2-2.2.1/rack-oauth2.gemspec000066400000000000000000000023341454275276700173460ustar00rootroot00000000000000Gem::Specification.new do |s| s.name = 'rack-oauth2' s.version = File.read('VERSION') s.authors = ['nov matake'] s.description = %q{OAuth 2.0 Server & Client Library. Both Bearer token type are supported.} s.summary = %q{OAuth 2.0 Server & Client Library - Both Bearer token type are supported} s.email = 'nov@matake.jp' s.extra_rdoc_files = ['LICENSE', 'README.rdoc'] s.rdoc_options = ['--charset=UTF-8'] s.homepage = 'https://github.com/nov/rack-oauth2' s.license = 'MIT' s.require_paths = ['lib'] s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.add_runtime_dependency 'rack', '>= 2.1.0' s.add_runtime_dependency 'faraday', '~> 2.0' s.add_runtime_dependency 'faraday-follow_redirects' s.add_runtime_dependency 'activesupport' s.add_runtime_dependency 'attr_required' s.add_runtime_dependency 'json-jwt', '>= 1.11.0' s.add_development_dependency 'rake' s.add_development_dependency 'simplecov' s.add_development_dependency 'rspec' s.add_development_dependency 'rspec-its' s.add_development_dependency 'webmock' s.add_development_dependency 'rexml' end rack-oauth2-2.2.1/spec/000077500000000000000000000000001454275276700146115ustar00rootroot00000000000000rack-oauth2-2.2.1/spec/helpers/000077500000000000000000000000001454275276700162535ustar00rootroot00000000000000rack-oauth2-2.2.1/spec/helpers/time.rb000066400000000000000000000005061454275276700175370ustar00rootroot00000000000000class Time class << self module NowWithFixedTime def now if @fixed_time @fixed_time.dup else super end end end prepend NowWithFixedTime def fix(time = Time.now) @fixed_time = time yield ensure @fixed_time = nil end end end rack-oauth2-2.2.1/spec/helpers/webmock_helper.rb000066400000000000000000000020751454275276700215720ustar00rootroot00000000000000require 'webmock/rspec' module WebMockHelper def mock_response(method, endpoint, response_file, options = {}) stub_request(method, endpoint).with( request_for(method, options) ).to_return( response_for(response_file, options) ) end private def request_for(method, options = {}) request = {} params = options&.[](:params) || {} case method when :post, :put, :delete request[:body] = params else request[:query] = params end if options[:request_header] request[:headers] = options[:request_header] end request end def response_for(response_file, options = {}) response = {} format = options[:format] || :json if format == :json response[:headers] = { 'Content-Type': 'application/json' } end response[:body] = File.new(File.join(File.dirname(__FILE__), '../mock_response', "#{response_file}.#{format}")) if options[:status] response[:status] = options[:status] end response end end include WebMockHelper WebMock.disable_net_connect!rack-oauth2-2.2.1/spec/mock_response/000077500000000000000000000000001454275276700174605ustar00rootroot00000000000000rack-oauth2-2.2.1/spec/mock_response/blank.txt000066400000000000000000000000001454275276700212760ustar00rootroot00000000000000rack-oauth2-2.2.1/spec/mock_response/errors/000077500000000000000000000000001454275276700207745ustar00rootroot00000000000000rack-oauth2-2.2.1/spec/mock_response/errors/invalid_request.json000066400000000000000000000001121454275276700250570ustar00rootroot00000000000000{ "error":"invalid_request", "error_description":"error description" }rack-oauth2-2.2.1/spec/mock_response/resources/000077500000000000000000000000001454275276700214725ustar00rootroot00000000000000rack-oauth2-2.2.1/spec/mock_response/resources/fake.txt000066400000000000000000000000041454275276700231330ustar00rootroot00000000000000fakerack-oauth2-2.2.1/spec/mock_response/tokens/000077500000000000000000000000001454275276700207635ustar00rootroot00000000000000rack-oauth2-2.2.1/spec/mock_response/tokens/_Bearer.json000066400000000000000000000001641454275276700232160ustar00rootroot00000000000000{ "access_token":"access_token", "refresh_token":"refresh_token", "token_type":"Bearer", "expires_in":3600 }rack-oauth2-2.2.1/spec/mock_response/tokens/bearer.json000066400000000000000000000001641454275276700231170ustar00rootroot00000000000000{ "access_token":"access_token", "refresh_token":"refresh_token", "token_type":"bearer", "expires_in":3600 }rack-oauth2-2.2.1/spec/mock_response/tokens/unknown.json000066400000000000000000000001651454275276700233570ustar00rootroot00000000000000{ "access_token":"access_token", "refresh_token":"refresh_token", "token_type":"unknown", "expires_in":3600 }rack-oauth2-2.2.1/spec/rack/000077500000000000000000000000001454275276700155315ustar00rootroot00000000000000rack-oauth2-2.2.1/spec/rack/oauth2/000077500000000000000000000000001454275276700167335ustar00rootroot00000000000000rack-oauth2-2.2.1/spec/rack/oauth2/access_token/000077500000000000000000000000001454275276700213745ustar00rootroot00000000000000rack-oauth2-2.2.1/spec/rack/oauth2/access_token/authenticator_spec.rb000066400000000000000000000013251454275276700256060ustar00rootroot00000000000000require 'spec_helper' describe Rack::OAuth2::AccessToken::Authenticator do let(:resource_endpoint) { 'https://server.example.com/resources/fake' } let(:request) { Faraday::Request.new(:get, URI.parse(resource_endpoint)) } let(:authenticator) { Rack::OAuth2::AccessToken::Authenticator.new(token) } shared_examples_for :authenticator do it 'should let the token authenticate the request' do expect(token).to receive(:authenticate).with(request) authenticator.authenticate(request) end end context 'when Bearer token is given' do let(:token) do Rack::OAuth2::AccessToken::Bearer.new( access_token: 'access_token' ) end it_behaves_like :authenticator end end rack-oauth2-2.2.1/spec/rack/oauth2/access_token/bearer_spec.rb000066400000000000000000000010741454275276700241750ustar00rootroot00000000000000require 'spec_helper' describe Rack::OAuth2::AccessToken::Bearer do let :token do Rack::OAuth2::AccessToken::Bearer.new( access_token: 'access_token' ) end let(:resource_endpoint) { 'https://server.example.com/resources/fake' } let(:request) { Faraday::Request.new(:post, URI.parse(resource_endpoint), '', {hello: "world"}, {}) } describe '.authenticate' do it 'should set Authorization header' do expect(request.headers).to receive(:[]=).with('Authorization', 'Bearer access_token') token.authenticate(request) end end end rack-oauth2-2.2.1/spec/rack/oauth2/access_token_spec.rb000066400000000000000000000030451454275276700227350ustar00rootroot00000000000000require 'spec_helper' describe Rack::OAuth2::AccessToken do let :token do Rack::OAuth2::AccessToken::Bearer.new( access_token: 'access_token', refresh_token: 'refresh_token', expires_in: 3600, scope: [:scope1, :scope2] ) end subject { token } its(:access_token) { should == 'access_token' } its(:refresh_token) { should == 'refresh_token' } its(:expires_in) { should == 3600 } its(:scope) { should == [:scope1, :scope2] } its(:token_response) do should == { token_type: :bearer, access_token: 'access_token', refresh_token: 'refresh_token', expires_in: 3600, scope: 'scope1 scope2' } end context 'when access_token is missing' do it do expect do Rack::OAuth2::AccessToken::Bearer.new( refresh_token: 'refresh_token', expires_in: 3600, scope: [:scope1, :scope2] ) end.to raise_error AttrRequired::AttrMissing end end context 'otherwise' do it do expect do Rack::OAuth2::AccessToken::Bearer.new( access_token: 'access_token' ) end.not_to raise_error end end let(:resource_endpoint) { 'https://server.example.com/resources/fake' } [:get, :delete, :post, :put].each do |method| context 'when extension params given' do subject do Rack::OAuth2::AccessToken::Bearer.new( access_token: 'access_token', ex_key: :ex_value ) end its(:raw_attributes) { should include :ex_key } end end end rack-oauth2-2.2.1/spec/rack/oauth2/client/000077500000000000000000000000001454275276700202115ustar00rootroot00000000000000rack-oauth2-2.2.1/spec/rack/oauth2/client/error_spec.rb000066400000000000000000000007431454275276700227050ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Client::Error do let :error do { error: :invalid_request, error_description: 'Include invalid parameters', error_uri: 'http://server.example.com/error/invalid_request' } end subject do Rack::OAuth2::Client::Error.new 400, error end its(:status) { should == 400 } its(:message) { should == [error[:error], error[:error_description]].join(' :: ') } its(:response) { should == error } end rack-oauth2-2.2.1/spec/rack/oauth2/client/grant/000077500000000000000000000000001454275276700213245ustar00rootroot00000000000000rack-oauth2-2.2.1/spec/rack/oauth2/client/grant/authorization_code_spec.rb000066400000000000000000000017751454275276700265670ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Client::Grant::AuthorizationCode do let(:redirect_uri) { 'https://client.example.com/callback' } let(:grant) { Rack::OAuth2::Client::Grant::AuthorizationCode } context 'when code is given' do let :attributes do {code: 'code'} end context 'when redirect_uri is given' do let :attributes do {code: 'code', redirect_uri: redirect_uri} end subject { grant.new attributes } its(:redirect_uri) { should == redirect_uri } its(:as_json) do should == {grant_type: :authorization_code, code: 'code', redirect_uri: redirect_uri} end end context 'otherwise' do subject { grant.new attributes } its(:redirect_uri) { should be_nil } its(:as_json) do should == {grant_type: :authorization_code, code: 'code', redirect_uri: nil} end end end context 'otherwise' do it do expect { grant.new }.to raise_error AttrRequired::AttrMissing end end end rack-oauth2-2.2.1/spec/rack/oauth2/client/grant/client_credentials_spec.rb000066400000000000000000000002421454275276700265140ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Client::Grant::ClientCredentials do its(:as_json) do should == {grant_type: :client_credentials} end end rack-oauth2-2.2.1/spec/rack/oauth2/client/grant/jwt_bearer_spec.rb000066400000000000000000000010641454275276700250100ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Client::Grant::JWTBearer do let(:grant) { Rack::OAuth2::Client::Grant::JWTBearer } context 'when JWT assertion is given' do let :attributes do {assertion: 'header.payload.signature'} end subject { grant.new attributes } its(:as_json) do should == {grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', assertion: 'header.payload.signature'} end end context 'otherwise' do it do expect { grant.new }.to raise_error AttrRequired::AttrMissing end end end rack-oauth2-2.2.1/spec/rack/oauth2/client/grant/password_spec.rb000066400000000000000000000014311454275276700245240ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Client::Grant::Password do let(:grant) { Rack::OAuth2::Client::Grant::Password } context 'when username is given' do let :attributes do {username: 'username'} end context 'when password is given' do let :attributes do {username: 'username', password: 'password'} end subject { grant.new attributes } its(:as_json) do should == {grant_type: :password, username: 'username', password: 'password'} end end context 'otherwise' do it do expect { grant.new attributes }.to raise_error AttrRequired::AttrMissing end end end context 'otherwise' do it do expect { grant.new }.to raise_error AttrRequired::AttrMissing end end end rack-oauth2-2.2.1/spec/rack/oauth2/client/grant/refresh_token_spec.rb000066400000000000000000000010151454275276700255160ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Client::Grant::RefreshToken do let(:grant) { Rack::OAuth2::Client::Grant::RefreshToken } context 'when refresh_token is given' do let :attributes do {refresh_token: 'refresh_token'} end subject { grant.new attributes } its(:as_json) do should == {grant_type: :refresh_token, refresh_token: 'refresh_token'} end end context 'otherwise' do it do expect { grant.new }.to raise_error AttrRequired::AttrMissing end end end rack-oauth2-2.2.1/spec/rack/oauth2/client/grant/saml2_bearer_spec.rb000066400000000000000000000010461454275276700252220ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Client::Grant::SAML2Bearer do let(:grant) { Rack::OAuth2::Client::Grant::SAML2Bearer } context 'when JWT assertion is given' do let :attributes do {assertion: '...'} end subject { grant.new attributes } its(:as_json) do should == {grant_type: 'urn:ietf:params:oauth:grant-type:saml2-bearer', assertion: '...'} end end context 'otherwise' do it do expect { grant.new }.to raise_error AttrRequired::AttrMissing end end end rack-oauth2-2.2.1/spec/rack/oauth2/client_spec.rb000066400000000000000000000404071454275276700215550ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Client do let(:client_id) { 'client_id' } let(:client_secret) { 'client_secret' } let :client do Rack::OAuth2::Client.new( identifier: client_id, secret: client_secret, host: 'server.example.com', redirect_uri: 'https://client.example.com/callback', revocation_endpoint: '/oauth2/revoke' ) end subject { client } its(:identifier) { should == 'client_id' } its(:secret) { should == 'client_secret' } its(:authorization_endpoint) { should == '/oauth2/authorize' } its(:token_endpoint) { should == '/oauth2/token' } its(:revocation_endpoint) { should == '/oauth2/revoke' } context 'when identifier is missing' do it do expect { Rack::OAuth2::Client.new }.to raise_error AttrRequired::AttrMissing end end describe '#authorization_uri' do subject { client.authorization_uri } it { should include 'https://server.example.com/oauth2/authorize' } it { should include 'client_id=client_id' } it { should include 'redirect_uri=https%3A%2F%2Fclient.example.com%2Fcallback' } it { should include 'response_type=code' } context 'when endpoints are absolute URIs' do before do client.authorization_endpoint = 'https://server2.example.com/oauth/authorize' client.token_endpoint = 'https://server2.example.com/oauth/token' end it { should include 'https://server2.example.com/oauth/authorize' } end context 'when scheme is specified' do before { client.scheme = 'http' } it { should include 'http://server.example.com/oauth2/authorize' } end context 'when response_type is token' do subject { client.authorization_uri(response_type: :token) } it { should include 'response_type=token' } end context 'when response_type is an Array' do subject { client.authorization_uri(response_type: [:token, :code]) } it { should include 'response_type=token%20code' } end context 'when scope is given' do subject { client.authorization_uri(scope: [:scope1, :scope2]) } it { should include 'scope=scope1%20scope2' } end end describe '#authorization_code=' do before { client.authorization_code = 'code' } subject { client.instance_variable_get('@grant') } it { should be_instance_of Rack::OAuth2::Client::Grant::AuthorizationCode } end describe '#resource_owner_credentials=' do before { client.resource_owner_credentials = 'username', 'password' } subject { client.instance_variable_get('@grant') } it { should be_instance_of Rack::OAuth2::Client::Grant::Password } end describe '#refresh_token=' do before { client.refresh_token = 'refresh_token' } subject { client.instance_variable_get('@grant') } it { should be_instance_of Rack::OAuth2::Client::Grant::RefreshToken } end describe '#access_token!' do subject { client.access_token! } context '*args handling' do describe 'client authentication method' do before do client.authorization_code = 'code' end it 'should be Basic auth as default' do mock_response( :post, 'https://server.example.com/oauth2/token', 'tokens/bearer', request_header: { 'Authorization' => 'Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=' } ) client.access_token! end context 'when Basic auth method is used' do context 'when client_id is a url' do let(:client_id) { 'https://client.example.com'} it 'should be encoded in "application/x-www-form-urlencoded"' do mock_response( :post, 'https://server.example.com/oauth2/token', 'tokens/bearer', request_header: { 'Authorization' => 'Basic aHR0cHMlM0ElMkYlMkZjbGllbnQuZXhhbXBsZS5jb206Y2xpZW50X3NlY3JldA==' } ) client.access_token! end end end context 'when basic_without_www_form_urlencode method is used' do context 'when client_id is a url' do let(:client_id) { 'https://client.example.com'} it 'should be encoded in "application/x-www-form-urlencoded"' do mock_response( :post, 'https://server.example.com/oauth2/token', 'tokens/bearer', request_header: { 'Authorization' => 'Basic aHR0cHM6Ly9jbGllbnQuZXhhbXBsZS5jb206Y2xpZW50X3NlY3JldA==' } ) client.access_token! :basic_without_www_form_urlencode end end end context 'when jwt_bearer auth method specified' do context 'when client_secret is given' do it 'should be JWT bearer client assertion w/ auto-generated HS256-signed JWT assertion' do mock_response( :post, 'https://server.example.com/oauth2/token', 'tokens/bearer', params: { client_assertion: /^eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9\..+/, # NOTE: HS256 client_assertion_type: Rack::OAuth2::URN::ClientAssertionType::JWT_BEARER, code: 'code', grant_type: 'authorization_code', redirect_uri: 'https://client.example.com/callback' } ) client.access_token! :jwt_bearer end end context 'when private_key is given' do context 'when RSA key' do let :client do Rack::OAuth2::Client.new( identifier: 'client_id', private_key: OpenSSL::PKey::RSA.generate(2048), host: 'server.example.com', redirect_uri: 'https://client.example.com/callback' ) end it 'should be JWT bearer client assertion w/ auto-generated RS256-signed JWT assertion' do mock_response( :post, 'https://server.example.com/oauth2/token', 'tokens/bearer', params: { client_assertion: /^eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9\..+/, # NOTE: RS256 client_assertion_type: Rack::OAuth2::URN::ClientAssertionType::JWT_BEARER, code: 'code', grant_type: 'authorization_code', redirect_uri: 'https://client.example.com/callback' } ) client.access_token! :jwt_bearer end end context 'when EC key' do let :client do Rack::OAuth2::Client.new( identifier: 'client_id', private_key: OpenSSL::PKey::EC.generate('prime256v1'), host: 'server.example.com', redirect_uri: 'https://client.example.com/callback' ) end it 'should be JWT bearer client assertion w/ auto-generated ES256-signed JWT assertion' do mock_response( :post, 'https://server.example.com/oauth2/token', 'tokens/bearer', params: { client_assertion: /^eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9\..+/, # NOTE: ES256 client_assertion_type: Rack::OAuth2::URN::ClientAssertionType::JWT_BEARER, code: 'code', grant_type: 'authorization_code', redirect_uri: 'https://client.example.com/callback' } ) client.access_token! :jwt_bearer end end end context 'when client_assertion is explicitly given' do let :client do Rack::OAuth2::Client.new( identifier: 'client_id', host: 'server.example.com', redirect_uri: 'https://client.example.com/callback' ) end it 'should be JWT bearer client assertion w/ specified assertion' do mock_response( :post, 'https://server.example.com/oauth2/token', 'tokens/bearer', params: { client_assertion: 'any.jwt.assertion', client_assertion_type: Rack::OAuth2::URN::ClientAssertionType::JWT_BEARER, code: 'code', grant_type: 'authorization_code', redirect_uri: 'https://client.example.com/callback' } ) client.access_token! :jwt_bearer, client_assertion: 'any.jwt.assertion' end end end context 'when other auth method specified' do it 'should be body params' do mock_response( :post, 'https://server.example.com/oauth2/token', 'tokens/bearer', params: { client_id: 'client_id', client_secret: 'client_secret', code: 'code', grant_type: 'authorization_code', redirect_uri: 'https://client.example.com/callback' } ) client.access_token! :client_auth_body end end context 'when auth method is specified as Hash' do it 'should be removed before sending request' do mock_response( :post, 'https://server.example.com/oauth2/token', 'tokens/bearer', params: { client_id: 'client_id', client_secret: 'client_secret', code: 'code', grant_type: 'authorization_code', redirect_uri: 'https://client.example.com/callback' } ) client.access_token! client_auth_method: :body end end end describe 'scopes' do context 'when scope option given' do it 'should specify given scope' do mock_response( :post, 'https://server.example.com/oauth2/token', 'tokens/bearer', params: { grant_type: 'client_credentials', scope: 'a b' } ) client.access_token! scope: [:a, :b] end end end describe 'unknown params' do it 'should be included in body params' do mock_response( :post, 'https://server.example.com/oauth2/token', 'tokens/bearer', params: { grant_type: 'client_credentials', resource: 'something' } ) client.access_token! resource: :something end end end context 'local_http_config handling' do it do mock_response( :post, 'https://server.example.com/oauth2/token', 'tokens/bearer', request_header: { 'Authorization' => 'Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=', 'X-Foo' => 'bar' } ) client.access_token! do |request| request.headers['X-Foo'] = 'bar' end end end context 'when bearer token is given' do before do client.authorization_code = 'code' mock_response( :post, 'https://server.example.com/oauth2/token', 'tokens/bearer' ) end it { should be_instance_of Rack::OAuth2::AccessToken::Bearer } its(:token_type) { should == :bearer } its(:access_token) { should == 'access_token' } its(:refresh_token) { should == 'refresh_token' } its(:expires_in) { should == 3600 } context 'when token type is "Bearer", not "bearer"' do before do client.authorization_code = 'code' mock_response( :post, 'https://server.example.com/oauth2/token', 'tokens/_Bearer' ) end it { should be_instance_of Rack::OAuth2::AccessToken::Bearer } its(:token_type) { should == :bearer } end end context 'when unknown-type token is given' do before do client.authorization_code = 'code' mock_response( :post, 'https://server.example.com/oauth2/token', 'tokens/unknown' ) end it do expect { client.access_token! }.to raise_error(StandardError, 'Unknown Token Type') end end context 'when error response is given' do before do mock_response( :post, 'https://server.example.com/oauth2/token', 'errors/invalid_request', status: 400 ) end it do expect { client.access_token! }.to raise_error Rack::OAuth2::Client::Error end end context 'when no body given' do context 'when error given' do before do mock_response( :post, 'https://server.example.com/oauth2/token', 'blank', format: 'txt', status: 400 ) end it do expect { client.access_token! }.to raise_error Rack::OAuth2::Client::Error end end end end describe '#revoke!' do context 'local_http_config handling' do it do mock_response( :post, 'https://server.example.com/oauth2/revoke', 'blank', format: 'txt', status: 200, body: { token: 'access_token', token_type_hint: 'access_token' }, request_header: { 'Authorization' => 'Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=', 'X-Foo' => 'bar' } ) client.revoke!(access_token: 'access_token') do |request| request.headers['X-Foo'] = 'bar' end end end context 'when access_token given' do before do mock_response( :post, 'https://server.example.com/oauth2/revoke', 'blank', format: 'txt', status: 200, body: { token: 'access_token', token_type_hint: 'access_token' } ) end it do client.revoke!(access_token: 'access_token').should == :success end end context 'when refresh_token given' do before do mock_response( :post, 'https://server.example.com/oauth2/revoke', 'blank', format: 'txt', status: 200, body: { token: 'refresh_token', token_type_hint: 'refresh_token' } ) end context 'as argument' do it do client.revoke!(refresh_token: 'refresh_token').should == :success end end context 'as grant' do it do client.refresh_token = 'refresh_token' client.revoke! end end end context 'when error response given' do before do mock_response( :post, 'https://server.example.com/oauth2/revoke', 'errors/invalid_request', status: 400 ) end it do expect do client.revoke! access_token: 'access_token' end.to raise_error Rack::OAuth2::Client::Error end end context 'when no token given' do it do expect do client.revoke! end.to raise_error ArgumentError end end end context 'when no host info' do let :client do Rack::OAuth2::Client.new( identifier: 'client_id', secret: 'client_secret', redirect_uri: 'https://client.example.com/callback', revocation_endpoint: '/oauth2/revoke' ) end describe '#authorization_uri' do it do expect { client.authorization_uri }.to raise_error 'No Host Info' end end describe '#access_token!' do it do expect { client.access_token! }.to raise_error 'No Host Info' end end describe '#revoke!' do it do expect { client.revoke! access_token: 'access_token' }.to raise_error 'No Host Info' end end end end rack-oauth2-2.2.1/spec/rack/oauth2/oauth2_spec.rb000066400000000000000000000013451454275276700214770ustar00rootroot00000000000000require 'spec_helper' describe Rack::OAuth2 do subject { Rack::OAuth2 } after { Rack::OAuth2.debugging = false } its(:logger) { should be_a Logger } its(:debugging?) { should == false } describe '.debug!' do before { Rack::OAuth2.debug! } its(:debugging?) { should == true } end describe '.debug' do it 'should enable debugging within given block' do Rack::OAuth2.debug do Rack::OAuth2.debugging?.should == true end Rack::OAuth2.debugging?.should == false end it 'should not force disable debugging' do Rack::OAuth2.debug! Rack::OAuth2.debug do Rack::OAuth2.debugging?.should == true end Rack::OAuth2.debugging?.should == true end end endrack-oauth2-2.2.1/spec/rack/oauth2/server/000077500000000000000000000000001454275276700202415ustar00rootroot00000000000000rack-oauth2-2.2.1/spec/rack/oauth2/server/abstract/000077500000000000000000000000001454275276700220445ustar00rootroot00000000000000rack-oauth2-2.2.1/spec/rack/oauth2/server/abstract/error_spec.rb000066400000000000000000000033061454275276700245360ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Server::Abstract::Error do context 'when full attributes are given' do subject do Rack::OAuth2::Server::Abstract::Error.new 400, :invalid_request, 'Missing some required params', uri: 'http://server.example.com/error' end its(:status) { should == 400 } its(:error) { should == :invalid_request } its(:description) { should == 'Missing some required params' } its(:uri) { should == 'http://server.example.com/error' } its(:protocol_params) do should == { error: :invalid_request, error_description: 'Missing some required params', error_uri: 'http://server.example.com/error' } end end context 'when optional attributes are not given' do subject do Rack::OAuth2::Server::Abstract::Error.new 400, :invalid_request end its(:status) { should == 400 } its(:error) { should == :invalid_request } its(:description) { should be_nil } its(:uri) { should be_nil } its(:protocol_params) do should == { error: :invalid_request, error_description: nil, error_uri: nil } end end end describe Rack::OAuth2::Server::Abstract::BadRequest do its(:status) { should == 400 } end describe Rack::OAuth2::Server::Abstract::Unauthorized do its(:status) { should == 401 } end describe Rack::OAuth2::Server::Abstract::Forbidden do its(:status) { should == 403 } end describe Rack::OAuth2::Server::Abstract::ServerError do its(:status) { should == 500 } end describe Rack::OAuth2::Server::Abstract::TemporarilyUnavailable do its(:status) { should == 503 } end rack-oauth2-2.2.1/spec/rack/oauth2/server/authorize/000077500000000000000000000000001454275276700222535ustar00rootroot00000000000000rack-oauth2-2.2.1/spec/rack/oauth2/server/authorize/code_spec.rb000066400000000000000000000036451454275276700245340ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Server::Authorize::Code do let(:request) { Rack::MockRequest.new app } let(:redirect_uri) { 'http://client.example.com/callback' } let(:authorization_code) { 'authorization_code' } let(:response) { request.get "/?response_type=code&client_id=client&redirect_uri=#{redirect_uri}&state=state" } context 'when approved' do subject { response } let :app do Rack::OAuth2::Server::Authorize.new do |request, response| response.redirect_uri = redirect_uri response.code = authorization_code response.approve! end end its(:status) { should == 302 } its(:location) { should == "#{redirect_uri}?code=#{authorization_code}&state=state" } context 'when redirect_uri already includes query' do let(:redirect_uri) { 'http://client.example.com/callback?k=v' } its(:location) { should == "#{redirect_uri}&code=#{authorization_code}&state=state" } end context 'when redirect_uri is missing' do let(:redirect_uri) { nil } it do expect { response }.to raise_error AttrRequired::AttrMissing end end context 'when code is missing' do let(:authorization_code) { nil } it do expect { response }.to raise_error AttrRequired::AttrMissing end end end context 'when denied' do let :app do Rack::OAuth2::Server::Authorize.new do |request, response| request.verify_redirect_uri! redirect_uri request.access_denied! end end it 'should redirect with error in query' do response.status.should == 302 error_message = { error: :access_denied, error_description: Rack::OAuth2::Server::Authorize::ErrorMethods::DEFAULT_DESCRIPTION[:access_denied] } response.location.should == "#{redirect_uri}?#{error_message.to_query.gsub('+', '%20')}&state=state" end end end rack-oauth2-2.2.1/spec/rack/oauth2/server/authorize/error_spec.rb000066400000000000000000000074451454275276700247550ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Server::Authorize::BadRequest do let(:klass) { Rack::OAuth2::Server::Authorize::BadRequest } let(:error) { klass.new(:invalid_request) } let(:redirect_uri) { 'http://client.example.com/callback' } subject { error } it { should be_a Rack::OAuth2::Server::Abstract::BadRequest } its(:protocol_params) do should == { error: :invalid_request, error_description: nil, error_uri: nil, state: nil } end describe '#finish' do context 'when redirect_uri is given' do before { error.redirect_uri = redirect_uri } context 'when protocol_params_location = :query' do before { error.protocol_params_location = :query } it 'should redirect with error in query' do state, headers, response = error.finish state.should == 302 headers["Location"].should == "#{redirect_uri}?error=invalid_request" end end context 'when protocol_params_location = :fragment' do before { error.protocol_params_location = :fragment } it 'should redirect with error in fragment' do state, headers, response = error.finish state.should == 302 headers["Location"].should == "#{redirect_uri}#error=invalid_request" end end context 'otherwise' do before { error.protocol_params_location = :other } it 'should redirect without error' do state, headers, response = error.finish state.should == 302 headers["Location"].should == redirect_uri end end end context 'otherwise' do it 'should raise itself' do expect { error.finish }.to raise_error(klass) { |e| e.should == error } end end end end describe Rack::OAuth2::Server::Authorize::ErrorMethods do let(:klass) { Rack::OAuth2::Server::Authorize::BadRequest } let(:redirect_uri) { 'http://client.example.com/callback' } let(:default_description) { Rack::OAuth2::Server::Authorize::ErrorMethods::DEFAULT_DESCRIPTION } let(:env) { Rack::MockRequest.env_for("/authorize?client_id=client_id") } let(:request) { Rack::OAuth2::Server::Authorize::Request.new env } let(:request_for_code) { Rack::OAuth2::Server::Authorize::Code::Request.new env } let(:request_for_token) { Rack::OAuth2::Server::Authorize::Token::Request.new env } describe 'bad_request!' do it do expect { request.bad_request! }.to raise_error klass end context 'when response_type = :code' do it 'should set protocol_params_location = :query' do expect { request_for_code.bad_request! }.to raise_error(klass) { |e| e.protocol_params_location.should == :query } end end context 'when response_type = :token' do it 'should set protocol_params_location = :fragment' do expect { request_for_token.bad_request! }.to raise_error(klass) { |e| e.protocol_params_location.should == :fragment } end end end Rack::OAuth2::Server::Authorize::ErrorMethods::DEFAULT_DESCRIPTION.keys.each do |error_code| method = "#{error_code}!" klass = case error_code when :server_error Rack::OAuth2::Server::Authorize::ServerError when :temporarily_unavailable Rack::OAuth2::Server::Authorize::TemporarilyUnavailable else Rack::OAuth2::Server::Authorize::BadRequest end describe method do it "should raise #{klass} with error = :#{error_code}" do klass = expect { request.send method }.to raise_error(klass) { |error| error.error.should == error_code error.description.should == default_description[error_code] } end end end end rack-oauth2-2.2.1/spec/rack/oauth2/server/authorize/extensions/000077500000000000000000000000001454275276700244525ustar00rootroot00000000000000rack-oauth2-2.2.1/spec/rack/oauth2/server/authorize/extensions/code_and_token_spec.rb000066400000000000000000000043331454275276700307500ustar00rootroot00000000000000require 'spec_helper.rb' require 'rack/oauth2/server/authorize/extension/code_and_token' describe Rack::OAuth2::Server::Authorize::Extension::CodeAndToken do let(:request) { Rack::MockRequest.new app } let(:redirect_uri) { 'http://client.example.com/callback' } let(:access_token) { 'access_token' } let(:authorization_code) { 'authorization_code' } let(:response) do request.get("/?response_type=code%20token&client_id=client&redirect_uri=#{redirect_uri}") end context "when approved" do subject { response } let(:bearer_token) { Rack::OAuth2::AccessToken::Bearer.new(access_token: access_token) } let :app do Rack::OAuth2::Server::Authorize.new do |request, response| response.redirect_uri = redirect_uri response.access_token = bearer_token response.code = authorization_code response.approve! end end its(:status) { should == 302 } its(:location) { should include "#{redirect_uri}#" } its(:location) { should include "code=#{authorization_code}"} its(:location) { should include "access_token=#{access_token}"} its(:location) { should include 'token_type=bearer' } context 'when refresh_token is given' do let :bearer_token do Rack::OAuth2::AccessToken::Bearer.new( access_token: access_token, refresh_token: 'refresh' ) end its(:location) { should include "#{redirect_uri}#" } its(:location) { should include "code=#{authorization_code}"} its(:location) { should include "access_token=#{access_token}"} its(:location) { should include 'token_type=bearer' } end end context 'when denied' do let :app do Rack::OAuth2::Server::Authorize.new do |request, response| request.verify_redirect_uri! redirect_uri request.access_denied! end end it 'should redirect with error in fragment' do response.status.should == 302 error_message = { error: :access_denied, error_description: Rack::OAuth2::Server::Authorize::ErrorMethods::DEFAULT_DESCRIPTION[:access_denied] } response.location.should == "#{redirect_uri}##{error_message.to_query.gsub('+', '%20')}" end end end rack-oauth2-2.2.1/spec/rack/oauth2/server/authorize/token_spec.rb000066400000000000000000000046201454275276700247340ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Server::Authorize::Token do let(:request) { Rack::MockRequest.new app } let(:redirect_uri) { 'http://client.example.com/callback' } let(:access_token) { 'access_token' } let(:response) { request.get("/?response_type=token&client_id=client&redirect_uri=#{redirect_uri}&state=state") } context "when approved" do subject { response } let(:bearer_token) { Rack::OAuth2::AccessToken::Bearer.new(access_token: access_token) } let :app do Rack::OAuth2::Server::Authorize.new do |request, response| response.redirect_uri = redirect_uri response.access_token = bearer_token response.approve! end end its(:status) { should == 302 } its(:location) { should == "#{redirect_uri}#access_token=#{access_token}&state=state&token_type=bearer" } context 'when refresh_token is given' do let :bearer_token do Rack::OAuth2::AccessToken::Bearer.new( access_token: access_token, refresh_token: 'refresh' ) end its(:location) { should == "#{redirect_uri}#access_token=#{access_token}&state=state&token_type=bearer" } end context 'when redirect_uri is missing' do let :app do Rack::OAuth2::Server::Authorize.new do |request, response| response.access_token = bearer_token response.approve! end end it do expect { response }.to raise_error AttrRequired::AttrMissing end end context 'when access_token is missing' do let :app do Rack::OAuth2::Server::Authorize.new do |request, response| response.redirect_uri = redirect_uri response.approve! end end it do expect { response }.to raise_error AttrRequired::AttrMissing end end end context 'when denied' do let :app do Rack::OAuth2::Server::Authorize.new do |request, response| request.verify_redirect_uri! redirect_uri request.access_denied! end end it 'should redirect with error in fragment' do response.status.should == 302 error_message = { error: :access_denied, error_description: Rack::OAuth2::Server::Authorize::ErrorMethods::DEFAULT_DESCRIPTION[:access_denied] } response.location.should == "#{redirect_uri}##{error_message.to_query.gsub('+', '%20')}&state=state" end end end rack-oauth2-2.2.1/spec/rack/oauth2/server/authorize_spec.rb000066400000000000000000000151771454275276700236250ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Server::Authorize do let(:app) { Rack::OAuth2::Server::Authorize.new } let(:request) { Rack::MockRequest.new app } let(:redirect_uri) { 'http://client.example.com/callback' } let(:bad_request) { Rack::OAuth2::Server::Authorize::BadRequest } context 'when response_type is missing' do it do expect { request.get "/?client_id=client&redirect_uri=#{redirect_uri}" }.to raise_error bad_request end end context 'when redirect_uri is missing' do it do expect { request.get "/?response_type=code&client_id=client" }.not_to raise_error end end context 'when client_id is missing' do it do expect { request.get "/?response_type=code&redirect_uri=#{redirect_uri}" }.to raise_error bad_request end end context 'when unknown response_type is given' do it do expect { request.get "/?response_type=unknown&client_id=client&redirect_uri=#{redirect_uri}" }.to raise_error bad_request end end context 'when all required parameters are valid' do [:code, :token].each do |request_type| context "when response_type = :#{request_type}" do subject { request.get "/?response_type=#{request_type}&client_id=client&redirect_uri=#{redirect_uri}" } its(:status) { should == 200 } end end end describe Rack::OAuth2::Server::Authorize::Request do let(:env) { Rack::MockRequest.env_for("/authorize?client_id=client&redirect_uri=#{redirect_uri}") } let(:request) { Rack::OAuth2::Server::Authorize::Request.new env } describe '#varified_redirect_uri' do context 'when an Array of pre-registered URIs are given' do context 'when given redirect_uri is valid against one of them' do let :pre_registered do [ redirect_uri, 'http://ja.client.example.com/callback', 'http://en.client.example.com/callback' ] end it 'should be valid' do request.verify_redirect_uri!(pre_registered).should == redirect_uri end end context 'otherwise' do let :pre_registered do [ 'http://ja.client.example.com/callback', 'http://en.client.example.com/callback' ] end it do expect do request.verify_redirect_uri!(pre_registered) end.to raise_error bad_request end end end context 'when exact mathed redirect_uri is given' do let(:pre_registered) { redirect_uri } it 'should be valid' do request.verify_redirect_uri!(pre_registered).should == redirect_uri end end context 'when partially mathed redirect_uri is given' do let(:pre_registered) { 'http://client.example.com' } context 'when partial matching allowed' do it 'should be valid' do request.verify_redirect_uri!(pre_registered, :allow_partial_match).should == redirect_uri end end context 'otherwise' do it do expect do request.verify_redirect_uri!(pre_registered) end.to raise_error bad_request end end end context 'when invalid redirect_uri is given' do let(:pre_registered) { 'http://client2.example.com' } it do expect do request.verify_redirect_uri!(pre_registered) end.to raise_error bad_request end end context 'when redirect_uri is missing' do let(:env) { Rack::MockRequest.env_for("/authorize?client_id=client") } context 'when pre-registered redirect_uri is a String' do let(:pre_registered) { redirect_uri } it 'should use pre-registered redirect_uri' do request.verify_redirect_uri!(pre_registered).should == pre_registered end end context 'when pre-registered redirect_uri is an Array' do context 'when only 1' do let(:pre_registered) { [redirect_uri] } context 'when partial match allowed' do it do expect do request.verify_redirect_uri!(pre_registered, :allow_partial_match) end.to raise_error bad_request end end context 'otherwise' do it 'should use pre-registered redirect_uri' do request.verify_redirect_uri!(pre_registered).should == pre_registered.first end end end context 'when more than 2' do let(:pre_registered) { [redirect_uri, 'http://client.example.com/callback2'] } it do expect do request.verify_redirect_uri!(pre_registered) end.to raise_error bad_request end end end end end end describe 'extensibility' do before do require 'rack/oauth2/server/authorize/extension/code_and_token' end let(:env) do Rack::MockRequest.env_for("/authorize?response_type=#{response_type}&client_id=client") end let(:request) { Rack::OAuth2::Server::Authorize::Request.new env } its(:extensions) { should == [Rack::OAuth2::Server::Authorize::Extension::CodeAndToken] } describe 'code token' do let(:response_type) { 'code%20token' } it do app.send( :response_type_for, request ).should == Rack::OAuth2::Server::Authorize::Extension::CodeAndToken end end describe 'token code' do let(:response_type) { 'token%20code' } it do app.send( :response_type_for, request ).should == Rack::OAuth2::Server::Authorize::Extension::CodeAndToken end end describe 'token code id_token' do let(:response_type) { 'token%20code%20id_token' } it do expect do app.send(:response_type_for, request) end.to raise_error bad_request end end describe 'id_token' do before do class Rack::OAuth2::Server::Authorize::Extension::IdToken < Rack::OAuth2::Server::Abstract::Handler def self.response_type_for?(response_type) response_type == 'id_token' end end end its(:extensions) do should == [ Rack::OAuth2::Server::Authorize::Extension::CodeAndToken, Rack::OAuth2::Server::Authorize::Extension::IdToken ] end let(:response_type) { 'id_token' } it do app.send( :response_type_for, request ).should == Rack::OAuth2::Server::Authorize::Extension::IdToken end end end end rack-oauth2-2.2.1/spec/rack/oauth2/server/extension/000077500000000000000000000000001454275276700222555ustar00rootroot00000000000000rack-oauth2-2.2.1/spec/rack/oauth2/server/extension/pkce_spec.rb000066400000000000000000000122061454275276700245370ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Server::Authorize::Code do let(:request) { Rack::MockRequest.new app } let(:redirect_uri) { 'http://client.example.com/callback' } let(:code_verifier) { SecureRandom.hex(16) } let(:code_challenge) { Base64.urlsafe_encode64(OpenSSL::Digest::SHA256.digest(code_verifier)).delete('=') } let(:code_challenge_method) { :S256 } subject { @request } describe 'authorization request' do let :app do Rack::OAuth2::Server::Authorize.new do |request, response| @request = request end end context 'when code_challenge is given' do context 'when code_challenge_method is given' do before do request.get "/?response_type=code&client_id=client&redirect_uri=#{redirect_uri}&state=state&code_challenge=#{code_challenge}&code_challenge_method=#{code_challenge_method}" end its(:code_challenge) { should == code_challenge } its(:code_challenge_method) { should == code_challenge_method.to_s } end context 'when code_challenge_method is omitted' do before do request.get "/?response_type=code&client_id=client&redirect_uri=#{redirect_uri}&state=state&code_challenge=#{code_challenge}" end its(:code_challenge) { should == code_challenge } its(:code_challenge_method) { should == nil } end end context 'otherwise' do before do request.get "/?response_type=code&client_id=client&redirect_uri=#{redirect_uri}&state=state" end its(:code_challenge) { should == nil } its(:code_challenge_method) { should == nil } end end describe 'token request' do let(:app) do Rack::OAuth2::Server::Token.new do |request, response| @request = request response.access_token = Rack::OAuth2::AccessToken::Bearer.new(access_token: 'access_token') end end let(:default_params) do { grant_type: 'authorization_code', client_id: 'client_id', client_secret: 'client_secret', code: 'authorization_code', redirect_uri: 'http://client.example.com/callback' } end context 'when code_verifier is given' do before do request.post '/', params: default_params.merge( code_verifier: code_verifier ) end its(:code_verifier) { should == code_verifier } describe '#verify_code_verifier!' do context 'when code_verifier is given with code_challenge_method=plain' do it do expect do subject.verify_code_verifier! code_verifier, :plain end.not_to raise_error end end context 'when collect code_challenge is given' do it do expect do subject.verify_code_verifier! code_challenge end.not_to raise_error end end context 'when wrong code_challenge is blank' do it do expect do subject.verify_code_verifier! 'wrong' end.to raise_error Rack::OAuth2::Server::Token::BadRequest, /invalid_grant/ end end context 'when code_challenge is nil' do it do expect do subject.verify_code_verifier! nil end.to raise_error Rack::OAuth2::Server::Token::BadRequest, /invalid_grant/ end end context 'when unknown code_challenge_method is given' do it do expect do subject.verify_code_verifier! code_challenge, :unknown end.to raise_error Rack::OAuth2::Server::Token::BadRequest, /invalid_grant/ end end end end context 'otherwise' do before do request.post '/', params: default_params end its(:code_verifier) { should == nil } describe '#verify_code_verifier!' do context 'when code_verifier is given with code_challenge_method=plain' do it do expect do subject.verify_code_verifier! code_verifier, :plain end.to raise_error Rack::OAuth2::Server::Token::BadRequest, /invalid_grant/ end end context 'when collect code_challenge is given' do it do expect do subject.verify_code_verifier! code_challenge end.to raise_error Rack::OAuth2::Server::Token::BadRequest, /invalid_grant/ end end context 'when wrong code_challenge is blank' do it do expect do subject.verify_code_verifier! 'wrong' end.to raise_error Rack::OAuth2::Server::Token::BadRequest, /invalid_grant/ end end context 'when code_challenge is nil' do it do expect do subject.verify_code_verifier! nil end.not_to raise_error end end context 'when unknown code_challenge_method is given' do it do expect do subject.verify_code_verifier! code_challenge, :unknown end.to raise_error Rack::OAuth2::Server::Token::BadRequest, /invalid_grant/ end end end end end end rack-oauth2-2.2.1/spec/rack/oauth2/server/extension/response_mode_spec.rb000066400000000000000000000016001454275276700264530ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Server::Authorize::Code do let(:request) { Rack::MockRequest.new app } let(:redirect_uri) { 'http://client.example.com/callback' } let(:response_mode) { 'form_post' } subject { @request } describe 'authorization request' do let :app do Rack::OAuth2::Server::Authorize.new do |request, response| @request = request end end context 'when response_mode is given' do before do request.get "/?response_type=code&client_id=client&redirect_uri=#{redirect_uri}&state=state&response_mode=#{response_mode}" end its(:response_mode) { should == response_mode } end context 'otherwise' do before do request.get "/?response_type=code&client_id=client&redirect_uri=#{redirect_uri}&state=state" end its(:response_mode) { should == nil } end end end rack-oauth2-2.2.1/spec/rack/oauth2/server/resource/000077500000000000000000000000001454275276700220705ustar00rootroot00000000000000rack-oauth2-2.2.1/spec/rack/oauth2/server/resource/bearer/000077500000000000000000000000001454275276700233305ustar00rootroot00000000000000rack-oauth2-2.2.1/spec/rack/oauth2/server/resource/bearer/error_spec.rb000066400000000000000000000034061454275276700260230ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Server::Resource::Bearer::Unauthorized do let(:error) { Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new(:invalid_token) } it { should be_a Rack::OAuth2::Server::Resource::Unauthorized } describe '#scheme' do subject { error } its(:scheme) { should == :Bearer } end describe '#finish' do it 'should use Bearer scheme' do status, headers, response = error.finish headers['WWW-Authenticate'].should include 'Bearer' end end end describe Rack::OAuth2::Server::Resource::Bearer::ErrorMethods do let(:unauthorized) { Rack::OAuth2::Server::Resource::Bearer::Unauthorized } let(:redirect_uri) { 'http://client.example.com/callback' } let(:default_description) { Rack::OAuth2::Server::Resource::ErrorMethods::DEFAULT_DESCRIPTION } let(:env) { Rack::MockRequest.env_for("/authorize?client_id=client_id") } let(:request) { Rack::OAuth2::Server::Resource::Bearer::Request.new env } describe 'unauthorized!' do it do expect { request.unauthorized! :invalid_client }.to raise_error unauthorized end end Rack::OAuth2::Server::Resource::Bearer::ErrorMethods::DEFAULT_DESCRIPTION.keys.each do |error_code| method = "#{error_code}!" case error_code when :invalid_request # ignore when :insufficient_scope # ignore else describe method do it "should raise Rack::OAuth2::Server::Resource::Bearer::Unauthorized with error = :#{error_code}" do expect { request.send method }.to raise_error(unauthorized) { |error| error.error.should == error_code error.description.should == default_description[error_code] } end end end end endrack-oauth2-2.2.1/spec/rack/oauth2/server/resource/bearer_spec.rb000066400000000000000000000076471454275276700247050ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Server::Resource::Bearer do let(:app) do Rack::OAuth2::Server::Resource::Bearer.new(simple_app) do |request| case request.access_token when 'valid_token' bearer_token when 'insufficient_scope_token' request.insufficient_scope! else request.invalid_token! end end end let(:bearer_token) do Rack::OAuth2::AccessToken::Bearer.new(access_token: 'valid_token') end let(:access_token) { env[Rack::OAuth2::Server::Resource::ACCESS_TOKEN] } let(:request) { app.call(env) } subject { app.call(env) } shared_examples_for :authenticated_bearer_request do it 'should be authenticated' do status, headers, response = request status.should == 200 access_token.should == bearer_token end end shared_examples_for :unauthorized_bearer_request do it 'should be unauthorized' do status, headers, response = request status.should == 401 headers['WWW-Authenticate'].should include 'Bearer' access_token.should be_nil end end shared_examples_for :bad_bearer_request do it 'should be bad_request' do status, headers, response = request status.should == 400 access_token.should be_nil end end shared_examples_for :skipped_authentication_request do it 'should skip OAuth 2.0 authentication' do status, headers, response = request status.should == 200 access_token.should be_nil end end context 'when no access token is given' do let(:env) { Rack::MockRequest.env_for('/protected_resource') } it_behaves_like :skipped_authentication_request end context 'when valid_token is given' do context 'when token is in Authorization header' do let(:env) { Rack::MockRequest.env_for('/protected_resource', 'HTTP_AUTHORIZATION' => 'Bearer valid_token') } it_behaves_like :authenticated_bearer_request end context 'when token is in params' do let(:env) { Rack::MockRequest.env_for('/protected_resource', params: {access_token: 'valid_token'}) } it_behaves_like :authenticated_bearer_request end end context 'when invalid authorization header is given' do let(:env) { Rack::MockRequest.env_for('/protected_resource', 'HTTP_AUTHORIZATION' => '') } it_behaves_like :skipped_authentication_request end context 'when invalid_token is given' do let(:env) { Rack::MockRequest.env_for('/protected_resource', 'HTTP_AUTHORIZATION' => 'Bearer invalid_token') } context 'when token is in Authorization header' do it_behaves_like :unauthorized_bearer_request end context 'when token is in params' do let(:env) { Rack::MockRequest.env_for('/protected_resource', params: {access_token: 'invalid_token'}) } it_behaves_like :unauthorized_bearer_request end describe 'realm' do context 'when specified' do let(:realm) { 'server.example.com' } let(:app) do Rack::OAuth2::Server::Resource::Bearer.new(simple_app, realm) do |request| request.unauthorized! end end it 'should use specified realm' do status, headers, response = request headers['WWW-Authenticate'].should include "Bearer realm=\"#{realm}\"" end end context 'otherwize' do it 'should use default realm' do status, headers, response = request headers['WWW-Authenticate'].should include "Bearer realm=\"#{Rack::OAuth2::Server::Resource::Bearer::DEFAULT_REALM}\"" end end end end context 'when multiple access_token is given' do context 'when token is in Authorization header and params' do let(:env) do Rack::MockRequest.env_for( '/protected_resource', 'HTTP_AUTHORIZATION' => 'Bearer valid_token', params: {access_token: 'valid_token'} ) end it_behaves_like :bad_bearer_request end end end rack-oauth2-2.2.1/spec/rack/oauth2/server/resource/error_spec.rb000066400000000000000000000127061454275276700245660ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Server::Resource::BadRequest do let(:error) { Rack::OAuth2::Server::Resource::BadRequest.new(:invalid_request) } it { should be_a Rack::OAuth2::Server::Abstract::BadRequest } describe '#finish' do it 'should respond in JSON' do status, headers, response = error.finish status.should == 400 headers['Content-Type'].should == 'application/json' response.should == ['{"error":"invalid_request"}'] end end end describe Rack::OAuth2::Server::Resource::Unauthorized do let(:error) { Rack::OAuth2::Server::Resource::Unauthorized.new(:invalid_token) } let(:realm) { Rack::OAuth2::Server::Resource::DEFAULT_REALM } it { should be_a Rack::OAuth2::Server::Abstract::Unauthorized } describe '#scheme' do it do expect { error.scheme }.to raise_error(RuntimeError, 'Define me!') end end context 'when scheme is defined' do let :error_with_scheme do e = error e.instance_eval do def scheme :Scheme end end e end describe '#finish' do it 'should respond in JSON' do status, headers, response = error_with_scheme.finish status.should == 401 headers['Content-Type'].should == 'application/json' headers['WWW-Authenticate'].should == "Scheme realm=\"#{realm}\", error=\"invalid_token\"" response.should == ['{"error":"invalid_token"}'] end context 'when error_code is not invalid_token' do let(:error) { Rack::OAuth2::Server::Resource::Unauthorized.new(:something) } it 'should have error_code in body but not in WWW-Authenticate header' do status, headers, response = error_with_scheme.finish headers['WWW-Authenticate'].should == "Scheme realm=\"#{realm}\"" response.first.should include '"error":"something"' end end context 'when no error_code is given' do let(:error) { Rack::OAuth2::Server::Resource::Unauthorized.new } it 'should have error_code in body but not in WWW-Authenticate header' do status, headers, response = error_with_scheme.finish headers['WWW-Authenticate'].should == "Scheme realm=\"#{realm}\"" response.first.should == '{"error":"unauthorized"}' end end context 'when realm is specified' do let(:realm) { 'server.example.com' } let(:error) { Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new(:something, nil, realm: realm) } it 'should use given realm' do status, headers, response = error_with_scheme.finish headers['WWW-Authenticate'].should == "Scheme realm=\"#{realm}\"" response.first.should include '"error":"something"' end end end end end describe Rack::OAuth2::Server::Resource::Forbidden do let(:error) { Rack::OAuth2::Server::Resource::Forbidden.new(:insufficient_scope) } it { should be_a Rack::OAuth2::Server::Abstract::Forbidden } describe '#finish' do it 'should respond in JSON' do status, headers, response = error.finish status.should == 403 headers['Content-Type'].should == 'application/json' response.should == ['{"error":"insufficient_scope"}'] end end context 'when scope option is given' do let(:error) { Rack::OAuth2::Server::Resource::Bearer::Forbidden.new(:insufficient_scope, 'Desc', scope: [:scope1, :scope2]) } it 'should have blank WWW-Authenticate header' do status, headers, response = error.finish response.first.should include '"scope":"scope1 scope2"' end end end describe Rack::OAuth2::Server::Resource::Bearer::ErrorMethods do let(:bad_request) { Rack::OAuth2::Server::Resource::BadRequest } let(:forbidden) { Rack::OAuth2::Server::Resource::Forbidden } let(:redirect_uri) { 'http://client.example.com/callback' } let(:default_description) { Rack::OAuth2::Server::Resource::ErrorMethods::DEFAULT_DESCRIPTION } let(:env) { Rack::MockRequest.env_for("/authorize?client_id=client_id") } let(:request) { Rack::OAuth2::Server::Resource::Request.new env } describe 'bad_request!' do it do expect { request.bad_request! :invalid_request }.to raise_error bad_request end end describe 'unauthorized!' do it do expect { request.unauthorized! :invalid_client }.to raise_error(RuntimeError, 'Define me!') end end Rack::OAuth2::Server::Resource::ErrorMethods::DEFAULT_DESCRIPTION.keys.each do |error_code| method = "#{error_code}!" case error_code when :invalid_request describe method do it "should raise Rack::OAuth2::Server::Resource::BadRequest with error = :#{error_code}" do expect { request.send method }.to raise_error(bad_request) { |error| error.error.should == error_code error.description.should == default_description[error_code] } end end when :insufficient_scope describe method do it "should raise Rack::OAuth2::Server::Resource::Forbidden with error = :#{error_code}" do expect { request.send method }.to raise_error(forbidden) { |error| error.error.should == error_code error.description.should == default_description[error_code] } end end else describe method do it do expect { request.send method }.to raise_error(RuntimeError, 'Define me!') end end end end end rack-oauth2-2.2.1/spec/rack/oauth2/server/resource_spec.rb000066400000000000000000000011531454275276700234270ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Server::Resource do subject { Rack::OAuth2::Server::Resource.new(simple_app, 'realm') } its(:realm) { should == 'realm' } end describe Rack::OAuth2::Server::Resource::Request do let(:env) { Rack::MockRequest.env_for('/protected_resource') } let(:request) { Rack::OAuth2::Server::Resource::Request.new(env) } describe '#setup!' do it do expect { request.setup! }.to raise_error(RuntimeError, 'Define me!') end end describe '#oauth2?' do it do expect { request.oauth2? }.to raise_error(RuntimeError, 'Define me!') end end endrack-oauth2-2.2.1/spec/rack/oauth2/server/token/000077500000000000000000000000001454275276700213615ustar00rootroot00000000000000rack-oauth2-2.2.1/spec/rack/oauth2/server/token/authorization_code_spec.rb000066400000000000000000000025051454275276700266140ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Server::Token::AuthorizationCode do let(:request) { Rack::MockRequest.new app } let(:app) do Rack::OAuth2::Server::Token.new do |request, response| response.access_token = Rack::OAuth2::AccessToken::Bearer.new(access_token: 'access_token') end end let(:params) do { grant_type: 'authorization_code', client_id: 'client_id', code: 'authorization_code', redirect_uri: 'http://client.example.com/callback' } end let(:response) { request.post('/', params: params) } subject { response } its(:status) { should == 200 } its(:content_type) { should == 'application/json' } its(:body) { should include '"access_token":"access_token"' } its(:body) { should include '"token_type":"bearer"' } it 'should prevent to be cached' do response.headers['Cache-Control'].should == 'no-store' response.headers['Pragma'].should == 'no-cache' end [:code].each do |required| context "when #{required} is missing" do before do params.delete_if do |key, value| key == required end end its(:status) { should == 400 } its(:content_type) { should == 'application/json' } its(:body) { should include '"error":"invalid_request"' } end end end rack-oauth2-2.2.1/spec/rack/oauth2/server/token/client_credentials_spec.rb000066400000000000000000000031321454275276700265520ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Server::Token::ClientCredentials do let(:request) { Rack::MockRequest.new app } let(:app) do Rack::OAuth2::Server::Token.new do |request, response| unless request.client_id == client_id && request.client_secret == client_secret request.invalid_client! end response.access_token = Rack::OAuth2::AccessToken::Bearer.new(access_token: 'access_token') end end let(:client_id) { 'client_id '} let(:client_secret) { 'client_secret' } let(:params) do { grant_type: 'client_credentials', client_id: client_id, client_secret: client_secret } end subject { request.post('/', params: params) } its(:status) { should == 200 } its(:content_type) { should == 'application/json' } its(:body) { should include '"access_token":"access_token"' } its(:body) { should include '"token_type":"bearer"' } context 'basic auth' do let(:params) do { grant_type: 'client_credentials' } end let(:encoded_creds) do Base64.strict_encode64([ Rack::OAuth2::Util.www_form_url_encode(client_id), Rack::OAuth2::Util.www_form_url_encode(client_secret) ].join(':')) end subject do request.post('/', {params: params, 'HTTP_AUTHORIZATION' => "Basic #{encoded_creds}"}) end its(:status) { should == 200 } context 'compliance with RFC6749 sec 2.3.1' do let(:client_id) { 'client: yes/please!' } let(:client_secret) { 'terrible:secret:of:space' } its(:status) { should == 200 } end end end rack-oauth2-2.2.1/spec/rack/oauth2/server/token/error_spec.rb000066400000000000000000000053161454275276700240560ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Server::Token::BadRequest do let(:error) { Rack::OAuth2::Server::Token::BadRequest.new(:invalid_request) } it { should be_a Rack::OAuth2::Server::Abstract::BadRequest } describe '#finish' do it 'should respond in JSON' do status, headers, response = error.finish status.should == 400 headers['Content-Type'].should == 'application/json' response.should == ['{"error":"invalid_request"}'] end end end describe Rack::OAuth2::Server::Token::Unauthorized do let(:error) { Rack::OAuth2::Server::Token::Unauthorized.new(:invalid_request) } it { should be_a Rack::OAuth2::Server::Abstract::Unauthorized } describe '#finish' do it 'should respond in JSON' do status, headers, response = error.finish status.should == 401 headers['Content-Type'].should == 'application/json' headers['WWW-Authenticate'].should == 'Basic realm="OAuth2 Token Endpoint"' response.should == ['{"error":"invalid_request"}'] end end end describe Rack::OAuth2::Server::Token::ErrorMethods do let(:bad_request) { Rack::OAuth2::Server::Token::BadRequest } let(:unauthorized) { Rack::OAuth2::Server::Token::Unauthorized } let(:redirect_uri) { 'http://client.example.com/callback' } let(:default_description) { Rack::OAuth2::Server::Token::ErrorMethods::DEFAULT_DESCRIPTION } let(:env) { Rack::MockRequest.env_for("/authorize?client_id=client_id") } let(:request) { Rack::OAuth2::Server::Token::Request.new env } describe 'bad_request!' do it do expect { request.bad_request! :invalid_request }.to raise_error bad_request end end describe 'unauthorized!' do it do expect { request.unauthorized! :invalid_client }.to raise_error unauthorized end end Rack::OAuth2::Server::Token::ErrorMethods::DEFAULT_DESCRIPTION.keys.each do |error_code| method = "#{error_code}!" case error_code when :invalid_client describe method do it "should raise Rack::OAuth2::Server::Token::Unauthorized with error = :#{error_code}" do expect { request.send method }.to raise_error(unauthorized) { |error| error.error.should == error_code error.description.should == default_description[error_code] } end end else describe method do it "should raise Rack::OAuth2::Server::Token::BadRequest with error = :#{error_code}" do expect { request.send method }.to raise_error(bad_request) { |error| error.error.should == error_code error.description.should == default_description[error_code] } end end end end end rack-oauth2-2.2.1/spec/rack/oauth2/server/token/jwt_bearer_spec.rb000066400000000000000000000020661454275276700250500ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Server::Token::JWTBearer do let(:request) { Rack::MockRequest.new app } let(:app) do Rack::OAuth2::Server::Token.new do |request, response| response.access_token = Rack::OAuth2::AccessToken::Bearer.new(access_token: 'access_token') end end let(:params) do { grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', client_id: 'client_id', assertion: 'header.payload.signature' } end subject { request.post('/', params: params) } its(:status) { should == 200 } its(:content_type) { should == 'application/json' } its(:body) { should include '"access_token":"access_token"' } its(:body) { should include '"token_type":"bearer"' } context 'when assertion is missing' do before do params.delete_if do |key, value| key == :assertion end end its(:status) { should == 400 } its(:content_type) { should == 'application/json' } its(:body) { should include '"error":"invalid_request"' } end end rack-oauth2-2.2.1/spec/rack/oauth2/server/token/password_spec.rb000066400000000000000000000021341454275276700245620ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Server::Token::Password do let(:request) { Rack::MockRequest.new app } let(:app) do Rack::OAuth2::Server::Token.new do |request, response| response.access_token = Rack::OAuth2::AccessToken::Bearer.new(access_token: 'access_token') end end let(:params) do { grant_type: 'password', client_id: 'client_id', username: 'nov', password: 'secret' } end subject { request.post('/', params: params) } its(:status) { should == 200 } its(:content_type) { should == 'application/json' } its(:body) { should include '"access_token":"access_token"' } its(:body) { should include '"token_type":"bearer"' } [:username, :password].each do |required| context "when #{required} is missing" do before do params.delete_if do |key, value| key == required end end its(:status) { should == 400 } its(:content_type) { should == 'application/json' } its(:body) { should include '"error":"invalid_request"' } end end end rack-oauth2-2.2.1/spec/rack/oauth2/server/token/refresh_token_spec.rb000066400000000000000000000020341454275276700255550ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Server::Token::RefreshToken do let(:request) { Rack::MockRequest.new app } let(:app) do Rack::OAuth2::Server::Token.new do |request, response| response.access_token = Rack::OAuth2::AccessToken::Bearer.new(access_token: 'access_token') end end let(:params) do { grant_type: "refresh_token", client_id: "client_id", refresh_token: "refresh_token" } end subject { request.post('/', params: params) } its(:status) { should == 200 } its(:content_type) { should == 'application/json' } its(:body) { should include '"access_token":"access_token"' } its(:body) { should include '"token_type":"bearer"' } context 'when refresh_token is missing' do before do params.delete_if do |key, value| key == :refresh_token end end its(:status) { should == 400 } its(:content_type) { should == 'application/json' } its(:body) { should include '"error":"invalid_request"' } end end rack-oauth2-2.2.1/spec/rack/oauth2/server/token/saml2_bearer_spec.rb000066400000000000000000000020601454275276700252540ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Server::Token::SAML2Bearer do let(:request) { Rack::MockRequest.new app } let(:app) do Rack::OAuth2::Server::Token.new do |request, response| response.access_token = Rack::OAuth2::AccessToken::Bearer.new(access_token: 'access_token') end end let(:params) do { grant_type: 'urn:ietf:params:oauth:grant-type:saml2-bearer', client_id: 'client_id', assertion: '...' } end subject { request.post('/', params: params) } its(:status) { should == 200 } its(:content_type) { should == 'application/json' } its(:body) { should include '"access_token":"access_token"' } its(:body) { should include '"token_type":"bearer"' } context 'when assertion is missing' do before do params.delete_if do |key, value| key == :assertion end end its(:status) { should == 400 } its(:content_type) { should == 'application/json' } its(:body) { should include '"error":"invalid_request"' } end end rack-oauth2-2.2.1/spec/rack/oauth2/server/token_spec.rb000066400000000000000000000141701454275276700227230ustar00rootroot00000000000000require 'spec_helper.rb' require 'base64' describe Rack::OAuth2::Server::Token do let(:request) { Rack::MockRequest.new app } let(:app) do Rack::OAuth2::Server::Token.new do |request, response| response.access_token = Rack::OAuth2::AccessToken::Bearer.new(access_token: 'access_token') end end let(:params) do { grant_type: 'authorization_code', client_id: 'client_id', code: 'authorization_code', redirect_uri: 'http://client.example.com/callback' } end subject { request.post('/token', params: params) } context 'when multiple client credentials are given' do context 'when different credentials are given' do let(:env) do Rack::MockRequest.env_for( '/token', 'HTTP_AUTHORIZATION' => "Basic #{Base64.encode64('client_id2:client_secret')}", params: params ) end it 'should fail with unsupported_grant_type' do status, headers, response = app.call(env) status.should == 400 response.first.should include '"error":"invalid_request"' end end context 'when same credentials are given' do let(:env) do Rack::MockRequest.env_for( '/token', 'HTTP_AUTHORIZATION' => "Basic #{Base64.encode64('client_id:client_secret')}", params: params ) end it 'should ignore duplicates' do status, headers, response = app.call(env) status.should == 200 end end end context 'when unsupported grant_type is given' do before do params.merge!(grant_type: 'unknown') end its(:status) { should == 400 } its(:content_type) { should == 'application/json' } its(:body) { should include '"error":"unsupported_grant_type"' } end [:client_id, :grant_type].each do |required| context "when #{required} is missing" do before do params.delete_if do |key, value| key == required end end its(:status) { should == 400 } its(:content_type) { should == 'application/json' } its(:body) { should include '"error":"invalid_request"' } end end context 'when client_id is given via JWT client assertion' do before do require 'json/jwt' params[:client_assertion] = JSON::JWT.new( sub: params[:client_id] # NOTE: actual client_assertion should have more claims. ).sign('client_secret').to_s params[:client_assertion_type] = Rack::OAuth2::URN::ClientAssertionType::JWT_BEARER params.delete(:client_id) end context 'when client_assertion is invalid JWT' do before do params[:client_assertion] = 'invalid-jwt' end its(:status) { should == 400 } its(:content_type) { should == 'application/json' } its(:body) { should include '"error":"invalid_request"' } end context 'when client_assertion_type is missing' do before do params.delete(:client_assertion_type) end its(:status) { should == 400 } its(:content_type) { should == 'application/json' } its(:body) { should include '"error":"invalid_request"' } end context 'when client_assertion_type is unknown' do before do params[:client_assertion_type] = 'unknown' end its(:status) { should == 400 } its(:content_type) { should == 'application/json' } its(:body) { should include '"error":"invalid_request"' } end context 'when client_assertion issuer is different from client_id' do before do params[:client_id] = 'another_client_id' end its(:status) { should == 400 } its(:content_type) { should == 'application/json' } its(:body) { should include '"error":"invalid_request"' } end context 'otherwise' do its(:status) { should == 200 } its(:content_type) { should == 'application/json' } its(:body) { should include '"access_token":"access_token"' } end end Rack::OAuth2::Server::Token::ErrorMethods::DEFAULT_DESCRIPTION.each do |error, default_message| status = if error == :invalid_client 401 else 400 end context "when #{error}" do let(:app) do Rack::OAuth2::Server::Token.new do |request, response| request.send "#{error}!" end end its(:status) { should == status } its(:content_type) { should == 'application/json' } its(:body) { should include "\"error\":\"#{error}\"" } its(:body) { should include "\"error_description\":\"#{default_message}\"" } if error == :invalid_client its(:headers) { should include 'WWW-Authenticate' } end end end context 'when skip_www_authenticate option is specified on invalid_client' do let(:app) do Rack::OAuth2::Server::Token.new do |request, response| request.invalid_client!( Rack::OAuth2::Server::Token::ErrorMethods::DEFAULT_DESCRIPTION[:invalid_client], skip_www_authenticate: true ) end end its(:headers) { should_not include 'WWW-Authenticate' } end context 'when responding' do context 'when access_token is missing' do let(:app) do Rack::OAuth2::Server::Token.new end it do expect { request.post('/', params: params) }.to raise_error AttrRequired::AttrMissing end end end describe 'extensibility' do before do require 'rack/oauth2/server/token/extension/example' end subject { app } let(:env) do Rack::MockRequest.env_for( '/token', params: params ) end let(:request) { Rack::OAuth2::Server::Token::Request.new env } its(:extensions) { should == [Rack::OAuth2::Server::Token::Extension::Example] } describe 'JWT assertion' do let(:params) do { grant_type: 'urn:ietf:params:oauth:grant-type:example', assertion: 'header.payload.signature' } end it do app.send( :grant_type_for, request ).should == Rack::OAuth2::Server::Token::Extension::Example end end end end rack-oauth2-2.2.1/spec/rack/oauth2/util_spec.rb000066400000000000000000000050621454275276700212520ustar00rootroot00000000000000require 'spec_helper.rb' describe Rack::OAuth2::Util do let :util do Rack::OAuth2::Util end let :uri do 'http://client.example.com/callback' end describe '.www_form_url_encode' do subject { util.www_form_url_encode '=+ .-/' } it { should == '%3D%2B+.-%2F' } end describe '.www_form_urldecode' do subject { util.www_form_url_decode '%3D%2B+.-%2F' } it { should == '=+ .-/' } end describe '.base64_encode' do subject { util.base64_encode '=+ .-/' } it { should == 'PSsgLi0v' } end describe '.compact_hash' do subject { util.compact_hash k1: 'v1', k2: '', k3: nil } it { should == {k1: 'v1'} } end describe '.parse_uri' do context 'when String is given' do it { util.parse_uri(uri).should be_a URI::Generic } end context 'when URI is given' do it 'should be itself' do _uri_ = URI.parse uri util.parse_uri(_uri_).should be _uri_ end end context 'when invalid URI is given' do it do expect do util.parse_uri '::' end.to raise_error URI::InvalidURIError end end context 'otherwise' do it do expect { util.parse_uri nil }.to raise_error StandardError expect { util.parse_uri 123 }.to raise_error StandardError end end end describe '.redirect_uri' do let(:base_uri) { 'http://client.example.com' } let(:params) do {k1: :v1, k2: ''} end subject { util.redirect_uri base_uri, location, params } context 'when location = :fragment' do let(:location) { :fragment } it { should == "#{base_uri}##{util.compact_hash(params).to_query}" } end context 'when location = :query' do let(:location) { :query } it { should == "#{base_uri}?#{util.compact_hash(params).to_query}" } end end describe '.uri_match?' do context 'when invalid URI is given' do it do util.uri_match?('::', '::').should == false util.uri_match?(123, 'http://client.example.com/other').should == false util.uri_match?('http://client.example.com/other', nil).should == false end end context 'when exactly same' do it { util.uri_match?(uri, uri).should == true } end context 'when path prefix matches' do it { util.uri_match?(uri, "#{uri}/deep_path").should == true } end context 'otherwise' do it do util.uri_match?(uri, 'http://client.example.com/other').should == false util.uri_match?(uri, 'http://attacker.example.com/callback').should == false end end end end rack-oauth2-2.2.1/spec/spec_helper.rb000066400000000000000000000006051454275276700174300ustar00rootroot00000000000000require 'simplecov' SimpleCov.start do add_filter 'spec' end require 'rspec' require 'rspec/its' require 'rack/oauth2' RSpec.configure do |config| config.expect_with :rspec do |c| c.syntax = [:should, :expect] end end require 'helpers/time' require 'helpers/webmock_helper' def simple_app lambda do |env| [ 200, {'Content-Type' => 'text/plain'}, ["HELLO"] ] end end