Class: SignIn::Logingov::Service
Constant Summary
collapse
- DEFAULT_SCOPES =
[
PROFILE_SCOPE = 'profile',
VERIFIED_AT_SCOPE = 'profile:verified_at',
ADDRESS_SCOPE = 'address',
EMAIL_SCOPE = 'email',
OPENID_SCOPE = 'openid',
SSN_SCOPE = 'social_security_number'
].freeze
- OPTIONAL_SCOPES =
[ALL_EMAILS_SCOPE = 'all_emails'].freeze
Instance Attribute Summary collapse
Instance Method Summary
collapse
Methods included from PublicJwks
#jwks_loader, #parse_public_jwks, #public_jwks
#config, configuration, #connection, #delete, #get, #perform, #post, #put, #raise_backend_exception, #raise_not_authenticated, #request, #sanitize_headers!, #service_name
#log_exception_to_sentry, #log_message_to_sentry, #non_nil_hash?, #normalize_level, #rails_logger, #set_sentry_metadata
Constructor Details
#initialize(optional_scopes: []) ⇒ Service
Returns a new instance of Service.
27
28
29
30
|
# File 'lib/sign_in/logingov/service.rb', line 27
def initialize(optional_scopes: [])
@optional_scopes = valid_optional_scopes(optional_scopes)
super()
end
|
Instance Attribute Details
#optional_scopes ⇒ Object
Returns the value of attribute optional_scopes.
25
26
27
|
# File 'lib/sign_in/logingov/service.rb', line 25
def optional_scopes
@optional_scopes
end
|
Instance Method Details
#auth_params(acr, state, scope) ⇒ Object
94
95
96
97
98
99
100
101
102
103
104
105
|
# File 'lib/sign_in/logingov/service.rb', line 94
def auth_params(acr, state, scope)
{
acr_values: acr,
client_id: config.client_id,
nonce: random_seed,
prompt: config.prompt,
redirect_uri: config.redirect_uri,
response_type: config.response_type,
scope:,
state:
}
end
|
#auth_url ⇒ Object
164
165
166
|
# File 'lib/sign_in/logingov/service.rb', line 164
def auth_url
"#{config.base_path}/#{config.auth_path}"
end
|
#client_assertion_jwt ⇒ Object
204
205
206
207
208
209
210
211
212
213
214
|
# File 'lib/sign_in/logingov/service.rb', line 204
def client_assertion_jwt
jwt_payload = {
iss: config.client_id,
sub: config.client_id,
aud: token_url,
jti: SecureRandom.hex,
nonce: random_seed,
exp: Time.now.to_i + config.client_assertion_expiration_seconds
}
JWT.encode(jwt_payload, config.ssl_key, 'RS256')
end
|
#encode_logout_redirect(logout_redirect_uri) ⇒ Object
193
194
195
|
# File 'lib/sign_in/logingov/service.rb', line 193
def encode_logout_redirect(logout_redirect_uri)
Base64.encode64(logout_state_payload(logout_redirect_uri).to_json)
end
|
#get_authn_context(current_ial) ⇒ Object
160
161
162
|
# File 'lib/sign_in/logingov/service.rb', line 160
def get_authn_context(current_ial)
current_ial == Constants::Auth::IAL_TWO ? Constants::Auth::LOGIN_GOV_IAL2 : Constants::Auth::LOGIN_GOV_IAL1
end
|
#jwt_decode(encoded_jwt) ⇒ Object
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
# File 'lib/sign_in/logingov/service.rb', line 142
def jwt_decode(encoded_jwt)
verify_expiration = true
JWT.decode(
encoded_jwt,
nil,
verify_expiration,
{ verify_expiration:, algorithm: config.jwt_decode_algorithm, jwks: method(:jwks_loader) }
).first
rescue JWT::JWKError
raise Errors::PublicJWKError, '[SignIn][Logingov][Service] Public JWK is malformed'
rescue JWT::VerificationError
raise Errors::JWTVerificationError, '[SignIn][Logingov][Service] JWT body does not match signature'
rescue JWT::ExpiredSignature
raise Errors::JWTExpiredError, '[SignIn][Logingov][Service] JWT has expired'
rescue JWT::DecodeError
raise Errors::JWTDecodeError, '[SignIn][Logingov][Service] JWT is malformed'
end
|
#log_credential(credential) ⇒ Object
125
126
127
|
# File 'lib/sign_in/logingov/service.rb', line 125
def log_credential(credential)
MockedAuthentication::Mockdata::Writer.save_credential(credential:, credential_type: 'logingov')
end
|
#logout_state_payload(logout_redirect_uri) ⇒ Object
197
198
199
200
201
202
|
# File 'lib/sign_in/logingov/service.rb', line 197
def logout_state_payload(logout_redirect_uri)
{
logout_redirect: logout_redirect_uri,
seed: random_seed
}
end
|
#normalize_address(address) ⇒ Object
107
108
109
110
111
112
113
114
115
116
117
118
119
|
# File 'lib/sign_in/logingov/service.rb', line 107
def normalize_address(address)
return unless address
street_array = address[:street_address].split("\n")
{
street: street_array[0],
street2: street_array[1],
postal_code: address[:postal_code],
state: address[:region],
city: address[:locality],
country: united_states_country_code
}
end
|
#normalized_attributes(user_info, credential_level) ⇒ Object
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
# File 'lib/sign_in/logingov/service.rb', line 73
def normalized_attributes(user_info, credential_level)
{
logingov_uuid: user_info.sub,
current_ial: credential_level.current_ial,
max_ial: credential_level.max_ial,
ssn: user_info.social_security_number&.tr('-', ''),
birth_date: user_info.birthdate,
first_name: user_info.given_name,
last_name: user_info.family_name,
address: normalize_address(user_info.address),
csp_email: user_info.email,
all_csp_emails: user_info.all_emails,
multifactor: true,
service_name: config.service_name,
authn_context: get_authn_context(credential_level.current_ial),
auto_uplevel: credential_level.auto_uplevel
}
end
|
#parse_token_response(response_body) ⇒ Object
136
137
138
139
140
|
# File 'lib/sign_in/logingov/service.rb', line 136
def parse_token_response(response_body)
access_token = response_body[:access_token]
logingov_acr = jwt_decode(response_body[:id_token])['acr']
{ access_token:, logingov_acr: }
end
|
#raise_client_error(client_error, function_name) ⇒ Object
129
130
131
132
133
134
|
# File 'lib/sign_in/logingov/service.rb', line 129
def raise_client_error(client_error, function_name)
status = client_error.status
description = client_error.body && client_error.body[:error]
raise client_error, "[SignIn][Logingov][Service] Cannot perform #{function_name} request, " \
"status: #{status}, description: #{description}"
end
|
#random_seed ⇒ Object
216
217
218
|
# File 'lib/sign_in/logingov/service.rb', line 216
def random_seed
@random_seed ||= SecureRandom.hex
end
|
#render_auth(state: SecureRandom.hex, acr: Constants::Auth::LOGIN_GOV_IAL1, operation: Constants::Auth::AUTHORIZE) ⇒ Object
32
33
34
35
36
37
38
39
40
41
|
# File 'lib/sign_in/logingov/service.rb', line 32
def render_auth(state: SecureRandom.hex,
acr: Constants::Auth::LOGIN_GOV_IAL1,
operation: Constants::Auth::AUTHORIZE)
Rails.logger.info('[SignIn][Logingov][Service] Rendering auth, ' \
"state: #{state}, acr: #{acr}, operation: #{operation}, " \
"optional_scopes: #{optional_scopes}")
scope = (DEFAULT_SCOPES + optional_scopes).join(' ')
RedirectUrlGenerator.new(redirect_uri: auth_url, params_hash: auth_params(acr, state, scope)).perform
end
|
#render_logout(client_logout_redirect_uri) ⇒ Object
43
44
45
46
|
# File 'lib/sign_in/logingov/service.rb', line 43
def render_logout(client_logout_redirect_uri)
"#{sign_out_url}?#{sign_out_params(config.logout_redirect_uri,
encode_logout_redirect(client_logout_redirect_uri)).to_query}"
end
|
#render_logout_redirect(state) ⇒ Object
48
49
50
51
52
|
# File 'lib/sign_in/logingov/service.rb', line 48
def render_logout_redirect(state)
state_hash = JSON.parse(Base64.decode64(state))
logout_redirect_uri = state_hash['logout_redirect']
RedirectUrlGenerator.new(redirect_uri: URI.parse(logout_redirect_uri).to_s).perform
end
|
#sign_out_params(redirect_uri, state) ⇒ Object
176
177
178
179
180
181
182
|
# File 'lib/sign_in/logingov/service.rb', line 176
def sign_out_params(redirect_uri, state)
{
client_id: config.client_id,
post_logout_redirect_uri: redirect_uri,
state:
}
end
|
#sign_out_url ⇒ Object
172
173
174
|
# File 'lib/sign_in/logingov/service.rb', line 172
def sign_out_url
"#{config.base_path}/#{config.logout_path}"
end
|
#token(code) ⇒ Object
54
55
56
57
58
59
60
61
62
|
# File 'lib/sign_in/logingov/service.rb', line 54
def token(code)
response = perform(
:post, config.token_path, token_params(code), { 'Content-Type' => 'application/json' }
)
Rails.logger.info("[SignIn][Logingov][Service] Token Success, code: #{code}")
parse_token_response(response.body)
rescue Common::Client::Errors::ClientError => e
raise_client_error(e, 'Token')
end
|
#token_params(code) ⇒ Object
184
185
186
187
188
189
190
191
|
# File 'lib/sign_in/logingov/service.rb', line 184
def token_params(code)
{
grant_type: config.grant_type,
code:,
client_assertion_type: config.client_assertion_type,
client_assertion: client_assertion_jwt
}.to_json
end
|
#token_url ⇒ Object
168
169
170
|
# File 'lib/sign_in/logingov/service.rb', line 168
def token_url
"#{config.base_path}/#{config.token_path}"
end
|
#united_states_country_code ⇒ Object
121
122
123
|
# File 'lib/sign_in/logingov/service.rb', line 121
def united_states_country_code
'USA'
end
|
#user_info(token) ⇒ Object
64
65
66
67
68
69
70
71
|
# File 'lib/sign_in/logingov/service.rb', line 64
def user_info(token)
response = perform(:get, config.userinfo_path, nil, { 'Authorization' => "Bearer #{token}" })
log_credential(response.body) if config.log_credential
OpenStruct.new(response.body)
rescue Common::Client::Errors::ClientError => e
raise_client_error(e, 'UserInfo')
end
|
#valid_optional_scopes(optional_scopes) ⇒ Object
220
221
222
|
# File 'lib/sign_in/logingov/service.rb', line 220
def valid_optional_scopes(optional_scopes)
optional_scopes.to_a & OPTIONAL_SCOPES
end
|