How I built 2FA into the RingCentral WordPress plugin

Pbmacintyre
RingCentral Developers
7 min readDec 26, 2023

I have recently extended my WordPress RingCentral plugin to incorporate a 2FA (Two-factor Authentication) feature. 2FA/ MFA (Multi-factor Auth) is a best practice for authenticating access to protected content on the web. In fact, you’ve probably used it yourself when trying to log in and being sent a security code via SMS or an Authenticator app to verify your identity.

NOTE: In my previous versions of this plugin I was authenticating toRingCentral using their now deprecated Password Flow. I also updated my plugin to utilize the recommended JWT flow which is more secure. See this article and this article for more information if you also have an app that is using the RingCentral SDK and need to update the SDK authentication process.

In order to add the 2FA to the plugin, I had to alter and update the WordPress user_meta table with information that would be used by my code to validate a WordPress user. The administrator would have the initial control to set up the 2FA for themselves or other admin level users. Only admin level user types are able to set up 2FA on behalf of themselves or other admin users. Since WordPress allows for the dynamic alteration to the user_meta table with the update_user_meta() [for inserting data] and the get_the_author_meta() [for retrieving data] functions, I was able to insert rows of data into the user_meta table programmatically and recall or update them as needed. I added in the following meta_key_values:

RingCentral_2fa_user_mobile_validated — toggle for full validation having been completed.

RingCentral_2fa_user_mobile — the mobile number for the specified user.

RingCentral_2fa_fresh_code_sent — toggle to manage the sending of the 6 digit code.

RingCentral_2fa_user_enabled — toggle to know if the 2FA has been activated for the specified user.

RingCentral_2fa_user_2fa_code — the most recent 6 digit code that was sent to the specified user.

I will further explain each of these data points and how they were used to enable and manage the 2FA validation process over the course of this article.

In order to manage the 2FA aspect of a user account I had to add a section into the user management area with the help of PHP and JavaScript code. WordPress allows for this by using the add_action() function. The first parameter of that function, the hook_name, tells WordPress where to insert the new code and the second parameter, the callback, is a reference to that code; typically in a custom defined function. We have to use the add_action two times: once for the situation where an admin user is editing their own profile and another one for the situation where the admin user is editing another admin user’s profile.

add_action(‘show_user_profile’, ‘ringcentral_2fa_user_settings’); // other users
add_action(‘edit_user_profile’, ‘ringcentral_2fa_user_settings’); // your own profile

So, for example, putting these two function calls into my main plugin code file would insert the code found in the ringcentral_2fa_user_settings() function (my custom code) during the show user profile update or the edit user profile update.

It is not possible to insert custom code while you are adding a new user as WordPress needs to do it’s own things before we can get in there and make any alterations to the user accounts. Assigning a valid user ID, for example, should not have any variable custom activity happening while that is being done.

Once our insert code is triggered during a user update you can see our inserted form in the user profile edit page. This is appended at the bottom of the form, but keep in mind that other plugins may also be altering the user editing experience so our additions to the form may not be the last item shown on the page.

Our portion / addition to the user profile edit page looks like that shown in figure 1.

Figure 1–2FA initial user setup form on User Edit page.

Here you are being asked if you want to turn on the 2FA feature [1] for the user being edited and if so, provide a North American formatted mobile phone number. The JavaScript code in play here will validate the format of the phone number [2] and then show / hide the “Send 6 digit validation code” link [3] that will send out the random code to the provided number. The one line of PHP code that is needed to generate a random 6 digit number and store it into a variable looks like this:

$six_digit_code = rand(100000, 999999);

There is even more going on behind the scenes here code-wise. If, for example, the phone number is a valid format then the link appears, and if it is not an error message displays until it is corrected. Once the “Send…” link is clicked the number is collected from the form, a random 6 digit code is created, and the RingCentral SDK is used to send the generated code to the provided mobile number. Then the user edit form is re-displayed with the “Send…” link removed (hidden) and the validation text field showing. See figure 2.

Figure 2–2FA form on User Edit page, code sent.

The phone number is added into the user_meta table and associated with the userid being edited. The 6 digit code is also stored at the same time and then it is texted to the provided mobile number so that it can be validated during the next step in the profile edit process. I also store a 0 / 1 toggle that is used to test if I am sending out a new “fresh” code. Typically, when the six digit code has been sent, but not yet verified the user_meta table values that we are using look like this, figure 3.

Figure 3 — user_meta table custom values.

Back on the user profile form more JavaScript is executed that displays the hidden text fields that will accept the validation code (figure 2–1) and a new link is displayed to be used to validate the sent code (figure 2–2). Also during this process, I used code to authenticate to the RingCentral SDK and actually send out the generated six digit code. The sample SMS that is sent looks like that shown in figure 4 for a RingCentral sandbox account.

Figure 4 — SMS Message sent to provided mobile number.

Once the code is validated the user page is re-displayed again with a validation success message — Figure 5; and the appropriate user meta information is changed in the database to that shown in figure 6.

Figure 5 — Mobile number for selected user has been validated.
Figure 6 — User meta values when mobile number has been validated.

This process has now set up the selected admin user for the WordPress site to be challenged with a random 2FA validation code each time they attempt to login to the website’s administration area. This whole process needs to be done for each admin user account on each WordPress site that uses this plugin. This will add an additional level of security to the site’s administration. During the login process to the admin site, after the user provides their username and password, they are presented with the request to authenticate themselves with a provided six digit code, the screen looks like figure 7.

Figure 7 — login challenge for six digit code.

The code that launches this process is also triggered with an add_action() function call. This time the action hook is “authenticate” and my function code is called RingCentral_2fa_intercept(). In this code I look for the user information that should already be stored in the user_meta table and I extract it with the get_user_meta() function. First I test to see if the RingCentral_2fa_user_mobile_validated value is 1. This tells me that they have a validated mobile phone number stored in the meta_user table. I can then collect it and send a random six digit code to that number with the help of the RingCentral SDK. Then we get the number from the user who is trying to login — they type it into the provided form — and I compare the two numbers. If they match then access to the admin area is granted. If they don’t match then error messages are displayed and the user has to start the login process over from the beginning. This is all done for security and validation reasons.

Version 1.5.3 of this 2FA plugin has just been released and can be found here. Try it out and let me know if you have any feedback or suggestions for improvements.

--

--

Pbmacintyre
RingCentral Developers

Peter has over 35 years of experience in IT, primarily in PHP. Author of PHP: The Good Parts; co-author: Programming PHP-4th Ed. Zend certified in PHP 5.3 & 4.0