{"id":12193,"date":"2023-02-09T12:12:57","date_gmt":"2023-02-09T18:12:57","guid":{"rendered":"https:\/\/www.mrc-productivity.com\/docs\/?post_type=ht_kb&#038;p=12193"},"modified":"2026-09-10T15:54:14","modified_gmt":"2026-09-10T20:54:14","slug":"setting-up-two-factor-authentication-2fa-in-m-power","status":"publish","type":"ht_kb","link":"https:\/\/www.mrc-productivity.com\/docs\/knowledge-base\/setting-up-two-factor-authentication-2fa-in-m-power","title":{"rendered":"Setting up Two-Factor Authentication (2FA) in m-Power"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Overview<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Two-factor authentication provides an additional security layer for user authentication. When a user authenticates via their username and password, a secondary form of authentication is prompted where the user must enter a unique code sent via email, text message, or a TOTP (Time-based One-Time Password) authenticator app.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This document will discuss steps on how to set up two-factor authentication in m-Power. <\/p>\n\n\n\n<p class=\"wp-block-ht-blocks-messages wp-block-hb-message wp-block-hb-message--withicon is-style-alert\">Using m-Power&#8217;s built-in 2FA <strong>requires <\/strong>that you are utilizing a non-SSO option for user authentication. For more information on the different options offered for user authentication, please see <a href=\"\/docs\/knowledge-base\/implementing-security#configuring-your-security-options\">here<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Implementation<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Step 1 &#8211; Editing your Security Settings<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In the m-Power developer interface, navigate to Admin -&gt; Menu &amp; Security -&gt; Edit Security Settings. In the Security Settings window that appears, use the Property Options -&gt; Text Mode to open this file (the mrcSignon2.xml) in a text editor. This file will need the following code added to the bottom, inserted right before the final <code>&lt;\/mrc_signon&gt;<\/code>  tag:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;two_step_auth&gt;  \t \n  &lt;twoFactorAuthentication pdesc=\"twoFactorAuthentication\" value=\"true\"\/&gt; \n  &lt;sendTextEmail pdesc=\"sendTextEmail\" value=\"user-choice\"\/&gt; \n  &lt;emailSubject pagetype=\"template\"&gt;2FA Test from m-Power&lt;\/emailSubject&gt;\n  &lt;emailBody pagetype=\"template\"&gt;Here is your verification code: ${CODE}&lt;\/emailBody&gt;\n  &lt;smsTextBody pagetype=\"template\"&gt;Here is your verification code: ${CODE}&lt;\/smsTextBody&gt;\n  &lt;expireInDays pdesc=\"expireInDays\" value=\"31\"\/&gt;\n  &lt;user_info_table tablename=\"DICTIONARY.MRCSEC1\" col_user=\"USERID\" col_email=\"USEREMAIL\" col_phone=\"USERPHONE\" col_totpsecret=\"TOTPSECRET\"\/&gt;\n  &lt;totpIssuer value=\"m-Power\"\/&gt;\n&lt;\/two_step_auth&gt;\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Once this block of code has been added, save the editor and re-open the Edit Security Settings window. The Two Factor Authentication section should now be presented:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"\/techblog\/images\/2FA_1.png\" alt=\"Security Settings with 2FA options\"\/><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\">2FA Properties<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">All 2FA properties shown in the above figure are explained in detail below:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Email Subject:<\/strong> The subject text of the email that will be sent to the user containing the verification code. Only applicable when the <strong>Message Type Option<\/strong> is either &#8220;Email&#8221; or &#8220;Both&#8221;. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Email Text<\/strong>\/<strong>SMS Text Body:<\/strong> This is the display text the user will be shown via SMS text and email, respectively. Each message may be modified to your liking.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The variable <code>${CODE}<\/code> represents the authentication code that will supplied to the user. If you do not put it in there, it will append the code to the end of the body.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Two Factor Authentication:<\/strong> Enables or disables the user of 2FA within the current data dictionary.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Message Type Options:<\/strong> Use to select the verification options presented to the user. Choices are <\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>SMS<\/li>\n\n\n\n<li>Email<\/li>\n\n\n\n<li>Both (SMS and Email)<\/li>\n\n\n\n<li>TOTP (Time-based One-Time Password)<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Selecting both will allow the user to choose at runtime between SMS and Email for their verification code.<\/p>\n\n\n\n<p class=\"wp-block-ht-blocks-messages wp-block-hb-message wp-block-hb-message--withicon is-style-alert\">To send an SMS text message or an email to a user, the Messaging Configuration file in m-Power must be configured with either the SMS relay account information and\/or the mail server information. See <a href=\"\/docs\/knowledge-base\/messaging-configuration\" data-type=\"URL\" data-id=\"\/docs\/knowledge-base\/working-with-the-messaging-scheduled-tasks-plug-in#messaging-configuration\" target=\"_blank\" rel=\"noreferrer noopener\">here<\/a> for more information.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Two Factor Expiration:<\/strong> Use this option to specify the number of days before the 2FA cookie (set in the browser) will expire for the user and thus will require the user to re-authenticate via 2FA again. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example, when a user signs in and authenticates against 2FA for the first time, here are some applicable options for this setting:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A value of <strong>30<\/strong> means 30 days from the initial sign on before the user&#8217;s next sign on attempt will require 2FA again. This number can be adjusted as desired.<\/li>\n\n\n\n<li>A value <strong>0<\/strong> means no expiration date after the end-user&#8217;s initial 2FA. The user will never be presented with 2FA again, unless they attempt to access the application from a new device or new browser. <\/li>\n\n\n\n<li>A value of <strong>-1<\/strong> means 2FA will be prompted to the end-user on <strong>every<\/strong> sign on attempt.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Table and Column info:<\/strong> The table and columns that contain the user information, which m-Power will use for sending the user an email or SMS text message, must be specified here.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 2 &#8211; Setting up the 2FA screen<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The screen presented to the end-user during the 2FA process depends on your chosen message type.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>For Email and SMS Verification:<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The default screen is the <code>mrcAuthentication.html<\/code> file located in your dictionary folder (<code>...\/m-power\/mrcjava\/WEB-INF\/classes\/DICTIONARY<\/code>). If this file is not within this directory, simply copy it from <code>...\/m-power\/mrcjava\/mrcclasses\/<\/code>. You may customize the style of this page to your liking.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"\/techblog\/images\/2FA_2.png\" alt=\"2FA screen\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>For TOTP Verification:<\/strong> m-Power utilizes two separate HTML files specifically for authenticator-app workflows<sup><\/sup>:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>mrcAuthenticationTotpSetup.html<\/code>: Prompted on an unenrolled user&#8217;s first login to display the QR enrollment code. Default versions are bundled inside m-Power and are used automatically when a dictionary has no local copy. To customize them, copy the master files from <code>\/mrcjava\/mrcclasses\/<\/code> into your dictionary folder.<\/p>\n\n\n\n<figure data-wp-context=\"{&quot;imageId&quot;:&quot;6ab5b3754a446&quot;}\" data-wp-interactive=\"core\/image\" data-wp-key=\"6ab5b3754a446\" class=\"wp-block-image size-large wp-lightbox-container\"><img decoding=\"async\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on--click=\"actions.showLightbox\" data-wp-on--load=\"callbacks.setButtonStyles\" data-wp-on--pointerdown=\"actions.preloadImage\" data-wp-on--pointerenter=\"actions.preloadImageWithDelay\" data-wp-on--pointerleave=\"actions.cancelPreload\" data-wp-on-window--resize=\"callbacks.setButtonStyles\" src=\"\/techblog\/images\/2faTotpSetup.png\" alt=\"\"\/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\tdata-wp-bind--aria-label=\"state.thisImage.triggerButtonAriaLabel\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"state.thisImage.buttonRight\"\n\t\t\tdata-wp-style--top=\"state.thisImage.buttonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><code>mrcAuthenticationTotp.html<\/code>: Prompted when an enrolled user needs to enter their 6-digit code.<\/p>\n\n\n\n<figure data-wp-context=\"{&quot;imageId&quot;:&quot;6ab5b3754a680&quot;}\" data-wp-interactive=\"core\/image\" data-wp-key=\"6ab5b3754a680\" class=\"wp-block-image size-large wp-lightbox-container\"><img decoding=\"async\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on--click=\"actions.showLightbox\" data-wp-on--load=\"callbacks.setButtonStyles\" data-wp-on--pointerdown=\"actions.preloadImage\" data-wp-on--pointerenter=\"actions.preloadImageWithDelay\" data-wp-on--pointerleave=\"actions.cancelPreload\" data-wp-on-window--resize=\"callbacks.setButtonStyles\" src=\"\/techblog\/images\/2faTotp.png\" alt=\"\"\/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\tdata-wp-bind--aria-label=\"state.thisImage.triggerButtonAriaLabel\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"state.thisImage.buttonRight\"\n\t\t\tdata-wp-style--top=\"state.thisImage.buttonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Step 3 &#8211; Add entry to web.xml file<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Open the web.xml file, located in &#8230;\/m-power\/mrcjava\/WEB-INF in a text editor and do a search for &#8220;mrc_auth2&#8221;. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The following servlet shown below must be listed in this file. If is it not listed, please copy the code and place this at the bottom of the file before the final <code>&lt;\/webapp&gt;<\/code> tag at the very bottom of the file.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;servlet&gt;\n  &lt;servlet-name&gt;mrc_auth2&lt;\/servlet-name&gt;\n  &lt;servlet-class&gt;mrc.TwoFactorAuthentication&lt;\/servlet-class&gt;\n&lt;\/servlet&gt;\n&lt;servlet-mapping&gt;\n  &lt;servlet-name&gt;mrc_auth2&lt;\/servlet-name&gt;\n  &lt;url-pattern&gt;\/servlet\/mrc.auth2&lt;\/url-pattern&gt;\n&lt;\/servlet-mapping&gt;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Once the file has been saved, Tomcat will need to be restarted at this point.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">TOTP Configuration<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If you selected <code>totp<\/code> for your Message Type Option, additional database and server configurations are required.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Database Changes<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">One new column is needed on your user info table to store the user&#8217;s TOTP secret. By default, this column should be named <code>TOTPSECRET<\/code>.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The column stores the secret as plain Base32 when no encryption key is configured.<\/li>\n\n\n\n<li>When the <code>totp_encryption_key<\/code> property is set, the secret is stored as <code>enc:&lt;base64&gt;<\/code>.<\/li>\n\n\n\n<li>An empty or NULL value in this column means the user is &#8220;not enrolled yet,&#8221; prompting the QR enrollment page upon their next login.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Example DDL per platform (adjust schema\/library as needed):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>-- IBM i \/ DB2:\nALTER TABLE MYLIB.MRCSEC1 ADD COLUMN TOTPSECRET VARCHAR(200) DEFAULT NULL;\n\n-- SQL Server:\nALTER TABLE MYLIB.dbo.MRCSEC1 ADD TOTPSECRET VARCHAR(200) NULL;\n\n-- Oracle:\nALTER TABLE MYLIB.MRCSEC1 ADD TOTPSECRET VARCHAR2(200);\n\n-- MySQL \/ MariaDB:\nALTER TABLE MYLIB.MRCSEC1 ADD COLUMN TOTPSECRET VARCHAR(200) NULL;\n\n-- PostgreSQL:\nALTER TABLE mylib.mrcsec1 ADD COLUMN totpsecret VARCHAR(200);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A TOTPSECRET value that is <strong>empty\/NULL<\/strong> means &#8220;not enrolled yet&#8221;&nbsp;\u2014 the user gets the QR enrollment page on next login.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Re-enrollment \/ lost phone<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">If a user needs to be re-enrolled (for example, they lose their mobile device or get a new one), the TOTPSECRET value must be nullified. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>UPDATE MYLIB.MRCSEC1 SET TOTPSECRET = NULL WHERE USERID = 'JSMITH';<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Once the field value is reset, the user re-enrolls with a fresh secret on next login.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">mrc-runtime.properties<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Optional advanced properties can be configured in <code>\/mrcjava\/WEB-INF\/classes\/mrc-runtime.properties<\/code>:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>totp_encryption_key:<\/strong> <em>Recommended for live production environments.<\/em> This property scrambles  the secret codes (using AES-256-GCM) before saving them to your database. This ensures that even if someone gained unauthorized access to your database, they could not read the secrets or generate valid login codes. Without this key, the secrets are stored as readable plain text. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Example configuration:<\/em> <code>totp_encryption_key=0nyqUmzvM+UtNOgil8AZMrhcAgs8H3NzeEhtxgjh21Y=<\/code><\/p>\n\n\n\n<p class=\"wp-block-ht-blocks-messages wp-block-hb-message wp-block-hb-message--withicon is-style-danger\"><strong>Warning:<\/strong> If you change or lose this key, the encrypted secrets can no longer be decrypted. Affected users will receive an error on the 2FA screen and must have their <code>TOTPSECRET<\/code> column cleared by an administrator to re-enroll. Always back this key up alongside your other server secrets<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>totp_window:<\/strong> Adjusts for slight time differences between your server and a user&#8217;s phone, which is often called &#8220;clock-drift tolerance&#8221;. Because authenticator apps generate codes based on the exact current time, a legitimate code might fail if a user&#8217;s phone clock is slightly fast or slow. This setting provides a brief grace period by accepting codes from just before or just after the current time. It accepts values from <code>0<\/code> to <code>4<\/code>. The default value of <code>1<\/code> means a code is valid for roughly 30 seconds before and after the current time.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>totp_issuer:<\/strong> The default company or app name displayed inside the user&#8217;s authenticator app. This is the label shown right above the 6-digit code on their phone, helping them identify what the code is for. Setting a value here is global, but can be overridden per dictionary in your <code>mrcSignon2.xml<\/code> file<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">HTML Customizations<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Default versions are already included with m-Power and are used automatically when a dictionary has no copy. To customize for a dictionary, copy the master files (mrcAuthenticationTotp.html and mrcAuthenticationTotpSetup.html) from <code>\/mrcjava\/mrcclasses\/<\/code> into <code>\/mrcjava\/WEB-INF\/classes\/[DICTIONARY]<\/code>. Replace [DICTIONARY] with your actual dictionary name. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Overview Two-factor authentication provides an additional security layer for user authentication. When a user authenticates via their username and password, a secondary form of authentication is prompted where the user must enter a unique code sent via email, text message, or a TOTP (Time-based One-Time Password) authenticator app. This document&#8230;<\/p>\n","protected":false},"author":1,"comment_status":"closed","ping_status":"closed","template":"","format":"standard","meta":{"footnotes":""},"ht-kb-category":[257],"ht-kb-tag":[],"class_list":["post-12193","ht_kb","type-ht_kb","status-publish","format-standard","hentry","ht_kb_category-security"],"_links":{"self":[{"href":"https:\/\/www.mrc-productivity.com\/docs\/wp-json\/wp\/v2\/ht-kb\/12193","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.mrc-productivity.com\/docs\/wp-json\/wp\/v2\/ht-kb"}],"about":[{"href":"https:\/\/www.mrc-productivity.com\/docs\/wp-json\/wp\/v2\/types\/ht_kb"}],"author":[{"embeddable":true,"href":"https:\/\/www.mrc-productivity.com\/docs\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.mrc-productivity.com\/docs\/wp-json\/wp\/v2\/comments?post=12193"}],"version-history":[{"count":30,"href":"https:\/\/www.mrc-productivity.com\/docs\/wp-json\/wp\/v2\/ht-kb\/12193\/revisions"}],"predecessor-version":[{"id":15318,"href":"https:\/\/www.mrc-productivity.com\/docs\/wp-json\/wp\/v2\/ht-kb\/12193\/revisions\/15318"}],"wp:attachment":[{"href":"https:\/\/www.mrc-productivity.com\/docs\/wp-json\/wp\/v2\/media?parent=12193"}],"wp:term":[{"taxonomy":"ht_kb_category","embeddable":true,"href":"https:\/\/www.mrc-productivity.com\/docs\/wp-json\/wp\/v2\/ht-kb-category?post=12193"},{"taxonomy":"ht_kb_tag","embeddable":true,"href":"https:\/\/www.mrc-productivity.com\/docs\/wp-json\/wp\/v2\/ht-kb-tag?post=12193"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}