Class: SsoOauthServer

Inherits:
SsoServer show all
Defined in:
lib/app/models/sso_oauth_server.rb

Overview

OAuth 2.0 Server

Direct Known Subclasses

SsoGoogleServer

Instance Method Summary collapse

Methods inherited from SsoServer

#display_instructions, #display_name

Instance Method Details

#auth_code_url(request, parameters = {}) ⇒ Object

Returns a URL to the OAuth 2.0 provider's consent page asking for permissions for the given scopes explicitly.

State is a token to protect the user from CSRF attacks. You must always provide a non-zero string and validate that it matches the state query parameter on your redirect callback.



99
100
101
102
103
104
105
106
107
108
# File 'lib/app/models/sso_oauth_server.rb', line 99

def auth_code_url(request, parameters = {})
  super
  uri = URI(auth_endpoint)
  uri.query = parameters.merge(response_type: 'code',
                               client_id: client_id,
                               redirect_uri: redirect_uri,
                               scope: requested_scopes.join(' '),
                               state: request.state).to_query
  uri.to_s
end

#auth_endpointObject

This method is abstract.

OAuth 2.0 provider's authorization endpoint URL

Returns URL - The URL for the authorization end point.

Returns:

  • URL - The URL for the authorization end point



55
56
57
58
59
# File 'lib/app/models/sso_oauth_server.rb', line 55

def auth_endpoint
  return nil if server_url.blank? || auth_url.blank?

  prefix_server auth_url
end

#clean_user_profile(user_profile) ⇒ Object

This method is abstract.

Clean up the user profile to only return what is listed in the key mapping. Not everything

Returns Hash - The user profile using the mapping from this class.

Returns:

  • Hash - The user profile using the mapping from this class



45
46
47
48
49
50
51
# File 'lib/app/models/sso_oauth_server.rb', line 45

def ()
  profile = {}
  profile_key_mapping.each do |key, value|
    profile[key] = [value]
  end
  profile
end

#exchange(root_url, code) ⇒ Object

Exchange will convert an authorization code into a token.



113
114
115
116
117
118
119
120
121
122
# File 'lib/app/models/sso_oauth_server.rb', line 113

def exchange(root_url, code)
  response = send_request_for_exchange(root_url, code)
  log_debug response.body.inspect
  raise 'Cannot fetch token' unless response.code.between? 200, 299

  extract_token(response_values(response))
rescue StandardError => error
  log_error 'Unable to retrieve profile', error
  nil
end

#extract_token(vals) ⇒ Object

Token will come in the following schema (hopefully)

  • AccessToken: access_token
  • TokenType: token_type
  • Refresh Token: refresh_token
  • Expiry: expires_in, expires (thank you FB)


158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/app/models/sso_oauth_server.rb', line 158

def extract_token(vals)
  log_debug "extract_token: #{vals.inspect}"
  token = SsoOauthToken.new
  token.access_token = vals['access_token']
  token.token_type = vals['token_type']
  token.refresh_token = vals['refresh_token']

  # Convert `e` to integer and then to a time
  e = vals['expires_in']
  e = vals['expires'] if e.blank?
  token.expiry = Time.now.utc + e.to_i.seconds unless e.blank?

  token
end

#get_profile(access_token) ⇒ Hash

This method is abstract.

Get the profile given an access token, can be overriden by concrete implementations.

Parameters:

Returns:



127
128
129
130
131
132
# File 'lib/app/models/sso_oauth_server.rb', line 127

def get_profile(access_token)
  log_debug "get_profile profile_endpoint: #{profile_endpoint} with token: #{access_token.inspect}"
  response = RestClient.get(profile_endpoint, Authorization: access_token.authorization)
  log_debug "get_profile response: #{response.body}"
  response_values(response)
end

#profile_endpointObject

This method is abstract.

OAuth 2.0 provider's profile endpoint URL

Returns Profile end point.

Returns:

  • Profile end point



71
72
73
74
75
# File 'lib/app/models/sso_oauth_server.rb', line 71

def profile_endpoint
  return nil if server_url.blank? || profile_url.blank?

  prefix_server profile_url
end

#profile_key_mappingObject

This method is abstract.

Default mapping of user profile keys to what the caller is expecting

Returns Hash - Mapping of key names for the user profile.

Returns:

  • Hash - Mapping of key names for the user profile



39
40
41
# File 'lib/app/models/sso_oauth_server.rb', line 39

def profile_key_mapping
  @profile_key_mapping ||= { name: 'name', email: 'email' }
end

#redirect_uri(root_url = SystemConfiguration.base_url) ⇒ Object

This method is abstract.

URL to redirect users to going through the flow

Returns URL - Redirect URL for this server.

Returns:

  • URL - Redirect URL for this server



79
80
81
82
83
# File 'lib/app/models/sso_oauth_server.rb', line 79

def redirect_uri(root_url = SystemConfiguration.base_url)
  return root_url if redirect_path.blank? || root_url.blank?

  [root_url, (root_url.end_with?('/') ? '' : '/'), redirect_path.gsub(%r{^/+}, '')].join
end

#requested_scopesObject

This method is abstract.

Scope specifies optional requested permissions



87
88
89
# File 'lib/app/models/sso_oauth_server.rb', line 87

def requested_scopes
  scopes.present? ? scopes.split(' ') : []
end

#response_values(response) ⇒ Object

Based on the response content type get a hash of values.



137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/app/models/sso_oauth_server.rb', line 137

def response_values(response)
  log_debug "response_values response: #{response.inspect}"
  case response.headers[:content_type]
  when 'application/x-www-form-urlencoded', 'text/plain'
    log_debug "form or plain text response: #{response.body}"
    log_debug Rack::Utils.parse_nested_query(response.body).inspect
    Rack::Utils.parse_nested_query(response.body)
  else
    log_debug "json: #{response.body}"
    JSON.parse(response.body)
  end
end

#token_endpointObject

This method is abstract.

OAuth 2.0 provider's token endpoint URL

Returns URL - the Token endpoint.

Returns:

  • URL - the Token endpoint



63
64
65
66
67
# File 'lib/app/models/sso_oauth_server.rb', line 63

def token_endpoint
  return nil if server_url.blank? || token_url.blank?

  prefix_server token_url
end

#user_profile(code) ⇒ Object

This method is abstract.

Using the appropriate SSO server configuration obtain the user profile address

Returns Hash - User profile as a hash.

Returns:

  • Hash - User profile as a hash



22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/app/models/sso_oauth_server.rb', line 22

def (code)
  # Exchange the code to validate the token
  token = exchange(SystemConfiguration.base_url, code)
  log_debug "user_profile token: #{token.inspect}"
  raise 'Invalid token' unless token.present? && token.valid?

  # Get scoped information using the access token
   = get_profile(token)
  log_debug "user_profile user_profile: #{user_profile.inspect}"
  raise 'Failed to retrieve user profile from provider' if .blank?

  # user_profile.symbolize_keys
  ()
end