• This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn more.

1.5: Documentation for two step authentication?

rugk

Active member
#1
Mike said in his post about the new 2FA in XenForo:
The two-step verification "provider" system can be extended by third-party developers to add different methods
Are there any more details about this? Or will there be?
I mean is there any documentation or example implementation of this? Or is planned that there will be one released?

Are there any special APIs at all?
 

Chris D

XenForo developer
Staff member
#2
To add new providers to the 2FA system is pretty much no different from how we have added the default ones.

An entry needs to be added to the provider database table, and a class needs to be created which extends XenForo_Tfa_AbstractProvider. Aside from that, some templates and phrases will be required for the user interface.

It's unlikely we will write any specific documentation for it though I'm certain that if you have any questions, ask them in this forum and they'll get answered.

Good luck!
 

rugk

Active member
#4
Okay, I have a question:
Is there any way to execute code when the add-on is disabled/enabled? And I know, this was asked before, but has something changed since 2012?

The issue is just: If you have an 2FA add-on installed and disable it I'm quite sure you want to disable the 2FA. (Well... I would highly assume this. :)) But since you cannot get any code to work when disabling (AFAIK) you can't modify the 2FA provider database and so you can't disable it there.
Finally the add-on just stays enabled. And that's obviously not what the user expects when he disables an add-on.
 
Last edited:

Chris D

XenForo developer
Staff member
#5
It's actually probably possible to run a bit of code on disable.

It would require extending the _postSave() function of the AddOn DataWriter. Have your code execute before the parent code executes (checking for the add-on ID, the active state, etc.). There's also the AddOn Model and rebuildAddOnCachesAfterActiveSwitch().

Actually the big problem is reverting that when the add-on is re-enabled. When the code I mentioned above is run, technically the add-on is still disabled so extending the code above won't work for the enable case. That's maybe where the idea of a Cron entry comes in. If it ran every minute (it would only run when the add-on is enabled) then you could get it to check that the providers are enabled, and enable them if they're not.
 

rugk

Active member
#6
Okay, sounds difficult and more like a workaround...

And about:
If it ran every minute (it would only run when the add-on is enabled)
If it runs every minute when the add-on is enabled it also runs every minute after the provider is enabled.
And I don't want to query the database every minute just to check whether the provider is actually enabled.

Okay, it would be possible if the cron entry is just disabled/deleted after itself 'corrected' the providers entry. And it would have to be readded/activated if the add-on disables. :unsure:
 

James

Well-known member
#7
Would it not be possible to enable the cron entry on postSave so when the addon is disabled the cron is enabled.

Then, in the cron, you can possibly disable the cron from inside the cron? So it runs the required code then disables itself so it doesn't run until the addon is disabled and the cron is re-enable on postSave?

(this is all theoretical I've not seen the code)
 

rugk

Active member
#8
Yes that's what I thought too:
Okay, it would be possible if the cron entry is just disabled/deleted after itself 'corrected' the providers entry. And it [= the cron entry] would have to be readded/activated if the add-on disables. :unsure:
So theoretically I think it should be possible.
But it's still a heavy workaround. But well... if there is no other method.
It's just that this workaround has to be in place for all 2FA add-ons as they all have the same problem.
 

rugk

Active member
#9
So the code for generating a random 6-digits number I found in your implementation is this one:
PHP:
$length = 6;

$random = XenForo_Application::generateRandomString(4, true);
$code = (
    ((ord($random[0]) & 0x7f) << 24) |
   ((ord($random[1]) & 0xff) << 16) |
  ((ord($random[2]) & 0xff) << 8) |
  (ord($random[3]) & 0xff)
) % pow(10, $length);
$code = str_pad($code, $length, '0', STR_PAD_LEFT);
I have to admit that I don't really know what you do and bitshift there, but as it seems to work quite good and such codes may be used in many 2FA methods I would like to ask whether I could use this code snipped in my add-on too. Note that I may publish it under an open source license.
 

Mike

XenForo developer
Staff member
#10
Well that code depends on an XF library function, but all you're seeing is how to turn a bit stream into an integer.
 

rugk

Active member
#12
Another question: There are requiresSetup and renderSetup in the abstract class. But as it is not used in any of the included 2FA methods I have no example code for implementing.
I've tried to create an template with $view->createTemplateObject in renderSetup (requiresSetup obviously returns true). However when accessing the site (enabling the 2FA provider) it only shows me a blank site. I got out that XenForo does not even execute renderSetup at all. (requiresSetup is executed)

So when looking in the XenForo code I saw that it all seems to go well until line 1204 in XenForo_ControllerPublic_Account. There I don't understand how it can call the class XenForo_ViewPublic_Account_TwoStepSetup - there is no file TwoStepSetup.php in the Account directory.
 
Last edited:

rugk

Active member
#15
The view class and doesn't actually have to exist as a PHP file.
That's not mentioned in https://xenforo.com/community/resources/understanding-the-xenforo-class-proxy-system.2080/...
Okay, so where does it load XenForo_ViewPublic_Account_TwoStepSetup? Respectively in what file is this class?

BTW "view class and" what?

And what about the original issue that renderSetup is not called at all?

BTW I'm using XF 1.5.2 so maybe a newer version contains some fixes. May this be an issue?
 

Chris D

XenForo developer
Staff member
#16
I've tried to create an template with $view->createTemplateObject in renderSetup (requiresSetup obviously returns true). However when accessing the site (enabling the 2FA provider) it only shows me a blank site. I got out that XenForo does not even execute renderSetup at all. (requiresSetup is executed)
You're right. These methods aren't actually implemented properly - they weren't needed and were just not used. Realistically, you'd probably be best to pretend they don't exist. At best, you might be able to piggy back the code here to do what you need to do.

So when looking in the XenForo code I saw that it all seems to go well until line 1204 in XenForo_ControllerPublic_Account. There I don't understand how it can call the class XenForo_ViewPublic_Account_TwoStepSetup - there is no file TwoStepSetup.php in the Account directory.
This is common within XF, as Brogan explains. View classes (the first argument of renderView()) don't actually need to exist (but theoretically you would be able to extend that non-existing class if required), so there's nothing really to see there. Part of what I was saying above is actually the "account_two_step_setup" template doesn't exist either so effectively this code here has no use at all.

You would be able to theoretically inject some behaviours here, but in a slightly more convoluted way than the code would lead you to believe.

For example, you could extend that controller action, e.g.
PHP:
public function actionTwoStepEnable()
{
    $parent = parent::actionTwoStepEnable();

    if ($parent instanceof XenForo_ControllerResponse_View)
    {
        // At this point, we should already be at the "TwoStepSetup" stage
        // You should get your provider here, and do the necessary steps to ensure it is valid and check it is your provider...

        if ($provider->provider_id == 'YourProviderId')
        {
            // Do whatever you need to do here to get your provider set up
        }
    }

    return $parent;
}
That needs fleshing out a bit, but hopefully you get the idea.
 

rugk

Active member
#17
You're right. These methods aren't actually implemented properly - they weren't needed and were just not used. Realistically, you'd probably be best to pretend they don't exist. At best, you might be able to piggy back the code here to do what you need to do.
:cry:
That's why we need a developer documentation. Okay at least a comment in the abstract class would have been enough to know that it is not completely implemented. I tried to find the error since yesterday and now I hear that it's just not completely implemented in XenForo...
And about "they weren't needed and were just not used": As you see other 2FA providers possibly need.

Thanks for the description and code example. Just as a theoretically question: Can't I also override these non-existent classes (in this case XenForo_ViewPublic_Account_TwoStepSetup) and put the code there? There I would not need all these checks.

Back to this example:
So when I confirmed that the provider is mine, how can I output a template then? I assume I somehow have to add it to $parent, but $parent already seems to be a finished view (it is not even an instance of XenForo_View), so how can I do that?
 

Chris D

XenForo developer
Staff member
#18
Thanks for the description and code example. Just as a theoretically question: Can't I also override these non-existent classes (in this case XenForo_ViewPublic_Account_TwoStepSetup) and put the code there? There I would not need all these checks.
Yes, that is another option though there are considerations there in case other add-ons want to extend the same thing.

So when I confirmed that the provider is mine, how can I output a template then? I assume I somehow have to add it to $parent, but $parent already seems to be a finished view (it is not even an instance of XenForo_View), so how can I do that?
You can just return your own response view, e.g.
PHP:
public function actionTwoStepEnable()
{
    $parent = parent::actionTwoStepEnable();

    if ($parent instanceof XenForo_ControllerResponse_View)
    {
        // At this point, we should already be at the "TwoStepSetup" stage
        // You should get your provider here, and do the necessary steps to ensure it is valid and check it is your provider...

        if ($provider->provider_id == 'YourProviderId')
        {
                // Do whatever you need to do here to get your provider set up
                $viewParams = array();
                return $this->responseView('YourAddOn_Blah_Blah', 'youraddon_template_blah_blah', $viewParams);
        }
    }

    return $parent;
}
 

rugk

Active member
#19
I begin to doubt that this (using the "setup" part) is a good way to implement it.
So a more general question: The current 2FA methods all trigger the verification already when you set them up. Before the user even sees the setup. Only the setup is done before, but if you use the setup it does not redirect to the handleManage any more, but just to the main site.
What I want to have is basically both modes together. So when the provider is enabled the user is at first presented with some options which need to be set before the provider is triggered. These options are similar or even the same which may be showed later when the user "manages" the provider.
So after he put all the details into this he can submit it and the verification is triggered. Afterwards the user should just pass the verification (so renderVerification and verifyFromInput after submitting the input) like it is already done with all verification modes. Only if these two steps are done the provider should be enabled.

So I thought the setup section would be a thing where I can do the first thing, but I'm not sure whether I could not reuse the management a bit better.
Basically I just want 3 steps for enabling a provider instead of two. So what's the best way to do this?
 

rugk

Active member
#20
I'd like to add another thing you should be aware when creating an 2FA add-on: The templates for the normal user login (2FA verification) and the administrative login (2FA verification) when logging into the ACP are two different ones: So you almost certainly have to create your "two_step_..." template two times (with the same name): One time as a normal template and one as an admin template, otherwise your ACP 2FA login page may show nothing.