Page MenuHomePhabricator

Require MFA implementations to return a formal result object when validating factors

Authored by epriestley on Dec 13 2018, 8:41 PM.



Ref T13222. See PHI873. Currently, MFA implementations return this weird sort of ad-hoc dictionary from validation, which is later used to render form/control stuff.

I want to make this more formal to handle token reuse / session binding cases, and let MFA factors share more code around challenges. Formalize this into a proper object instead of an ad-hoc bundle of properties.

Test Plan
  • Answered a TOTP MFA prompt wrong (nothing, bad value).
  • Answered a TOTP MFA prompt properly.
  • Added new TOTP MFA, survived enrollment.

Diff Detail

rP Phabricator
Automatic diff as part of commit; lint not applicable.
Automatic diff as part of commit; unit tests not applicable.

Event Timeline

epriestley created this revision.Dec 13 2018, 8:41 PM
epriestley requested review of this revision.Dec 13 2018, 8:42 PM
amckinley accepted this revision.Dec 13 2018, 9:17 PM
amckinley added inline comments.

Does this type checking live here because someone might have added their own implementation of PhabricatorAuthFactor?


I'm assuming this was intentional, and that PhabricatorAuthFactorResult.hint is really more of an error-hint.

This revision is now accepted and ready to land.Dec 13 2018, 9:17 PM
epriestley added inline comments.Dec 13 2018, 9:22 PM

Yeah, this is just a guard rail in case someone has another MFA implementation somewhere that's expecting it can return a string or a picture of a dog or something.


"Hint" may not be the best name, but I'm trying to distinguish between "hint next to the input about what's wrong with the thing you input" and "big error message in a banner". Not 100% sure where things are headed yet so this might change. If I don't need both, I'll likely change this to setError(...).

D19886 ended up renaming "Hint" to "Error Message" and pushing the instanceof X logic into the abstract base AuthFactor class, using this sorta thing:

final public function getX() {
  $x = $this->newX();
  if ($x is bad) { cry };
  return $x;

abstract protected function newX();

...since that seemed cleaner by the time I got things working.

This revision was automatically updated to reflect the committed changes.