Class: MAP::SecurityToken::Service

Inherits:
Common::Client::Base show all
Defined in:
lib/map/security_token/service.rb

Instance Method Summary collapse

Methods inherited from Common::Client::Base

#config, configuration, #connection, #delete, #get, #perform, #post, #put, #raise_backend_exception, #raise_not_authenticated, #request, #sanitize_headers!, #service_name

Methods included from SentryLogging

#log_exception_to_sentry, #log_message_to_sentry, #non_nil_hash?, #normalize_level, #rails_logger, #set_sentry_metadata

Instance Method Details

#client_assertion_creation_timeObject (private)



122
123
124
# File 'lib/map/security_token/service.rb', line 122

def client_assertion_creation_time
  Time.zone.now
end

#client_assertion_expirationObject (private)



126
127
128
# File 'lib/map/security_token/service.rb', line 126

def client_assertion_expiration
  (client_assertion_creation_time + config.client_assertion_expiration_seconds)
end

#client_assertion_jti_seedObject (private)



118
119
120
# File 'lib/map/security_token/service.rb', line 118

def client_assertion_jti_seed
  SecureRandom.uuid
end

#client_assertion_jwt(client_id, icn) ⇒ Object (private)



112
113
114
115
116
# File 'lib/map/security_token/service.rb', line 112

def client_assertion_jwt(client_id, icn)
  JWT.encode(client_assertion_jwt_payload(client_id, icn),
             config.client_assertion_private_key,
             config.client_assertion_encode_algorithm)
end

#client_assertion_jwt_payload(client_id, icn) ⇒ Object (private)



98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/map/security_token/service.rb', line 98

def client_assertion_jwt_payload(client_id, icn)
  {
    role: config.client_assertion_role,
    patient_id: icn,
    patient_id_type: config.client_assertion_patient_id_type,
    sub: client_id,
    jti: client_assertion_jti_seed,
    iss: client_id,
    aud: "#{config.base_path}/#{config.token_path}",
    nbf: client_assertion_creation_time.to_i,
    exp: client_assertion_expiration.to_i
  }
end

#client_id_from_application(application) ⇒ Object (private)



71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/map/security_token/service.rb', line 71

def client_id_from_application(application)
  case application
  when :chatbot
    config.chatbot_client_id
  when :sign_up_service
    config.
  when :check_in
    config.check_in_client_id
  when :appointments
    config.appointments_client_id
  else
    raise Errors::ApplicationMismatchError, "#{config.logging_prefix} token failed, application mismatch detected"
  end
end

#parse_and_raise_error(e, icn, application) ⇒ Object (private)

Raises:

  • (e)


47
48
49
50
51
52
53
54
55
56
# File 'lib/map/security_token/service.rb', line 47

def parse_and_raise_error(e, icn, application)
  status = e.status
  error_source = status >= 500 ? 'server' : 'client'
  parse_body = e.body.presence || {}
  context = { error: parse_body['error'] }
  message = "#{config.logging_prefix} token failed, #{error_source} error"

  Rails.logger.error(message, status:, application:, icn:, context:)
  raise e, "#{message}, status: #{status}, application: #{application}, icn: #{icn}, context: #{context}"
end

#parse_response(response, application, icn) ⇒ Object (private)



58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/map/security_token/service.rb', line 58

def parse_response(response, application, icn)
  response_body = response.body

  {
    access_token: response_body['access_token'],
    expiration: Time.zone.now + response_body['expires_in']
  }
rescue => e
  message = "#{config.logging_prefix} token failed, response unknown"
  Rails.logger.error(message, application:, icn:)
  raise e, "#{message}, application: #{application}, icn: #{icn}"
end

#request_token(application, icn) ⇒ Object (private)



39
40
41
42
43
44
45
# File 'lib/map/security_token/service.rb', line 39

def request_token(application, icn)
  response = perform(:post,
                     config.token_path,
                     token_params(application, icn),
                     { 'Content-Type' => 'application/x-www-form-urlencoded' })
  parse_response(response, application, icn)
end

#token(application:, icn:, cache: true) ⇒ Object



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/map/security_token/service.rb', line 11

def token(application:, icn:, cache: true)
  cached_response = true
  Rails.logger.info("#{config.logging_prefix} token request", { application:, icn: })
  token = Rails.cache.fetch("map_sts_token_#{application}_#{icn}", expires_in: 5.minutes, force: !cache) do
    cached_response = false
    request_token(application, icn)
  end
  Rails.logger.info("#{config.logging_prefix} token success", { application:, icn:, cached_response: })
  token
rescue Common::Client::Errors::ParsingError => e
  Rails.logger.error("#{config.logging_prefix} token failed, parsing error", application:, icn:,
                                                                             context: e.message)
  raise e
rescue Common::Client::Errors::ClientError => e
  parse_and_raise_error(e, icn, application)
rescue Common::Exceptions::GatewayTimeout => e
  Rails.logger.error("#{config.logging_prefix} token failed, gateway timeout", application:, icn:)
  raise e
rescue Errors::ApplicationMismatchError => e
  Rails.logger.error(e.message, application:, icn:)
  raise e
rescue Errors::MissingICNError => e
  Rails.logger.error(e.message, application:)
  raise e
end

#token_params(application, icn) ⇒ Object (private)



86
87
88
89
90
91
92
93
94
95
96
# File 'lib/map/security_token/service.rb', line 86

def token_params(application, icn)
  unless icn
    raise Errors::MissingICNError, "#{config.logging_prefix} token failed, ICN not present in access token"
  end

  client_id = client_id_from_application(application)
  URI.encode_www_form({ grant_type: config.grant_type,
                        client_id:,
                        client_assertion_type: config.client_assertion_type,
                        client_assertion: client_assertion_jwt(client_id, icn) })
end