Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 52 additions & 3 deletions app/forms/openid_connect_authorize_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class OpenidConnectAuthorizeForm
ATTRS = [
:unauthorized_scope,
:acr_values,
:vtr,
:scope,
:verified_within,
:biometric_comparison_required,
Expand All @@ -37,7 +38,7 @@ class OpenidConnectAuthorizeForm
RANDOM_VALUE_MINIMUM_LENGTH = 22
MINIMUM_REPROOF_VERIFIED_WITHIN_DAYS = 30

validates :acr_values, presence: true
validates :acr_values, presence: true, if: ->(form) { form.vtr.blank? }
validates :client_id, presence: true
validates :redirect_uri, presence: true
validates :scope, presence: true
Expand All @@ -49,6 +50,7 @@ class OpenidConnectAuthorizeForm
validates :code_challenge_method, inclusion: { in: %w[S256] }, if: :code_challenge

validate :validate_acr_values
validate :validate_vtr
validate :validate_client_id
validate :validate_scope
validate :validate_unauthorized_scope
Expand All @@ -59,6 +61,7 @@ class OpenidConnectAuthorizeForm

def initialize(params)
@acr_values = parse_to_values(params[:acr_values], Saml::Idp::Constants::VALID_AUTHN_CONTEXTS)
@vtr = parse_vtr(params[:vtr])
SIMPLE_ATTRS.each { |key| instance_variable_set(:"@#{key}", params[key]) }
@prompt ||= 'select_account'
@scope = parse_to_values(params[:scope], scopes)
Expand Down Expand Up @@ -119,15 +122,27 @@ def ial_context
end

def ial
Saml::Idp::Constants::AUTHN_CONTEXT_CLASSREF_TO_IAL[ial_values.sort.max]
if parsed_vector_of_trust&.identity_proofing?
2
elsif parsed_vector_of_trust.present?
1
else
Saml::Idp::Constants::AUTHN_CONTEXT_CLASSREF_TO_IAL[ial_values.sort.max]
end
end

def aal_values
acr_values.filter { |acr| acr.include?('aal') }
end

def aal
Saml::Idp::Constants::AUTHN_CONTEXT_CLASSREF_TO_AAL[requested_aal_value]
if parsed_vector_of_trust&.aal2?
2
elsif parsed_vector_of_trust.present?
1
else
Saml::Idp::Constants::AUTHN_CONTEXT_CLASSREF_TO_AAL[requested_aal_value]
end
end

def requested_aal_value
Expand Down Expand Up @@ -163,7 +178,18 @@ def parse_to_values(param_value, possible_values)
param_value.split(' ').compact & possible_values
end

def parse_vtr(param_value)
return if !IdentityConfig.store.use_vot_in_sp_requests
return if param_value.blank?

JSON.parse(param_value)
rescue JSON::ParserError
nil
end

def validate_acr_values
return if vtr.present?

if acr_values.empty?
errors.add(
:acr_values, t('openid_connect.authorization.errors.no_valid_acr_values'),
Expand All @@ -177,6 +203,15 @@ def validate_acr_values
end
end

def validate_vtr
return if vtr.blank?
return if parsed_vector_of_trust.present?
errors.add(
:vtr, t('openid_connect.authorization.errors.no_valid_vtr'),
type: :no_valid_vtr
)
end

# This checks that the SP matches something in the database
# OpenidConnect::AuthorizationController#check_sp_active checks that it's currently active
def validate_client_id
Expand Down Expand Up @@ -246,6 +281,7 @@ def extra_analytics_attributes
redirect_uri: result_uri,
scope: scope&.sort&.join(' '),
acr_values: acr_values&.sort&.join(' '),
vtr: vtr,
unauthorized_scope: @unauthorized_scope,
code_digest: code ? Digest::SHA256.hexdigest(code) : nil,
code_challenge_present: code_challenge.present?,
Expand Down Expand Up @@ -275,6 +311,19 @@ def scopes
OpenidConnectAttributeScoper::VALID_IAL1_SCOPES
end

def parsed_vector_of_trust
return @parsed_vector_of_trust if defined?(@parsed_vector_of_trust)
return @parsed_vector_of_trust = nil if vtr.blank?

@parsed_vector_of_trust = begin
if vtr.is_a?(Array) && !vtr.empty?
Vot::Parser.new(vector_of_trust: vtr.first).parse
end
rescue Vot::Parser::ParseException
nil
end
end

def validate_privileges
if (ial2_requested? && !ial_context.ial2_service_provider?) ||
(ial_context.ialmax_requested? &&
Expand Down
8 changes: 8 additions & 0 deletions app/models/federated_protocols/oidc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ def aal
request.aal_values.sort.max
end

def acr_values
[aal, ial].compact.join(' ')
end

def vtr
request.vtr
end

def requested_attributes
OpenidConnectAttributeScoper.new(request.scope).requested_attributes
end
Expand Down
8 changes: 8 additions & 0 deletions app/models/federated_protocols/saml.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ def aal
request.requested_aal_authn_context
end

def acr_values
[aal, ial].compact.join(' ')
end

def vtr
nil
end

def requested_attributes
@requested_attributes ||= SamlRequestPresenter.new(
request: request, service_provider: current_service_provider,
Expand Down
3 changes: 3 additions & 0 deletions app/services/analytics_events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3636,12 +3636,14 @@ def openid_connect_bearer_token(success:, ial:, client_id:, errors:, **extra)
# @param [String] client_id
# @param [String] scope
# @param [Array] acr_values
# @param [Array] vtr
# @param [Boolean] unauthorized_scope
# @param [Boolean] user_fully_authenticated
def openid_connect_request_authorization(
client_id:,
scope:,
acr_values:,
vtr:,
unauthorized_scope:,
user_fully_authenticated:,
**extra
Expand All @@ -3651,6 +3653,7 @@ def openid_connect_request_authorization(
client_id: client_id,
scope: scope,
acr_values: acr_values,
vtr: vtr,
unauthorized_scope: unauthorized_scope,
user_fully_authenticated: user_fully_authenticated,
**extra,
Expand Down
2 changes: 2 additions & 0 deletions app/services/service_provider_request_handler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ def attributes
issuer: protocol.issuer,
ial: protocol.ial,
aal: protocol.aal,
acr_values: protocol.acr_values,
vtr: protocol.vtr,
requested_attributes: protocol.requested_attributes,
biometric_comparison_required: protocol.biometric_comparison_required?,
uuid: request_id,
Expand Down
2 changes: 2 additions & 0 deletions app/services/store_sp_metadata_in_session.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ def update_session
request_id: sp_request.uuid,
requested_attributes: sp_request.requested_attributes,
biometric_comparison_required: sp_request.biometric_comparison_required,
acr_values: sp_request.acr_values,
vtr: sp_request.vtr,
}
end

Expand Down
2 changes: 2 additions & 0 deletions config/application.yml.default
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ team_ursula_email: ''
test_ssn_allowed_list: ''
totp_code_interval: 30
unauthorized_scope_enabled: false
use_vot_in_sp_requests: true
usps_upload_enabled: false
usps_upload_sftp_timeout: 5
valid_authn_contexts: '["http://idmanagement.gov/ns/assurance/loa/1", "http://idmanagement.gov/ns/assurance/loa/3", "http://idmanagement.gov/ns/assurance/ial/1", "http://idmanagement.gov/ns/assurance/ial/2", "http://idmanagement.gov/ns/assurance/ial/0", "http://idmanagement.gov/ns/assurance/ial/2?strict=true", "urn:gov:gsa:ac:classes:sp:PasswordProtectedTransport:duo", "http://idmanagement.gov/ns/assurance/aal/2", "http://idmanagement.gov/ns/assurance/aal/3", "http://idmanagement.gov/ns/assurance/aal/3?hspd12=true","http://idmanagement.gov/ns/assurance/aal/2?phishing_resistant=true","http://idmanagement.gov/ns/assurance/aal/2?hspd12=true"]'
Expand Down Expand Up @@ -494,6 +495,7 @@ production:
state_tracking_enabled: false
telephony_adapter: pinpoint
use_kms: true
use_vot_in_sp_requests: false
usps_auth_token_refresh_job_enabled: true
usps_confirmation_max_days: 30
usps_upload_sftp_directory: ''
Expand Down
1 change: 1 addition & 0 deletions config/locales/openid_connect/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ en:
no_auth: The acr_values are not authorized
no_valid_acr_values: No acceptable acr_values found
no_valid_scope: No valid scope values found
no_valid_vtr: No acceptable vots found
prompt_invalid: No valid prompt values found
redirect_uri_invalid: redirect_uri is invalid
redirect_uri_no_match: redirect_uri does not match registered redirect_uri
Expand Down
1 change: 1 addition & 0 deletions config/locales/openid_connect/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ es:
no_auth: Los acr_values no están autorizados
no_valid_acr_values: ial_valores encontrados no aceptables
no_valid_scope: No se han encontrado valores de magnitud válidos
no_valid_vtr: vots encontrados no aceptables
prompt_invalid: Prompt no es válido
redirect_uri_invalid: Redirect_uri no es válido
redirect_uri_no_match: Redirect_uri no coincide con redirect_uri registrado
Expand Down
1 change: 1 addition & 0 deletions config/locales/openid_connect/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ fr:
no_auth: Les acr_values ne sont pas autorisées
no_valid_acr_values: Valeurs acr_values inacceptables trouvées
no_valid_scope: Aucune étendue de données valide trouvée
no_valid_vtr: vots encontrados no aceptables
prompt_invalid: prompt est non valide
redirect_uri_invalid: redirect_uri est non valide
redirect_uri_no_match: redirect_uri ne correspond pas au redirect_uri enregistré
Expand Down
1 change: 1 addition & 0 deletions lib/identity_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,7 @@ def self.build_store(config_map)
config.add(:unauthorized_scope_enabled, type: :boolean)
config.add(:use_dashboard_service_providers, type: :boolean)
config.add(:use_kms, type: :boolean)
config.add(:use_vot_in_sp_requests, type: :boolean)
config.add(:usps_auth_token_refresh_job_enabled, type: :boolean)
config.add(:usps_confirmation_max_days, type: :integer)
config.add(:usps_ipp_client_id, type: :string)
Expand Down
26 changes: 18 additions & 8 deletions spec/controllers/openid_connect/authorization_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@
acr_values: 'http://idmanagement.gov/ns/assurance/ial/1',
code_challenge_present: false,
service_provider_pkce: nil,
scope: 'openid')
scope: 'openid',
vtr: nil)
expect(@analytics).to receive(:track_event).
with('OpenID Connect: authorization request handoff',
success: true,
Expand Down Expand Up @@ -257,7 +258,8 @@
acr_values: 'http://idmanagement.gov/ns/assurance/ial/2',
code_challenge_present: false,
service_provider_pkce: nil,
scope: 'openid profile')
scope: 'openid profile',
vtr: nil)
expect(@analytics).to receive(:track_event).
with('OpenID Connect: authorization request handoff',
success: true,
Expand Down Expand Up @@ -495,7 +497,8 @@
acr_values: 'http://idmanagement.gov/ns/assurance/ial/0',
code_challenge_present: false,
service_provider_pkce: nil,
scope: 'openid profile')
scope: 'openid profile',
vtr: nil)
expect(@analytics).to receive(:track_event).
with('OpenID Connect: authorization request handoff',
success: true,
Expand Down Expand Up @@ -578,7 +581,8 @@
acr_values: 'http://idmanagement.gov/ns/assurance/ial/0',
code_challenge_present: false,
service_provider_pkce: nil,
scope: 'openid profile')
scope: 'openid profile',
vtr: nil)
expect(@analytics).to receive(:track_event).
with('OpenID Connect: authorization request handoff',
success: true,
Expand Down Expand Up @@ -663,7 +667,8 @@
acr_values: 'http://idmanagement.gov/ns/assurance/ial/0',
code_challenge_present: false,
service_provider_pkce: nil,
scope: 'openid profile')
scope: 'openid profile',
vtr: nil)
expect(@analytics).to receive(:track_event).
with('OpenID Connect: authorization request handoff',
success: true,
Expand Down Expand Up @@ -865,7 +870,8 @@
acr_values: 'http://idmanagement.gov/ns/assurance/ial/1',
code_challenge_present: false,
service_provider_pkce: nil,
scope: 'openid')
scope: 'openid',
vtr: nil)
expect(@analytics).to_not receive(:track_event).with('sp redirect initiated')

action
Expand Down Expand Up @@ -898,7 +904,8 @@
acr_values: 'http://idmanagement.gov/ns/assurance/ial/1',
code_challenge_present: false,
service_provider_pkce: nil,
scope: 'openid')
scope: 'openid',
vtr: nil)
expect(@analytics).to_not receive(:track_event).with('SP redirect initiated')

action
Expand Down Expand Up @@ -1013,7 +1020,8 @@
acr_values: 'http://idmanagement.gov/ns/assurance/ial/1',
code_challenge_present: false,
service_provider_pkce: nil,
scope: 'openid')
scope: 'openid',
vtr: nil)

action
sp_request_id = ServiceProviderRequestProxy.last.uuid
Expand All @@ -1028,6 +1036,7 @@

expect(session[:sp]).to eq(
aal_level_requested: nil,
acr_values: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF,
piv_cac_requested: false,
phishing_resistant_requested: false,
ial: 1,
Expand All @@ -1038,6 +1047,7 @@
request_url: request.original_url,
requested_attributes: %w[],
biometric_comparison_required: false,
vtr: nil,
)
end

Expand Down
16 changes: 16 additions & 0 deletions spec/controllers/saml_idp_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1166,6 +1166,12 @@ def name_id_version(format_urn)
end

context 'POST to auth correctly stores SP in session' do
let(:acr_values) do
Saml::Idp::Constants::DEFAULT_AAL_AUTHN_CONTEXT_CLASSREF +
' ' +
Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF
end

before do
@user = create(:user, :fully_registered)
@saml_request = saml_request(saml_settings)
Expand All @@ -1181,6 +1187,7 @@ def name_id_version(format_urn)
issuer: saml_settings.issuer,
aal_level_requested: aal_level,
piv_cac_requested: false,
acr_values: acr_values,
phishing_resistant_requested: false,
ial: 1,
ial2: false,
Expand All @@ -1189,6 +1196,7 @@ def name_id_version(format_urn)
request_id: sp_request_id,
requested_attributes: ['email'],
biometric_comparison_required: false,
vtr: nil,
)
end

Expand All @@ -1201,6 +1209,12 @@ def name_id_version(format_urn)
end

context 'service provider is valid' do
let(:acr_values) do
Saml::Idp::Constants::DEFAULT_AAL_AUTHN_CONTEXT_CLASSREF +
' ' +
Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF
end

before do
@user = create(:user, :fully_registered)
@saml_request = saml_get_auth(saml_settings)
Expand All @@ -1212,6 +1226,7 @@ def name_id_version(format_urn)
expect(session[:sp]).to eq(
issuer: saml_settings.issuer,
aal_level_requested: aal_level,
acr_values: acr_values,
piv_cac_requested: false,
phishing_resistant_requested: false,
ial: 1,
Expand All @@ -1221,6 +1236,7 @@ def name_id_version(format_urn)
request_id: sp_request_id,
requested_attributes: ['email'],
biometric_comparison_required: false,
vtr: nil,
)
end

Expand Down
Loading