Currently the module defines the concept of a Send Plugin but does not implement it.

The idea of a Send Plugin is an interface that can identify a Validation Plugins as being able to deliver 2nd-factor codes.

Todo:

  • Tease out TfaSendInterface.php. Is begin() the only necessary method?
  • Un-comment code related to the Send Plugins during Validation and other processes throughout the code base.
  • Write a fake Send Plugin for the tfa_test_plugins test module, and ensure there is a working test that leverages it.

Need to consider and handle the following cases for when a validation plugin's begin() method should be executed:

  1. if the user closes the browser or logs in again then they should get the code again.
  2. if the user goes to another TFA plugin form and comes back they should not get the code again.
  3. if the user refreshes the page they should not get the code again.
  4. if the user hits the re-send button then they should get the code again.

This work will likely happen as a group with these two issues:

  1. (this issue) #2930532: Create the Send Plugin API with a working test
  2. #2930541: Create "Email one-time-code" Validation Plugin & related Setup Plugin

Issue fork tfa-2930532

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

daggerhart created an issue. See original summary.

daggerhart’s picture

Category: Bug report » Task
Issue summary: View changes
Parent issue: » #2928728: Remove Totp and Hotp validation plugins after they are moved to ga_login
daggerhart’s picture

Title: Complete the Send Plugin API with a working test » Create the Send Plugin API with a working test
cslevy’s picture

StatusFileSize
new7.43 KB

Hi, I created a patch with the Send Plugin API. I didn't have time to handle the test as well.
Here is my patch

jonas139’s picture

Assigned: Unassigned » jonas139
jonas139’s picture

Assigned: jonas139 » Unassigned
brentschuddinck’s picture

Assigned: Unassigned » brentschuddinck
kalpaitch’s picture

StatusFileSize
new11.04 KB

In regards to: * Tease out TfaSendInterface.php. Is begin() the only necessary method?

I'm not sure begin() is the right method even. I see send() as the main method, the reason for this is that it doesn't have to happen at the beginning of the validation process. Most send plugins will use a 'resend' button and having this fire the begin() method all over again seems potentially odd.

Or it might be that a user has two methods enabled, but an authenticator otp code is the default, in this situation you might not want to send unnecessary codes until the user explicitly choose the SMS verification, or some other time of a user's choosing.

On top of this I'm wondering if there aren't things that happen once at the beginning but not when re-sending a code, or vice versa.

Anyway, re-rolled the patch and added some tests to start with.

daggerhart’s picture

@kalpaitch Thanks for taking a look at this.

I haven't had the time to look at implementing this API thoughtfully, which is why it's not implemented, but I like your line of thinking about the begin() vs send() methods and general needs for this new API.

If you want to propose the API as you think it should be done, I'm all ears and welcome the discussion. I believe the begin() method is how the send api in tfa 7.x is implemented, which is the only reason why I bring it up at all. We don't have to follow that approach if we think something else makes more sense.

kalpaitch’s picture

StatusFileSize
new1.85 MB

I'm going to add this comment here although it is broader than just the send plugin. I see a few things holding back the send plugins with the current setup, let me know what you think...

  1. There should be a way for setup plugins to validate against the appropriate validate plugin immediately after being configured (perhaps we should consider splitting the setup routes into configuration and validation, or build in multi step support to the setup plugin forms). Currently the ga_login totp plugins seem to import the validation form into the setup form which doesn't work well when you have to configure the setup plugin _before_ it can be validated e.g. adding a phone or email to send the codes to or even adding a user configurable string to generate the totp code.
  2. It seems to me that the begin method of a send plugin should be fired the moment the relevant validation plugin is instantiated and the validation form retrieved. There are plenty of instances where a user might enable multiple second factor methods on their account and we would only want the begin methods for each one to be fired when that validation plugin was instantiated e.g. if totp is their preferred method we don't want to send unnecessary text messages or emails to them.

I've attached a little ditty scribble to try and get across these thoughts.
Login and setup flow

mwebaze’s picture

@kalpaitch I have created a patch that can be used to send codes to email but also validate and also remove them incase they haven't been used with-in a specific period of time. I am open to helping out with this initiative.

mwebaze’s picture

StatusFileSize
new15.52 KB

I just realized patch 'create_the_send_plugin_api-2930532-11.patch' is an empty file. Please find attached create_the_send_plugin_api-2930532-12.patch

Thanks

mwebaze’s picture

StatusFileSize
new29.26 KB

An updated patch. The last one missed out some new files I had added.

mwebaze’s picture

Status: Active » Needs review

Patch create_the_send_plugin_api-2930532-13.patch is ready for review.

kalpaitch’s picture

That's awesome, thanks that would be awesome, I'm sure we can pick the better parts of each patch. Before I get started though I think my comments from #2930532-10: Create the Send Plugin API with a working test still stand:

  1. Should there not be a unified way for setup plugins to validate against the appropriate validate plugin immediately after being configured?
  2. Should the begin method of a send plugin be fired the moment the relevant validation form is retrieved?

IMO these should both to be implemented, but the first point will require a re-work to how existing plugins (ga_login) validate, happy to provide these patches too though. @daggerhart what are your thoughts?

daggerhart’s picture

@mwebaze thanks for the patch! This looks like a good place to get this patch moving with a working email plugin. I'm still reviewing the patch atm and will report back shortly.

@kalpaitch Yes to number 1, I think there should be a unified way to validate the plugin as soon as a user enables it for their account. In the case of ga_login, this is somewhat already happening since the setup of the user's authenticator is required to enable the plugin on their account. But that step isn't technically the same kind of validation you're talking about, and in other cases that step may not be as obvious and the tfa module would benefit from providing that immediate validation step. I like your idea for splitting the setup route into 2 distinct steps, one for configuration (setup plugin), and another for validation immediately after setup (validation plugin). In that scenario, we should require successful validation before the setup is considered to be complete and the plugin enabled for the account.

Regarding #2, yes! I completely agree that the send plugins should only `begin()` (or equivalent method) when the validation form is retrieved.

daggerhart’s picture

Issue summary: View changes

@mwebaze This is really great, thanks! It looks like you started with the #8 patch, and worked from there. That is awesome and made half of this patch very easy to review. I've done a bit of manual testing and see that the email plugin works as well!

I think our next steps are to split this up into its three parts (noted below) and roll new patches for each part. IMO that will facilitate discussion and updates in a good way. Here we can focus on the ideal send plugin API, and the other issues can focus on the ideals for their parts. I'm going to make a pass at splitting this up immediately after I post this comment to facilitate moving that process forward.

1. (this issue) #2930532: Create the Send Plugin API with a working test
2. #2930537: Create an Email Send Plugin and test
3. #2930541: Create "Email one-time-code" Validation Plugin & related Setup Plugin

daggerhart’s picture

StatusFileSize
new16.36 KB

Attached is my pass at rerolling this focused on the send api only. I've made a few changes to the configuration name, added a simple test send plugin, and updated the tests to pass (hopefully).

@kalpaitch, this should look very familiar to you because @mwebaze started his work on top of your patch.

I'll update the other two issues soon.

Feedback always welcome.

daggerhart’s picture

edit: removed previous comment because it was over complicated the approach... see below

mwebaze’s picture

The ValidationSendableInterface is a good idea but I was wondering the usefulness of getSendPlugin() function. My thinking is that it would be applicable to any validation plugin a user has enabled. Also how do we use this with a service such as Authy in which the code is generated by another service?

daggerhart’s picture

edit: deleted comment because it was overcomplicating the approach... see below

daggerhart’s picture

This morning I realized I needed to look back at the D7 version of the module for a little guidance here and found that I had been over complicating the approach needed for Send plugins.

Looking at an example of a send plugin in tfa_basic, it's not seperate from a validation plugin -- https://git.drupalcode.org/project/tfa_basic/blob/7.x-1.x/includes/tfa_s...

That's makes a lot of sense, answers my questions, and makes it easy to move forward. Let's do that. I'm going to re-roll some patches and update related issues.

daggerhart’s picture

Issue summary: View changes
daggerhart’s picture

StatusFileSize
new13.62 KB
new19.71 KB

I believe the confusion came from the D7 tfa module using the word "plugin" to mean "another means of extension for the tfa module". When the module was ported to D8, all the D7 tfa "plugin" concepts got brought over as D8 plugins. In most cases this was correct, but in the case of the Send plugins, this was unneccesary.

A send plugin is an interface that is applied to a validation plugin (it even says so in TfaSendInterface). It's not a unique type of thing that needs to be retrieved and managed by D8 independent of a validation plugin. All send plugins are validation plugins.

Apologies for my part in this confusion. I've certainly added to it.

Here is an updated patch that cleans up a Lot of the previous confusion around the Send plugin api. It basically removes the whole concept of a new type of Drupal 8 plugin that is a TFA "Send plugin".

The patch is failing 1 test (I predict), but I'm out of time this morning on tracking it down. Aside from that this is ready for review. If someone has time to review and fix the test that's great! Otherwise I'll continue working on it when I have time.

Remaining TODO:

- We need to get the begin() process happening in either EntryForm or TfaLoginForm. TfaLoginForm has a begin() method currently, but it is not being called anywhere I can see.

daggerhart’s picture

StatusFileSize
new18.18 KB
new6.1 KB

Fixing tests, and moving the "begin()" concept into the EntryForm, and renamed the TfaSendInterface to TfaValidationSendInterface to clarify its purpose.

Ideally it would be nice if this control over the begin() concept could live in the TfaLoginForm, but due to how users can change which plugin they are using to validate in the EntryForm, the EntryForm must be responsible for the begin() method for now.

One thing to note is that the system needs to know whether or not the begin() method has been executed for a validation plugin, otherwise it will send an email/sms everytime the form is loaded. For the sake of getting this patch working I used session data, but I really don't like that approach and think we need a better mechanism. I tried setting it as a form_state value, but didn't have any luck with programmatically set values persisting through multiple form loads. Any thoughts?

daggerhart’s picture

Assigned: brentschuddinck » Unassigned
kalpaitch’s picture

I think this approach makes a lot more sense. I was going to say that send plugins are tied to a validation plugin in a one to one relationship, and this is probably the best way of representing this.

In terms of only sending the code once per form, I suspect we could either cache the form with the session context or store the hashed code in the session manager.

I agree that the begin() method is best suited to the EntryForm. Not that it bothers me either way but I don't see the need to change the send interface's name, while they might be part of the same plugin they do different things and naming the send interface off the validation interface might misrepresent it's function. But don't think it matters either way.

kalpaitch’s picture

It's probably worth identifying when the validation plugin should begin again:
if the user closes the browser or logs in again then they should get the code again.
if the user goes to another TFA plugin form and comes back they should not get the code again.
if the user refreshes the page they should not get the code again.
if the user hits the re-send button then they should get the code again.

I think you're right to say that based on this the session is the only appropriate gate keeper for whether the resend the code.

If you don't want to set a variable in the session directly perhaps we could cache the method call with the session context. I briefly messed around with having a lazy loaded element on the validation form that calls begin(), really loose and experimental idea but:

public function begin($session) {
  if ($cache = \Drupal::cache($session->id())) {
    return $cache;
  }
  else {
    $this->send();

    return [
      '#markup' => 'Render ajax button calls send() method',
    ];
  }
}
daggerhart’s picture

Issue summary: View changes

Good point about needing to know when a validation plugin should begin again, and I think your list is a great start. I will update the issue description with your list so that it will be easy to refer to.

One thing to note is that I don't believe there is a "resend" button of any type implemented yet, but we do probably need this. Do you think we can make an assumption that across-the-board any time we're dealing with a plugin that implements the ValidationSendInterface, we provide a Re-send button? It makes sense to me, but I haven't sat down to try to come up with a scenario in which a validation plugin would not want a re-send button.

Regarding the data storage, @mwebaze suggested the `tempstore.private` service in slack (https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21TempStore...) and I liked that idea. It uses the session and handles edge cases when dealing with anonymous users (which is what we're always dealing with).

kalpaitch’s picture

So long as the PrivateTempStore doesn't persist the session, which I guess it shouldn't for anonymous users (but does for identified users), then this probably is better, worth checking this explicitly I guess.

A second point to make is that currently we're setting the `dont' send marker` in the session when the plugin sends out the code (and then we remove it when the form is submitted. I wonder how this functions when the user doesn't submit the validation form and instead hits the back button and logs in again for example.

Perhaps a more hardy approach would be to set a `to be sent marker` on every plugin whenever the login form is submitted (this appears to be our refresh marker), this way the plugin can remove the marker as appropriate when it issues the code. At this point the only thing that could issue another code would be logging in again.

This might resolve issues like above where the persistence of the cache could be a problem.

kalpaitch’s picture

StatusFileSize
new9.3 KB
new19.92 KB

Ok so I've made an attempt at updating this with the PrivateTempStore and flagging each ValidationSendPlugin as 'ready to send' upon login.

I'm still unsure as to how best to handle the creation of a re-send button. I feel that this should be consistent and therefore generated by a single method, however, I think it's something that we really should be able to override within a given validation plugin.

The obvious way to do this would be to have the base implementation defined in the a validation plugin base class, however, there doesn't seem to be a specific base class for each plugin which seems strange. Perhaps worth considering why all these plugins share a single base class, and perhaps whether there is another way for each plugin to share common logic like code generation etc.

kalpaitch’s picture

Oops, I don't know why that last patch ignored some of the test files. Will make sure to get them back in the next patch.

mattdanger’s picture

StatusFileSize
new20.27 KB
new2.38 KB

Attached is a bug fix for the previous patch that fixes errors with the PrivateTempStore dependency.

Status: Needs review » Needs work

The last submitted patch, 33: create_send_api-2930532-33.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

piggito’s picture

Status: Needs work » Needs review
StatusFileSize
new22.1 KB

Re-rolled and added some fixes + added back the removed tests.

Status: Needs review » Needs work

The last submitted patch, 35: 2930532-35.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

piggito’s picture

Status: Needs work » Needs review
StatusFileSize
new22.13 KB
new3.73 KB

Fixing private temp store issue among other fixes.

wadator’s picture

StatusFileSize
new21.6 KB

Rerolled patch #37 by lattest module version.

Status: Needs review » Needs work

The last submitted patch, 38: 2930532-38.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

cantrellnm’s picture

Status: Needs work » Needs review
StatusFileSize
new22.17 KB

#38 reroll was missing changes to the BasicOverview form

rylan-michael made their first commit to this issue’s fork.

cameron prince’s picture

StatusFileSize
new23.96 KB

Re-roll against the latest dev.

Status: Needs review » Needs work

The last submitted patch, 42: 2930532-42.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

awset’s picture

StatusFileSize
new23.97 KB

The #42 patch failed to apply, provided a re-roll for the same.

jcnventura’s picture

Status: Needs work » Needs review
StatusFileSize
new0 bytes

Let's try to re-roll again.

jcnventura’s picture

StatusFileSize
new23.89 KB

Now with some bytes in the patch file.

Status: Needs review » Needs work

The last submitted patch, 46: 2930532-46.patch, failed testing. View results

jcnventura’s picture

Version: 8.x-1.x-dev » 2.x-dev
Related issues: +#3314265: Unify the plugins

This now seems to be a task for the 2.x branch which will use a single plugin type with different interfaces to distinguish the personalities. And looking at the proposed changes here I'm not even sure if there really should be a 'send' plugin, as it seems to be just a validation plugin with the 'begin' method.