lib/flexmls_api/authentication/oauth2.rb in flexmls_api-0.4.5 vs lib/flexmls_api/authentication/oauth2.rb in flexmls_api-0.6.4
- old
+ new
@@ -1,10 +1,20 @@
require 'uri'
+
module FlexmlsApi
module Authentication
+
+ module OAuth2Impl
+ require 'flexmls_api/authentication/oauth2_impl/middleware'
+ require 'flexmls_api/authentication/oauth2_impl/grant_type_base'
+ require 'flexmls_api/authentication/oauth2_impl/grant_type_refresh'
+ require 'flexmls_api/authentication/oauth2_impl/grant_type_code'
+ require 'flexmls_api/authentication/oauth2_impl/grant_type_password'
+ end
+
#=OAuth2 Authentication
# Auth implementation to the API using the OAuth2 service endpoint. Current adheres to the 10
# draft of the OAuth2 specification. With OAuth2, the application supplies credentials for the
# application, and a separate a user authentication flow dictactes the active user for
# requests.
@@ -17,64 +27,35 @@
#==OAuth2
# Implementation the BaseAuth interface for API style authentication
class OAuth2 < BaseAuth
- def session
- @provider.load_session()
- end
- def session=(s)
- @provider.save_session(s)
- end
-
def initialize(client)
@client = client
@provider = client.oauth2_provider
end
- # Generate the appropriate request uri for authorizing this application for current user.
- def authorization_url
- params = {
- "client_id" => @provider.client_id,
- "response_type" => "code",
- "redirect_uri" => @provider.redirect_uri
- }
- "#{@provider.authorization_uri}?#{build_url_parameters(params)}"
+ def session
+ @provider.load_session()
end
-
- def token_params
- params = {
- "client_id" => @provider.client_id,
- "client_secret" => @provider.client_secret,
- "grant_type" => "authorization_code",
- "code" => @provider.code,
- "redirect_uri" => @provider.redirect_uri
- }
- "?#{build_url_parameters(params)}"
+ def session=(s)
+ @provider.save_session(s)
end
def authenticate
- if(@provider.code.nil?)
- FlexmlsApi.logger.debug("Redirecting to provider to get the authorization code")
- @provider.redirect(authorization_url)
- end
- FlexmlsApi.logger.debug("Authenticating to #{@provider.access_uri}")
- uri = URI.parse(@provider.access_uri)
- request_path = "#{uri.path}#{token_params}"
- response = oauth_access_connection("#{uri.scheme}://#{uri.host}").post(request_path, "").body
- response.expires_in = @provider.session_timeout if response.expires_in.nil?
- self.session=response
- response
+ granter = OAuth2Impl::GrantTypeBase.create(@client, @provider, session)
+ self.session = granter.authenticate
+ session
end
# Perform an HTTP request (no data)
- def request(method, path, body, options)
+ def request(method, path, body, options={})
+ escaped_path = URI.escape(path)
connection = @client.connection(true) # SSL Only!
connection.headers.merge!(self.auth_header)
- request_opts = {}
- request_opts.merge!(options)
- request_path = "#{path}?#{build_url_parameters({:access_token => session.access_token}.merge(options))}"
+ parameter_string = options.size > 0 ? "?#{build_url_parameters(options)}" : ""
+ request_path = "#{escaped_path}#{parameter_string}"
FlexmlsApi.logger.debug("Request: #{request_path}")
if body.nil?
response = connection.send(method, request_path)
else
FlexmlsApi.logger.debug("Data: #{body}")
@@ -84,47 +65,50 @@
end
def logout
@provider.save_session(nil)
end
+
+ def authorization_url()
+ params = {
+ "client_id" => @provider.client_id,
+ "response_type" => "code",
+ "redirect_uri" => @provider.redirect_uri
+ }
+ "#{@provider.authorization_uri}?#{build_url_parameters(params)}"
+ end
protected
def auth_header
{"Authorization"=> "OAuth #{session.access_token}"}
end
- # Setup a faraday connection for dealing with an OAuth2 endpoint
- def oauth_access_connection(endpoint)
- opts = {
- :headers => @client.headers
- }
- opts[:ssl] = {:verify => false }
- opts[:url] = endpoint
- conn = Faraday::Connection.new(opts) do |builder|
- builder.adapter Faraday.default_adapter
- builder.use Faraday::Response::ParseJson
- builder.use FlexmlsApi::Authentication::FlexmlsOAuth2Middleware
- end
+ def provider
+ @provider
end
+ def client
+ @client
+ end
+
end
# Representation of a session with the api using oauth2
class OAuthSession
- attr_accessor :access_token, :expires_in, :scope, :refresh_token
+ attr_accessor :access_token, :expires_in, :scope, :refresh_token, :refresh_timeout
def initialize(options={})
@access_token = options["access_token"]
- # TODO The current oauth2 service does not send an expiration time. I'm setting it to default to 1 hour.
@expires_in = options["expires_in"]
@scope = options["scope"]
@refresh_token = options["refresh_token"]
@start_time = DateTime.now
+ @refresh_timeout = 3600
end
# Is the user session token expired?
def expired?
- @start_time + Rational(@expires_in, 24*60*60) < DateTime.now
+ @start_time + Rational(@expires_in - @refresh_timeout, 86400) < DateTime.now
end
end
#=OAuth2 configuration provider for applications
# Applications planning to use OAuth2 authentication with the API must extend this class as
@@ -135,11 +119,20 @@
# @redirect_uri - Application uri to redirect to
# @client_id - OAuth2 provided application identifier
# @client_secret - OAuth2 provided password for the client id
class BaseOAuth2Provider
- attr_accessor :authorization_uri, :code, :access_uri, :redirect_uri, :client_id, :client_secret
+ attr_accessor :authorization_uri, :access_uri, :grant_type, :client_id, :client_secret
+
+ # Requirements for authorization_code grant type
+ attr_accessor :code, :redirect_uri
+ # Requirements for password grant type
+ attr_accessor :username, :password
+
+ def grant_type
+ :authorization_code
+ end
# Application using the client must handle user redirect for user authentication. For
# command line applications, this method is called prior to initial client requests so that
# the process can notify the user to go to the url and retrieve the access_code for the app.
# In a web based web application, this method can be mostly ignored. However, the web based
@@ -166,54 +159,13 @@
end
# Provides a default session time out
# returns - the session timeout length (in seconds)
def session_timeout
- 3600
+ 86400 # 1.day
end
-
- end
- #==OAuth2 Faraday response middleware
- # HTTP Response after filter to package oauth2 responses and bubble up basic api errors.
- class FlexmlsOAuth2Middleware < Faraday::Response::Middleware
- begin
- def self.register_on_complete(env)
- env[:response].on_complete do |finished_env|
- validate_and_build_response(finished_env)
- end
- end
- rescue LoadError, NameError => e
- self.load_error = e
- end
-
- def self.validate_and_build_response(finished_env)
- body = finished_env[:body]
- FlexmlsApi.logger.debug("Response Body: #{body.inspect}")
- unless body.is_a?(Hash)
- raise InvalidResponse, "The server response could not be understood"
- end
- case finished_env[:status]
- when 200..299
- FlexmlsApi.logger.debug("Success!")
- session = OAuthSession.new(body)
- else
- # Handle the WWW-Authenticate Response Header Field if present. This can be returned by
- # OAuth2 implementations and wouldn't hurt to log.
- auth_header_error = finished_env[:request_headers]["WWW-Authenticate"]
- FlexmlsApi.logger.warn("Authentication error #{auth_header_error}") unless auth_header_error.nil?
- raise ClientError.new(0, finished_env[:status]), body["error"]
- end
- FlexmlsApi.logger.debug("Session= #{session.inspect}")
- finished_env[:body] = session
- end
-
- def initialize(app)
- super
- @parser = nil
- end
-
end
-
+
end
end