Skip to content

Add flexible biometric proofing service selection (OIDC)#10948

Closed
lmgeorge wants to merge 3 commits intomainfrom
lmgeorge/acr-values-resolution-oidc
Closed

Add flexible biometric proofing service selection (OIDC)#10948
lmgeorge wants to merge 3 commits intomainfrom
lmgeorge/acr-values-resolution-oidc

Conversation

@lmgeorge
Copy link
Contributor

@lmgeorge lmgeorge commented Jul 16, 2024

Why?

This is the initial work to replace our public-facing Vectors of Trust (VOT) API interface(s).

See: https://gitlab.login.gov/lg-people/lg-people-appdev/Melba/backlog-fy24/-/issues/39

How?

  • Maintains the current VoT implementation and API for current SPs
  • Adds support for selecting proofing with biometric comparison using new Authentication Context Class Reference values (ACR values) in our OIDC API
  • Unlike IALMax, the new ACR values are not a step-up flow. Instead, SPs will be able to fallback to a lower level of identity proofing for their current users that have successfully proofed in the past.

[skip changelog]

🛠 Summary of changes

The primary changes live in AuthnContextResolver.

It mimics the service selection logic of #selected_vtr_parser_result_from_vtr_list with the important caveat of only handling the ial2?bio=preferred ACR value. The AAL and other IAL ACR values are handled the same as before.

Note: ACR values are no longer sorted during resolution.

No new logic was added to replace/refactor how IAL ACR values are used except in OpenidConnectUserInfoPresenter, where the actual identity proofing ACR value is used instead of always mapping to IAL2_AUTHN_CONTEXT_CLASSREF.

@lmgeorge lmgeorge self-assigned this Jul 16, 2024
Copy link
Contributor

@Sgtpluck Sgtpluck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm still working through the changes, but here are some nitpicks to start

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same Data.define comment as above

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the same case can be made here given how complex it is already.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably should have an initializer as well. Thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if it's being turned into a class, then yes i think we should have a real initializer rather than using Data.define. my personal preference would be to leave it alone for this PR, but the complexity comment is valid, so up to you whether to pull out the initializer or revert it to a class-like object!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, sorry thinking through this a little more:

as mentioned, the Data class was explicitly defined for use on class-like objects. it was not designed to be used in a class inheritance way as is being used here. if we wanted to fully convert the class, that would require removing this and updating all the references from using result.with.

i do not think we should do that as part of this PR (which is complicated enough already), so i'd like to more strongly encourage reverting this to Result = Data.define for the purposes of this PR, and adding a ticket to evaluate this more fully as part of next steps. i don't think converting this into a full class has any particularly strong benefits for the purpose of this change.

Copy link
Contributor

@vrajmohan vrajmohan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made a first pass at a code review.

@lmgeorge lmgeorge force-pushed the lmgeorge/acr-values-resolution-oidc branch from 87c7eac to 5854808 Compare July 17, 2024 22:29
Copy link
Contributor Author

@lmgeorge lmgeorge Jul 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If :biometric_comparison is marked as false, then :two_pieces_of_fair_evidence should also be false and vice versa.

(This is partially why I started with string substitution, so I could avoid having that type of business logic in the resolver.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah great point! we can add that while we pair (or feel free to add it while applying other changes)

that makes sense, although having had a chat with hooper, it sounds like he intended the resolver to be the place to put that kind of logic, so that we can flexibly apply things depending on user/service provider (maybe other) context when necessary. let's definitely talk about the approach, i ended up changing more than i thought i was going to b/c i ran with hooper's suggestion and i want to make sure you're comfortable with it!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I'll make those changes and see if anything breaks (😅)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this method has gotten a lot more complicated with the inclusion of the meta-programming. i understand the desire to be explicit about what's in the result, but this has become harder for me to reason about in the process.

my personal preference would be closer to my original implementation (where we were just changing the values that needed to be changed, since we know, for instance, if the result is asking for biometric_comparison and it's required, we are not doing any data transformation on the result).

however, if you want to avoid making assumptions/keeping it explicit (which is fair), i would remove the self.method call and just call the methods directly. ruby automatically returns the last statement, so the correct result will be returned. the final line could be no_identity_proofing_acr_result(result) in order to capture any cases not found. this will make this method more direct and easier to reason about (imo).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

other things i personally might consider is pulling all the asserted_ial_acr logic into a separate decorator (decorate_result_with_ial_acr), but that may be an optimization for another day.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with Davida that the meta-programming may be a bit much for this implementation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re: meta-programming. That's fair, I was trying to solve for not having a million base case statements returning the IAL1 acr result. I'll try a different approach.

I don't know that it would be feasible to move the asserted_ial_acr logic separate from the updating the result attributes as they are tightly coupled.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i had a thought: this asserted_ial_acr is explicilty for dermining the return ial value.

for the purposes of actually answering the question "what is info[:ial]", we'll never be answering that question unless there is no identity profile. which means we should only be figuring out what the asserted_ial_acr if it's identity proofed. so i think we should be able to make this a lot less complicted knowing that use case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had forgotten while in the weeds of the code that the vtr param is used as a logic gate. A number of specs were failing because I wasn't looking at the full resolution graph of for biometric proofing, but rather only places where AuthnContextResolver#resolve and Vot::Parser::Result were being directly utilized.
I changed the behavior of the resolver with that narrow focus in mind, which meant absorbing some business logic around IAL level assertion.

I'm going to unpublish the PR and attempt to streamline the changes further.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did this type value get added?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a thought about the reporting on this, but it doesn't seem needed, so will remove.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I am using this class downstream to determine if I should perform biometric comparison what am I to make of biometric_comparison? and biometric_comparison_required?? Which one should I inspect to determine if I should perform biometric comparison or both? Is there something in the design of this that I am missing that should signal the answer to that question?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is that if the SP wants to require a user to complete proofing with biometric comparison (instead of preferring it when the user has already completed biometric comparison), this would be used as opposed to testing the asserted_ial_acr value directly. I can revert this as it seems to confuse the issue.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the only real change here adding default values for these (and perhaps some documentation)? If so, does that makes sense as a separate pull request so it does not distract from the crux of the change here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this REQUIREMENTS constant used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No where, I can remove this. I just needed to map the requirements in my head.

@lmgeorge lmgeorge marked this pull request as ready for review July 23, 2024 15:30
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please remove the commented out code!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please remove the commented out code

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please remove commented out code

Copy link
Contributor

@Sgtpluck Sgtpluck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm really excited about these changes! The things that need to happen for this to be ready to go for me are:

  1. removing commented out code
  2. remove metaprogramming from the decorator method

and i would strongly recommend:

  1. reverting the classes that were created out of the class-like objects to make this PR more compact and readable, along with a ticket to evaluate that change.

thanks! glad to see it's looking so good, excited to merge it in.

@lmgeorge lmgeorge marked this pull request as draft July 23, 2024 19:21
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the original implementation of info[:ial] only returned either Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF or Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF. we should not be changing that implementation, it may break partners expecting a specific value back

lmgeorge and others added 3 commits July 26, 2024 16:53
**Why?**

This is the initial work to replace our public-facing Vectors of Trust
(VOT) API interface(s).

See: https://gitlab.login.gov/lg-people/lg-people-appdev/Melba/backlog-fy24/-/issues/39

**How?**

* Maintains the current VoT implementation and API for current SPs
* Adds support for selecting proofing with biometric comparison using
  new Authentication Context Class Reference values (ACR values) in our OIDC API
* Unlike IALMax, the new ACR values are not a step-up flow. Instead, SPs
  will be able to fallback to a lower level of identity proofing for their
  current users that have successfully proofed in the past.

[skip changelog]

contributors: davida marion <davida.marion@gsa.gov>
contributor: davida marion <davida.marion@gsa.gov>

- Clean up some logic
- Fix tests
- Add AuthnContextResolver specs
- Experiment with decorator rather than string replacement
- Clean up method placement
- Add condition that checks if biometrics is required
- Store acr_results in in assertion logic instance variable for performance

- PR feedback: made component descriptions more specific and some rubocop fixes

- wip: updated IAL ACR assertion logic, fixed broken tests, rubocop

- wip: Fixed all broken tests except services/analytics.rb

- PR fixes: fixed analytics tests

- vtr handling updated
@lmgeorge lmgeorge force-pushed the lmgeorge/acr-values-resolution-oidc branch from 66e6418 to dda9131 Compare July 26, 2024 21:12
@lmgeorge lmgeorge marked this pull request as ready for review July 26, 2024 21:15
@lmgeorge lmgeorge closed this Jul 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants