Class: SignIn::Idme::Service

Inherits:
Common::Client::Base show all
Includes:
PublicJwks
Defined in:
lib/sign_in/idme/service.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from PublicJwks

#jwks_loader, #parse_public_jwks, #public_jwks

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 Attribute Details

#typeObject

Returns the value of attribute type.



14
15
16
# File 'lib/sign_in/idme/service.rb', line 14

def type
  @type
end

Instance Method Details

#address_defined?(user_info) ⇒ Boolean (private)

Returns:

  • (Boolean)


147
148
149
# File 'lib/sign_in/idme/service.rb', line 147

def address_defined?()
  .street && .zip && .state && .city
end

#auth_params(acr, state, operation) ⇒ Object (private)



55
56
57
58
59
60
61
62
63
64
# File 'lib/sign_in/idme/service.rb', line 55

def auth_params(acr, state, operation)
  {
    scope: acr,
    state:,
    client_id: config.client_id,
    redirect_uri: config.redirect_uri,
    response_type: config.response_type,
    op: convert_operation(operation)
  }.compact
end

#auth_urlObject (private)



187
188
189
# File 'lib/sign_in/idme/service.rb', line 187

def auth_url
  "#{config.base_path}/#{config.auth_path}"
end

#convert_operation(operation) ⇒ Object (private)



66
67
68
69
70
71
# File 'lib/sign_in/idme/service.rb', line 66

def convert_operation(operation)
  case operation
  when Constants::Auth::SIGN_UP
    config.
  end
end

#dslogon_attributes(user_info) ⇒ Object (private)



116
117
118
119
120
121
122
123
124
125
# File 'lib/sign_in/idme/service.rb', line 116

def dslogon_attributes()
  {
    ssn: .dslogon_idvalue&.tr('-', ''),
    birth_date: .dslogon_birth_date,
    first_name: .dslogon_fname,
    middle_name: .dslogon_mname,
    last_name: .dslogon_lname,
    edipi: .dslogon_uuid
  }
end

#get_authn_context(current_ial) ⇒ Object (private)



93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/sign_in/idme/service.rb', line 93

def get_authn_context(current_ial)
  case type
  when Constants::Auth::IDME
    current_ial == Constants::Auth::IAL_TWO ? Constants::Auth::IDME_LOA3 : Constants::Auth::IDME_LOA1
  when Constants::Auth::MHV
    current_ial == Constants::Auth::IAL_TWO ? Constants::Auth::IDME_MHV_LOA3 : Constants::Auth::IDME_MHV_LOA1
  when Constants::Auth::DSLOGON
    return Constants::Auth::IDME_DSLOGON_LOA3 if current_ial == Constants::Auth::IAL_TWO

    Constants::Auth::IDME_DSLOGON_LOA1
  end
end

#idme_attributes(user_info) ⇒ Object (private)



106
107
108
109
110
111
112
113
114
# File 'lib/sign_in/idme/service.rb', line 106

def idme_attributes()
  {
    ssn: .social&.tr('-', ''),
    birth_date: .birth_date,
    first_name: .fname,
    last_name: .lname,
    address: normalize_address()
  }
end

#jwe_decrypt(encrypted_jwe) ⇒ Object (private)



155
156
157
158
159
# File 'lib/sign_in/idme/service.rb', line 155

def jwe_decrypt(encrypted_jwe)
  JWE.decrypt(encrypted_jwe, config.ssl_key)
rescue JWE::DecodeError
  raise Errors::JWEDecodeError, '[SignIn][Idme][Service] JWE is malformed'
end

#jwt_decode(encoded_jwt) ⇒ Object (private)



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/sign_in/idme/service.rb', line 161

def jwt_decode(encoded_jwt)
  verify_expiration = true

  decoded_jwt = JWT.decode(
    encoded_jwt,
    nil,
    verify_expiration,
    { verify_expiration:, algorithm: config.jwt_decode_algorithm, jwks: method(:jwks_loader) }
  ).first
  log_parsed_credential(decoded_jwt) if config.log_credential

  OpenStruct.new(decoded_jwt)
rescue JWT::JWKError
  raise Errors::PublicJWKError, '[SignIn][Idme][Service] Public JWK is malformed'
rescue JWT::VerificationError
  raise Errors::JWTVerificationError, '[SignIn][Idme][Service] JWT body does not match signature'
rescue JWT::ExpiredSignature
  raise Errors::JWTExpiredError, '[SignIn][Idme][Service] JWT has expired'
rescue JWT::DecodeError
  raise Errors::JWTDecodeError, '[SignIn][Idme][Service] JWT is malformed'
end

#log_parsed_credential(decoded_jwt) ⇒ Object (private)



183
184
185
# File 'lib/sign_in/idme/service.rb', line 183

def log_parsed_credential(decoded_jwt)
  MockedAuthentication::Mockdata::Writer.save_credential(credential: decoded_jwt, credential_type: type)
end

#mhv_attributes(user_info) ⇒ Object (private)



127
128
129
130
131
132
133
# File 'lib/sign_in/idme/service.rb', line 127

def mhv_attributes()
  {
    mhv_correlation_id: .mhv_uuid,
    mhv_icn: .mhv_icn,
    mhv_assurance: .mhv_assurance
  }
end

#normalize_address(user_info) ⇒ Object (private)



135
136
137
138
139
140
141
142
143
144
145
# File 'lib/sign_in/idme/service.rb', line 135

def normalize_address()
  return unless address_defined?()

  {
    street: .street,
    postal_code: .zip,
    state: .state,
    city: .city,
    country: united_states_country_code
  }
end

#normalized_attributes(user_info, credential_level) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
# File 'lib/sign_in/idme/service.rb', line 23

def normalized_attributes(, credential_level)
  attributes = case type
               when Constants::Auth::IDME
                 idme_attributes()
               when Constants::Auth::DSLOGON
                 dslogon_attributes()
               when Constants::Auth::MHV
                 mhv_attributes()
               end
  attributes.merge(standard_attributes(, credential_level))
end

#raise_client_error(client_error, function_name) ⇒ Object (private)

Raises:

  • (client_error)


73
74
75
76
77
78
# File 'lib/sign_in/idme/service.rb', line 73

def raise_client_error(client_error, function_name)
  status = client_error.status
  description = client_error.body && client_error.body[:error_description]
  raise client_error, "[SignIn][Idme][Service] Cannot perform #{function_name} request, " \
                      "status: #{status}, description: #{description}"
end

#render_auth(state: SecureRandom.hex, acr: Constants::Auth::IDME_LOA1, operation: Constants::Auth::AUTHORIZE) ⇒ Object



16
17
18
19
20
21
# File 'lib/sign_in/idme/service.rb', line 16

def render_auth(state: SecureRandom.hex, acr: Constants::Auth::IDME_LOA1,
                operation: Constants::Auth::AUTHORIZE)
  Rails.logger.info('[SignIn][Idme][Service] Rendering auth, ' \
                    "state: #{state}, acr: #{acr}, operation: #{operation}")
  RedirectUrlGenerator.new(redirect_uri: auth_url, params_hash: auth_params(acr, state, operation)).perform
end

#standard_attributes(user_info, credential_level) ⇒ Object (private)



80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/sign_in/idme/service.rb', line 80

def standard_attributes(, credential_level)
  {
    idme_uuid: .sub,
    current_ial: credential_level.current_ial,
    max_ial: credential_level.max_ial,
    service_name: type,
    csp_email: .email,
    multifactor: .multifactor,
    authn_context: get_authn_context(credential_level.current_ial),
    auto_uplevel: credential_level.auto_uplevel
  }
end

#token(code) ⇒ Object



35
36
37
38
39
40
41
42
43
# File 'lib/sign_in/idme/service.rb', line 35

def token(code)
  response = perform(
    :post, config.token_path, token_params(code), { 'Content-Type' => 'application/json' }
  )
  Rails.logger.info("[SignIn][Idme][Service] Token Success, code: #{code}, scope: #{response.body[:scope]}")
  response.body
rescue Common::Client::Errors::ClientError => e
  raise_client_error(e, 'Token')
end

#token_params(code) ⇒ Object (private)



191
192
193
194
195
196
197
198
199
# File 'lib/sign_in/idme/service.rb', line 191

def token_params(code)
  {
    grant_type: config.grant_type,
    code:,
    client_id: config.client_id,
    client_secret: config.client_secret,
    redirect_uri: config.redirect_uri
  }.to_json
end

#united_states_country_codeObject (private)



151
152
153
# File 'lib/sign_in/idme/service.rb', line 151

def united_states_country_code
  'USA'
end

#user_info(token) ⇒ Object



45
46
47
48
49
50
51
# File 'lib/sign_in/idme/service.rb', line 45

def (token)
  response = perform(:get, config.userinfo_path, nil, { 'Authorization' => "Bearer #{token}" })
  decrypted_jwe = jwe_decrypt(JSON.parse(response.body))
  jwt_decode(decrypted_jwe)
rescue Common::Client::Errors::ClientError => e
  raise_client_error(e, 'UserInfo')
end