class Nucleus::Adapters::OAuth2AuthClient

Public Class Methods

new(auth_url, check_certificates = true) click to toggle source

Create a new instance of an {OAuth2AuthClient}, which uses the standardized OAuth2 authentication method. @param [Boolean] check_certificates true if SSL certificates are to be validated, false if they are to be ignored (e.g. when using self-signed certificates in development environments) @param [String] auth_url URL to the OAuth2 endpoint

Calls superclass method Nucleus::Adapters::AuthClient::new
# File lib/nucleus/core/adapter_extensions/auth/o_auth2_auth_client.rb, line 10
def initialize(auth_url, check_certificates = true)
  @auth_url = auth_url
  super(check_certificates)
end

Public Instance Methods

auth_header() click to toggle source
# File lib/nucleus/core/adapter_extensions/auth/o_auth2_auth_client.rb, line 25
def auth_header
  raise Errors::EndpointAuthenticationError, 'Authentication client was not authenticated yet' unless @access_token
  if expired?
    log.debug('OAuth2 access_token is expired, trigger refresh before returning auth_header')
    # token is expired, renew first
    refresh
  end
  # then return the authorization header
  header
end
authenticate(username, password) click to toggle source
# File lib/nucleus/core/adapter_extensions/auth/o_auth2_auth_client.rb, line 15
def authenticate(username, password)
  return self if @access_token
  response = post(query: { grant_type: 'password', username: username, password: password })
  body = body(response)
  extract(body)
  # refresh token is not included in later updates
  @refresh_token = body[:refresh_token]
  self
end
refresh() click to toggle source
# File lib/nucleus/core/adapter_extensions/auth/o_auth2_auth_client.rb, line 36
def refresh
  if @refresh_token.nil?
    raise Errors::EndpointAuthenticationError, "Can't refresh token before initial authentication"
  end
  log.debug("Attempt to refresh the access_token with our refresh_token: '#{@refresh_token}'")
  response = post(query: { grant_type: 'refresh_token', refresh_token: @refresh_token })
  extract(body(response))
  self
end

Private Instance Methods

body(response) click to toggle source
# File lib/nucleus/core/adapter_extensions/auth/o_auth2_auth_client.rb, line 82
def body(response)
  Oj.load(response.body, symbol_keys: true)
end
expired?() click to toggle source
# File lib/nucleus/core/adapter_extensions/auth/o_auth2_auth_client.rb, line 77
def expired?
  return true if @expiration.nil?
  Time.now >= @expiration
end
extract(body) click to toggle source
# File lib/nucleus/core/adapter_extensions/auth/o_auth2_auth_client.rb, line 86
def extract(body)
  @access_token = body[:access_token]
  # number of seconds until expiration, deduct processing buffer
  seconds_left = body[:expires_in] - 30
  @expiration = Time.now + seconds_left
  @token_type = body[:token_type]
end
header() click to toggle source
# File lib/nucleus/core/adapter_extensions/auth/o_auth2_auth_client.rb, line 73
def header
  { 'Authorization' => "#{@token_type} #{@access_token}" }
end
post(params) click to toggle source
# File lib/nucleus/core/adapter_extensions/auth/o_auth2_auth_client.rb, line 48
def post(params)
  middleware = Excon.defaults[:middlewares].dup
  middleware << Excon::Middleware::Decompress
  middleware << Excon::Middleware::RedirectFollower
  # explicitly allow redirects, otherwise they would cause an error
  # TODO: Basic Y2Y6 could be cloud-foundry specific
  request_params = { expects: [200, 301, 302, 303, 307, 308], middlewares: middleware.uniq,
                     headers: { 'Authorization' => 'Basic Y2Y6',
                                'Content-Type' => 'application/x-www-form-urlencoded',
                                'Accept' => 'application/json' } }.merge(params)
  # execute the post request and return the response
  Excon.new(@auth_url, ssl_verify_peer: verify_ssl).post(request_params)
rescue Excon::Errors::HTTPStatusError => e
  log.debug "OAuth2 authentication failed: #{e}"
  case e.response.status
  when 403
    log.error("OAuth2 for '#{@auth_url}' failed with status 403 (access denied), indicating an adapter issue")
    raise Errors::UnknownAdapterCallError, 'Access to resource denied, probably the adapter must be updated'
  when 400, 401
    raise Errors::EndpointAuthenticationError, body(e.response)[:error_description]
  end
  # re-raise all unhandled exception, indicating adapter issues
  raise Errors::UnknownAdapterCallError, 'OAuth2 call failed unexpectedly, probably the adapter must be updated'
end