From 875d5dab08ff7a170912e354cd02ed9421c477e4 Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Fri, 16 Feb 2024 11:58:20 -0500 Subject: [PATCH 01/13] add changelog changelog: Internal, IdV, write acr_values and vtr to identites table From 63d448fd41f9d382471eb52d90be3b4731de55e6 Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Thu, 15 Feb 2024 14:13:46 -0500 Subject: [PATCH 02/13] add vot function to identity token --- app/services/id_token_builder.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/services/id_token_builder.rb b/app/services/id_token_builder.rb index 368d4e8d0a9..be091bb15e4 100644 --- a/app/services/id_token_builder.rb +++ b/app/services/id_token_builder.rb @@ -38,6 +38,7 @@ def jwt_payload def id_token_claims { acr: acr, + vot: vot, nonce: identity.nonce, aud: identity.service_provider, jti: SecureRandom.urlsafe_base64, @@ -65,6 +66,10 @@ def acr end end + def vot + identity.vot + end + def determine_ial_max_acr if identity.user.identity_verified? Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF From 6c77af28b56ba8590deeb9227ff68d8c1475a64e Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Thu, 15 Feb 2024 14:55:49 -0500 Subject: [PATCH 03/13] id_token_builder sends correct acr and vtr values --- app/services/id_token_builder.rb | 31 +++++++++++++++++--------- spec/services/id_token_builder_spec.rb | 23 +++++++++++++++++-- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/app/services/id_token_builder.rb b/app/services/id_token_builder.rb index be091bb15e4..b6c2d52729e 100644 --- a/app/services/id_token_builder.rb +++ b/app/services/id_token_builder.rb @@ -56,28 +56,37 @@ def timestamp_claims end def acr - ial = identity.ial - case ial - when Idp::Constants::IAL_MAX then determine_ial_max_acr - when Idp::Constants::IAL1 then Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF - when Idp::Constants::IAL2 then Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF - else - raise "Unknown ial #{ial}" - end + return nil unless identity.acr_values.present? + resolved_authn_context_result.component_values.map do |component_value| + if component_value == Vot::LegacyComponentValues::IALMAX + determine_ial_max_acr.name + else + component_value.name + end + end.join(' ') end def vot - identity.vot + return nil unless identity.vtr.present? + resolved_authn_context_result.component_values.map(&:name).join('.') end def determine_ial_max_acr if identity.user.identity_verified? - Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF + Vot::LegacyComponentValues::IAL2 else - Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF + Vot::LegacyComponentValues::IAL1 end end + def resolved_authn_context_result + @resolved_authn_context_result ||= AuthnContextResolver.new( + service_provider: identity.service_provider_record, + vtr: [identity.vot], + acr_values: identity.acr, + ).resolve + end + def expires now.to_i + ttl end diff --git a/spec/services/id_token_builder_spec.rb b/spec/services/id_token_builder_spec.rb index 22adaab045c..59c6e429629 100644 --- a/spec/services/id_token_builder_spec.rb +++ b/spec/services/id_token_builder_spec.rb @@ -61,22 +61,41 @@ expect(decoded_payload[:nonce]).to eq(identity.nonce) end + context 'it sets the vot' do + it 'sets the vot' do + identity.vot = 'Pb' + expect(decoded_payload[:vot]).to eq('C1.C2.P1.Pb') + end + end + context 'it sets the acr' do context 'ial2 request' do + before do + identity.ial = 2 + identity.acr = Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF + end + it 'sets the acr to the ial2 constant' do expect(decoded_payload[:acr]).to eq(Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF) end end context 'ial1 request' do - before { identity.ial = 1 } + before do + identity.ial = 1 + identity.acr = Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF + end + it 'sets the acr to the ial1 constant' do expect(decoded_payload[:acr]).to eq(Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF) end end context 'ialmax request' do - before { identity.ial = 0 } + before do + identity.ial = 0 + identity.acr = Saml::Idp::Constants::IALMAX_AUTHN_CONTEXT_CLASSREF + end context 'non-verified user' do it 'sets the acr to the ial1 constant' do From eddf90ff770b783f943b3b1fceed708a78fe4c64 Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Thu, 22 Feb 2024 12:59:19 -0500 Subject: [PATCH 04/13] add changelog changelog: Internal, IdV, Add vot to identity token From f0a6e23a4ee92e12e00976a467d5618d8f464d23 Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Thu, 22 Feb 2024 16:14:57 -0500 Subject: [PATCH 05/13] fix identity vot and acr names --- app/services/id_token_builder.rb | 4 ++-- spec/services/id_token_builder_spec.rb | 19 +++++++++++++++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/app/services/id_token_builder.rb b/app/services/id_token_builder.rb index b6c2d52729e..73eb8d0673e 100644 --- a/app/services/id_token_builder.rb +++ b/app/services/id_token_builder.rb @@ -82,8 +82,8 @@ def determine_ial_max_acr def resolved_authn_context_result @resolved_authn_context_result ||= AuthnContextResolver.new( service_provider: identity.service_provider_record, - vtr: [identity.vot], - acr_values: identity.acr, + vtr: [identity.vtr], + acr_values: identity.acr_values, ).resolve end diff --git a/spec/services/id_token_builder_spec.rb b/spec/services/id_token_builder_spec.rb index 59c6e429629..e3138131df7 100644 --- a/spec/services/id_token_builder_spec.rb +++ b/spec/services/id_token_builder_spec.rb @@ -63,16 +63,27 @@ context 'it sets the vot' do it 'sets the vot' do - identity.vot = 'Pb' + identity.vtr = 'Pb' expect(decoded_payload[:vot]).to eq('C1.C2.P1.Pb') end end context 'it sets the acr' do + context 'aal request' do + before do + identity.aal = 1 + identity.acr_values = Saml::Idp::Constants::AAL1_AUTHN_CONTEXT_CLASSREF + end + + it 'sets the acr to the aal1 constant' do + expect(decoded_payload[:acr]).to eq(Saml::Idp::Constants::AAL1_AUTHN_CONTEXT_CLASSREF) + end + end + context 'ial2 request' do before do identity.ial = 2 - identity.acr = Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF + identity.acr_values = Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF end it 'sets the acr to the ial2 constant' do @@ -83,7 +94,7 @@ context 'ial1 request' do before do identity.ial = 1 - identity.acr = Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF + identity.acr_values = Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF end it 'sets the acr to the ial1 constant' do @@ -94,7 +105,7 @@ context 'ialmax request' do before do identity.ial = 0 - identity.acr = Saml::Idp::Constants::IALMAX_AUTHN_CONTEXT_CLASSREF + identity.acr_values = Saml::Idp::Constants::IALMAX_AUTHN_CONTEXT_CLASSREF end context 'non-verified user' do From 61f70ffd3e83a43512e4812a46968b31bac8900d Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Fri, 23 Feb 2024 13:38:30 -0500 Subject: [PATCH 06/13] acr value only has IAL values within --- app/services/id_token_builder.rb | 8 +++++++- spec/services/id_token_builder_spec.rb | 14 +++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/app/services/id_token_builder.rb b/app/services/id_token_builder.rb index 73eb8d0673e..d87f6c4a82e 100644 --- a/app/services/id_token_builder.rb +++ b/app/services/id_token_builder.rb @@ -57,7 +57,7 @@ def timestamp_claims def acr return nil unless identity.acr_values.present? - resolved_authn_context_result.component_values.map do |component_value| + acr_ial_component_values.map do |component_value| if component_value == Vot::LegacyComponentValues::IALMAX determine_ial_max_acr.name else @@ -71,6 +71,12 @@ def vot resolved_authn_context_result.component_values.map(&:name).join('.') end + def acr_ial_component_values + resolved_authn_context_result.component_values.select do |component_value| + component_value.name.include?('ial') + end + end + def determine_ial_max_acr if identity.user.identity_verified? Vot::LegacyComponentValues::IAL2 diff --git a/spec/services/id_token_builder_spec.rb b/spec/services/id_token_builder_spec.rb index e3138131df7..39532b521d1 100644 --- a/spec/services/id_token_builder_spec.rb +++ b/spec/services/id_token_builder_spec.rb @@ -69,14 +69,18 @@ end context 'it sets the acr' do - context 'aal request' do + context 'aal and ial request' do before do - identity.aal = 1 - identity.acr_values = Saml::Idp::Constants::AAL1_AUTHN_CONTEXT_CLASSREF + identity.aal = 2 + acr_values = [ + Saml::Idp::Constants::AAL2_AUTHN_CONTEXT_CLASSREF, + Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF, + ].join(' ') + identity.acr_values = acr_values end - it 'sets the acr to the aal1 constant' do - expect(decoded_payload[:acr]).to eq(Saml::Idp::Constants::AAL1_AUTHN_CONTEXT_CLASSREF) + it 'ignores the aal value' do + expect(decoded_payload[:acr]).to eq(Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF) end end From b2ce75db540741da6f65fd659670a30c951db4cb Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Fri, 23 Feb 2024 14:14:28 -0500 Subject: [PATCH 07/13] hide vot behind feature flag --- app/services/id_token_builder.rb | 9 +++++++-- lib/idp/constants.rb | 2 ++ spec/services/id_token_builder_spec.rb | 10 ++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/app/services/id_token_builder.rb b/app/services/id_token_builder.rb index d87f6c4a82e..594b5649253 100644 --- a/app/services/id_token_builder.rb +++ b/app/services/id_token_builder.rb @@ -38,13 +38,14 @@ def jwt_payload def id_token_claims { acr: acr, - vot: vot, + vot: (vot if sp_requests_vot?), + vtm: (Idp::Constants::VTM if sp_requests_vot?), nonce: identity.nonce, aud: identity.service_provider, jti: SecureRandom.urlsafe_base64, at_hash: hash_token(identity.access_token), c_hash: hash_token(code), - } + }.compact end def timestamp_claims @@ -66,6 +67,10 @@ def acr end.join(' ') end + def sp_requests_vot? + IdentityConfig.store.use_vot_in_sp_requests + end + def vot return nil unless identity.vtr.present? resolved_authn_context_result.component_values.map(&:name).join('.') diff --git a/lib/idp/constants.rb b/lib/idp/constants.rb index b12c884dbcd..ed941d4c3d8 100644 --- a/lib/idp/constants.rb +++ b/lib/idp/constants.rb @@ -166,5 +166,7 @@ module Vendors MOCK_IDV_APPLICANT_FULL_STATE = 'Montana' MOCK_IDV_APPLICANT_FULL_IDENTITY_DOC_ADDRESS_STATE = 'Virginia' MOCK_IDV_APPLICANT_STATE = 'MT' + + VTM = 'https://developer.login.gov/vot-trust-framework' end end diff --git a/spec/services/id_token_builder_spec.rb b/spec/services/id_token_builder_spec.rb index 39532b521d1..aafe2716e62 100644 --- a/spec/services/id_token_builder_spec.rb +++ b/spec/services/id_token_builder_spec.rb @@ -62,10 +62,20 @@ end context 'it sets the vot' do + before do + allow(IdentityConfig.store).to receive(:use_vot_in_sp_requests). + and_return(true) + end + it 'sets the vot' do identity.vtr = 'Pb' expect(decoded_payload[:vot]).to eq('C1.C2.P1.Pb') end + + it 'sets the vtm' do + identity.vtr = 'Pb' + expect(decoded_payload[:vtm]).to eq(Idp::Constants::VTM) + end end context 'it sets the acr' do From 402811ebe20d07f501a843e0b2955cbf8ea34f66 Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Fri, 23 Feb 2024 14:27:19 -0500 Subject: [PATCH 08/13] store vtm in identity config --- config/application.yml.default | 1 + lib/identity_config.rb | 1 + lib/idp/constants.rb | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/config/application.yml.default b/config/application.yml.default index 9b82d3281e7..846acd36dcc 100644 --- a/config/application.yml.default +++ b/config/application.yml.default @@ -340,6 +340,7 @@ verify_gpo_key_max_attempts: 5 verify_personal_key_attempt_window_in_minutes: 15 verify_personal_key_max_attempts: 5 version_headers_enabled: false +vtm_url: 'https://developer.login.gov/vot-trust-framework' use_dashboard_service_providers: false use_kms: false use_vot_in_sp_requests: false diff --git a/lib/identity_config.rb b/lib/identity_config.rb index 66cbe876517..c509281e924 100644 --- a/lib/identity_config.rb +++ b/lib/identity_config.rb @@ -500,6 +500,7 @@ def self.build_store(config_map) config.add(:version_headers_enabled, type: :boolean) config.add(:voice_otp_pause_time) config.add(:voice_otp_speech_rate) + config.add(:vtm_url) config.add(:weekly_auth_funnel_report_config, type: :json) @key_types = config.key_types diff --git a/lib/idp/constants.rb b/lib/idp/constants.rb index ed941d4c3d8..bd6afaf61de 100644 --- a/lib/idp/constants.rb +++ b/lib/idp/constants.rb @@ -167,6 +167,6 @@ module Vendors MOCK_IDV_APPLICANT_FULL_IDENTITY_DOC_ADDRESS_STATE = 'Virginia' MOCK_IDV_APPLICANT_STATE = 'MT' - VTM = 'https://developer.login.gov/vot-trust-framework' + VTM = IdentityConfig.store.vtm_url end end From 5b407af09904091ab53590ff91669b778ec67fc6 Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Fri, 23 Feb 2024 14:34:46 -0500 Subject: [PATCH 09/13] fixup with config --- app/services/id_token_builder.rb | 2 +- lib/idp/constants.rb | 2 -- spec/services/id_token_builder_spec.rb | 5 ++++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/services/id_token_builder.rb b/app/services/id_token_builder.rb index 594b5649253..2c4f893822e 100644 --- a/app/services/id_token_builder.rb +++ b/app/services/id_token_builder.rb @@ -39,7 +39,7 @@ def id_token_claims { acr: acr, vot: (vot if sp_requests_vot?), - vtm: (Idp::Constants::VTM if sp_requests_vot?), + vtm: (IdentityConfig.store.vtm_url if sp_requests_vot?), nonce: identity.nonce, aud: identity.service_provider, jti: SecureRandom.urlsafe_base64, diff --git a/lib/idp/constants.rb b/lib/idp/constants.rb index bd6afaf61de..b12c884dbcd 100644 --- a/lib/idp/constants.rb +++ b/lib/idp/constants.rb @@ -166,7 +166,5 @@ module Vendors MOCK_IDV_APPLICANT_FULL_STATE = 'Montana' MOCK_IDV_APPLICANT_FULL_IDENTITY_DOC_ADDRESS_STATE = 'Virginia' MOCK_IDV_APPLICANT_STATE = 'MT' - - VTM = IdentityConfig.store.vtm_url end end diff --git a/spec/services/id_token_builder_spec.rb b/spec/services/id_token_builder_spec.rb index aafe2716e62..b1c420e36f6 100644 --- a/spec/services/id_token_builder_spec.rb +++ b/spec/services/id_token_builder_spec.rb @@ -21,6 +21,7 @@ let(:now) { Time.zone.now } let(:custom_expiration) { (now + 5.minutes).to_i } + let(:vtm_url) { 'https://example.com/vot-trust-framework' } subject(:builder) do IdTokenBuilder.new( identity: identity, @@ -65,6 +66,8 @@ before do allow(IdentityConfig.store).to receive(:use_vot_in_sp_requests). and_return(true) + allow(IdentityConfig.store).to receive(:vtm_url). + and_return(vtm_url) end it 'sets the vot' do @@ -74,7 +77,7 @@ it 'sets the vtm' do identity.vtr = 'Pb' - expect(decoded_payload[:vtm]).to eq(Idp::Constants::VTM) + expect(decoded_payload[:vtm]).to eq(vtm_url) end end From dddb63c8e143e9b0fa02b4cba576124292dc5c20 Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Mon, 26 Feb 2024 11:14:50 -0500 Subject: [PATCH 10/13] add check to not allow vtm if vot is nil --- app/services/id_token_builder.rb | 1 + spec/services/id_token_builder_spec.rb | 45 +++++++++++++++++++------- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/app/services/id_token_builder.rb b/app/services/id_token_builder.rb index 2c4f893822e..af1196d3a83 100644 --- a/app/services/id_token_builder.rb +++ b/app/services/id_token_builder.rb @@ -68,6 +68,7 @@ def acr end def sp_requests_vot? + return false unless identity.vtr.present? IdentityConfig.store.use_vot_in_sp_requests end diff --git a/spec/services/id_token_builder_spec.rb b/spec/services/id_token_builder_spec.rb index b1c420e36f6..e575d70602e 100644 --- a/spec/services/id_token_builder_spec.rb +++ b/spec/services/id_token_builder_spec.rb @@ -63,21 +63,42 @@ end context 'it sets the vot' do - before do - allow(IdentityConfig.store).to receive(:use_vot_in_sp_requests). - and_return(true) - allow(IdentityConfig.store).to receive(:vtm_url). - and_return(vtm_url) - end + context 'sp requests vot' do + before do + allow(IdentityConfig.store).to receive(:use_vot_in_sp_requests). + and_return(true) + allow(IdentityConfig.store).to receive(:vtm_url). + and_return(vtm_url) + end - it 'sets the vot' do - identity.vtr = 'Pb' - expect(decoded_payload[:vot]).to eq('C1.C2.P1.Pb') + it 'sets the vot if the sp requests it' do + identity.vtr = 'Pb' + expect(decoded_payload[:vot]).to eq('C1.C2.P1.Pb') + end + + it 'sets the vtm' do + identity.vtr = 'Pb' + expect(decoded_payload[:vtm]).to eq(vtm_url) + end end - it 'sets the vtm' do - identity.vtr = 'Pb' - expect(decoded_payload[:vtm]).to eq(vtm_url) + context 'sp does not request vot' do + before do + allow(IdentityConfig.store).to receive(:use_vot_in_sp_requests). + and_return(false) + allow(IdentityConfig.store).to receive(:vtm_url). + and_return(vtm_url) + end + + it 'does not set the vot if the sp does not request it' do + identity.vtr = 'Pb' + expect(decoded_payload[:vot]).to eq nil + end + + it 'does not set the vtm' do + identity.vtr = nil + expect(decoded_payload[:vtm]).to eq nil + end end end From 39e208e014fc0e4c1728117e6b8c63194127785e Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Mon, 26 Feb 2024 13:47:52 -0500 Subject: [PATCH 11/13] revamp acr method to return correct ial acr values --- app/services/id_token_builder.rb | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/app/services/id_token_builder.rb b/app/services/id_token_builder.rb index af1196d3a83..cf582448c21 100644 --- a/app/services/id_token_builder.rb +++ b/app/services/id_token_builder.rb @@ -58,13 +58,14 @@ def timestamp_claims def acr return nil unless identity.acr_values.present? - acr_ial_component_values.map do |component_value| - if component_value == Vot::LegacyComponentValues::IALMAX - determine_ial_max_acr.name - else - component_value.name - end - end.join(' ') + + if resolved_authn_context_result.ialmax? + determine_ial_max_acr.name + elsif resolved_authn_context_result.identity_proofing? + Vot::LegacyComponentValues::IAL2.name + else + Vot::LegacyComponentValues::IAL1.name + end end def sp_requests_vot? @@ -77,12 +78,6 @@ def vot resolved_authn_context_result.component_values.map(&:name).join('.') end - def acr_ial_component_values - resolved_authn_context_result.component_values.select do |component_value| - component_value.name.include?('ial') - end - end - def determine_ial_max_acr if identity.user.identity_verified? Vot::LegacyComponentValues::IAL2 From 0b1ce29f789954521e37745774c7687e8207ebe3 Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Mon, 26 Feb 2024 16:33:06 -0500 Subject: [PATCH 12/13] restart code climate From 89293b545de0da46ff35503e1dabc2b545827680 Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Tue, 27 Feb 2024 09:26:43 -0500 Subject: [PATCH 13/13] trying code climate again