require 'uri' module Warden module GitHub # This class encapsulates the configuration of the strategy. A strategy can # be configured through Warden::Manager by defining a scope's default. Thus, # it is possible to use the same strategy with different configurations by # using multiple scopes. # # To configure a scope, use #scope_defaults inside the Warden::Manager # config block. The first arg is the name of the scope (the default is # :default, so use that to configure the default scope), the second arg is # an options hash which should contain: # # - :strategies : An array of strategies to use for this scope. Since this # strategy is called :github, include it in the array. # # - :config : A hash containing the configs that are used for OAuth. # Valid parameters include :client_id, :client_secret, # :scope, :redirect_uri. Please refer to the OAuth # documentation of the GitHub API for the meaning of these # parameters. # # If :client_id or :client_secret are not specified, they # will be fetched from ENV['GITHUB_CLIENT_ID'] and # ENV['GITHUB_CLIENT_SECRET'], respectively. # # :scope defaults to nil. # # If no :redirect_uri is specified, the current path will # be used. If a path is specified it will be appended to # the request host, forming a valid URL. # # Examples # # use Warden::Manager do |config| # config.failure_app = BadAuthentication # # # The following line doesn't specify any custom configurations, thus # # the default scope will be using the implict client_id, # # client_secret, and redirect_uri. # config.default_strategies :github # # # This configures an additional scope that uses the github strategy # # with custom configuration. # config.scope_defaults :admin, :config => { :client_id => 'foobar', # :client_secret => 'barfoo', # :scope => 'user,repo', # :redirect_uri => '/admin/oauth/callback' } # end class Config BadConfig = Class.new(StandardError) include ::Warden::Mixins::Common attr_reader :env, :warden_scope def initialize(env, warden_scope) @env = env @warden_scope = warden_scope end def client_id custom_config[:client_id] || deprecated_config(:github_client_id) || ENV['GITHUB_CLIENT_ID'] || fail(BadConfig, 'Missing client_id configuration.') end def client_secret custom_config[:client_secret] || deprecated_config(:github_secret) || ENV['GITHUB_CLIENT_SECRET'] || fail(BadConfig, 'Missing client_secret configuration.') end def redirect_uri uri_or_path = custom_config[:redirect_uri] || deprecated_config(:github_callback_url) || request.path normalized_uri(uri_or_path).to_s end def scope custom_config[:scope] || deprecated_config(:github_scopes) end def to_hash { :client_id => client_id, :client_secret => client_secret, :redirect_uri => redirect_uri, :scope => scope } end private def custom_config @custom_config ||= env['warden']. config[:scope_defaults]. fetch(warden_scope, {}). fetch(:config, {}) end def deprecated_config(name) env['warden'].config[name].tap do |config| unless config.nil? warn "[warden-github] Deprecated configuration #{name} used. Please refer to the README for updated configuration instructions." end end end def normalized_uri(uri_or_path) uri = URI(request.url) uri.path = extract_path(URI(uri_or_path)) uri.query = nil uri.fragment = nil correct_scheme(uri) end def extract_path(uri) path = uri.path if path.start_with?('/') path else "/#{path}" end end def https_forwarded_proto? env['HTTP_X_FORWARDED_PROTO'] && env['HTTP_X_FORWARDED_PROTO'].split(',')[0] == "https" end def correct_scheme(uri) if uri.scheme != 'https' && https_forwarded_proto? uri.scheme = 'https' # Reparsing will use a different URI subclass, namely URI::HTTPS which # knows the default port for https and strips it if present. uri = URI(uri.to_s) end uri.port = nil if uri.port == 80 URI(uri.to_s) end end end end