PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 4.0.0
WP 2FA – Two-factor authentication for WordPress v4.0.0
4.0.0 1.7.1 2.0.0 2.0.1 2.1.0 2.2.0 2.2.1 2.3.0 2.4.0 2.4.1 2.4.2 2.5.0 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.8.0 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0 3.0.1 3.1.0 3.1.1 3.1.1.2 trunk 1.2.0 1.3.0 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.1 1.6.2 1.7.0
wp-2fa / js / wp2fa-wizard-email.js
wp-2fa / js Last commit date
passkeys 1 day ago profile 1 day ago wp2fa-dialog.js 1 day ago wp2fa-premium-badge-dialog.js 1 day ago wp2fa-wizard-backup-codes.js 1 day ago wp2fa-wizard-email.js 1 day ago wp2fa-wizard-totp.js 1 day ago wp2fa-wizard.js 1 day ago
wp2fa-wizard-email.js
298 lines
1 /**
2 * WP 2FA - Email (HOTP) Method Module
3 *
4 * Registers the Email method with the wizard via wp.hooks.
5 * Renders the email address selection and verification steps.
6 *
7 * @package wp2fa
8 * @since 4.0.0
9 *
10 * Hooks this module uses:
11 * - wp2fa_wizard_methods (filter) — registers Email as an available method
12 * - wp2fa_wizard_render_method (action) — renders Email-specific configuration UI
13 */
14 ( function () {
15 'use strict';
16
17 if ( ! window.wp || ! window.wp.hooks ) {
18 return;
19 }
20
21 var hooks = wp.hooks;
22 var METHOD_ID = 'email';
23
24 /* -------------------------------------------------------
25 * Register method in the selection list
26 * ------------------------------------------------------- */
27 hooks.addFilter( 'wp2fa_wizard_methods', 'wp2fa/email', function ( methods, context ) {
28 if ( typeof wp2faWizardData === 'undefined' ) {
29 return methods;
30 }
31 var enabledMethods = wp2faWizardData.enabledMethods || {};
32 if ( ! enabledMethods[ METHOD_ID ] ) {
33 return methods;
34 }
35
36 methods.push( {
37 id: METHOD_ID,
38 name: wp2faWizardData.methodLabels && wp2faWizardData.methodLabels[ METHOD_ID ]
39 ? wp2faWizardData.methodLabels[ METHOD_ID ]
40 : 'One-time code via email (HOTP)',
41 description: wp2faWizardData.methodDescriptions && wp2faWizardData.methodDescriptions[ METHOD_ID ]
42 ? wp2faWizardData.methodDescriptions[ METHOD_ID ]
43 : '',
44 order: ( wp2faWizardData.methodOrders && typeof wp2faWizardData.methodOrders[ METHOD_ID ] !== 'undefined' )
45 ? wp2faWizardData.methodOrders[ METHOD_ID ]
46 : 2
47 } );
48
49 return methods;
50 } );
51
52 /* -------------------------------------------------------
53 * Render Email configuration
54 * ------------------------------------------------------- */
55 hooks.addAction( 'wp2fa_wizard_render_method', 'wp2fa/email', function ( methodId, container, context ) {
56 if ( methodId !== METHOD_ID ) {
57 return;
58 }
59
60 var emailData = wp2faWizardData.emailData || {};
61
62 renderEmailSetup();
63
64 function renderEmailSetup() {
65 container.innerHTML = '';
66
67 /* Title */
68 var titleEl = document.getElementById( 'wp2fa-wizard-title' );
69 if ( titleEl ) {
70 titleEl.innerHTML = hooks.applyFilters(
71 'wp2fa_wizard_step_title',
72 wp2faWizardData.i18n.settingUpEmail || 'Setting up HOTP (one-time code via email)',
73 'configure-email-setup'
74 );
75 }
76
77 /* Build notice HTML */
78 var noticeHtml = '';
79 var whitelistNotice = wp2faWizardData.i18n.emailWhitelistNotice || '';
80 var showNotice = !! emailData.fromEmail && !! whitelistNotice;
81 if ( showNotice ) {
82 var safeFromEmail = emailData.fromEmail.replace( /&/g, '&amp;' ).replace( /</g, '&lt;' ).replace( />/g, '&gt;' ).replace( /"/g, '&quot;' );
83 noticeHtml = whitelistNotice.replace( '{from_email}', safeFromEmail );
84 }
85
86 /* Render body via template */
87 var setupTmpl = wp.template( 'wp2fa-email-setup' );
88 container.innerHTML = setupTmpl( {
89 description: wp2faWizardData.i18n.emailSetupIntro || '',
90 userEmail: emailData.userEmail || '',
91 useMyEmailLabel: wp2faWizardData.i18n.useMyEmail || 'Use my user email',
92 allowCustomEmail: !! emailData.allowCustomEmail,
93 useAnotherLabel: wp2faWizardData.i18n.useAnotherEmail || 'Use another email address',
94 emailPlaceholder: wp2faWizardData.i18n.emailPlaceholder || 'email@example.com',
95 emailHelpText: wp2faWizardData.i18n.emailHelpText || '',
96 showNotice: showNotice,
97 noticeHtml: noticeHtml
98 } );
99
100 /* Pre-populate nominated/custom email if the user had one configured */
101 if ( emailData.nominatedEmail && emailData.allowCustomEmail ) {
102 var customRadio = container.querySelector( '#wp2fa-email-use-custom' );
103 var wpRadio = container.querySelector( '#wp2fa-email-use-wp' );
104 var customInput = container.querySelector( '#wp2fa-email-custom-address' );
105 if ( customRadio && wpRadio && customInput ) {
106 wpRadio.checked = false;
107 customRadio.checked = true;
108 customInput.value = emailData.nominatedEmail;
109 }
110 }
111
112 /* Footer via template */
113 var footer = document.getElementById( 'wp2fa-wizard-footer' );
114 var setupFooterTmpl = wp.template( 'wp2fa-email-setup-footer' );
115 footer.innerHTML = setupFooterTmpl( {
116 goBackLabel: wp2faWizardData.i18n.goBack || 'Go back',
117 readyLabel: wp2faWizardData.i18n.imReady || "I'm Ready"
118 } );
119
120 footer.querySelector( '#wp2fa-email-btn-goback' ).addEventListener( 'click', function ( e ) {
121 e.preventDefault();
122 hooks.doAction( 'wp2fa_wizard_before_back', 'configure-email-setup', 'select-method' );
123 window.wp2faWizard.goToStep( 'select-method' );
124 hooks.doAction( 'wp2fa_wizard_after_back', 'select-method' );
125 } );
126
127 var readyBtn = footer.querySelector( '#wp2fa-email-btn-ready' );
128 readyBtn.addEventListener( 'click', function () {
129 /* Determine selected email */
130 var selectedRadio = container.querySelector( 'input[name="wp2fa_wizard_email_address"]:checked' );
131 var emailAddress = '';
132
133 if ( selectedRadio && selectedRadio.value === 'use_custom_email' ) {
134 var customEmailInput = container.querySelector( '#wp2fa-email-custom-address' );
135 emailAddress = customEmailInput ? customEmailInput.value.trim() : '';
136 if ( ! emailAddress || ! window.wp2faWizard.utils.isValidEmail( emailAddress ) ) {
137 customEmailInput.focus();
138 return;
139 }
140 } else if ( selectedRadio ) {
141 emailAddress = ''; //selectedRadio.value;
142 }
143
144 /* Send setup email via AJAX */
145 readyBtn.disabled = true;
146 var spinner = window.wp2faWizard.utils.addSpinner( readyBtn );
147
148 var formData = new FormData();
149 formData.append( 'action', 'send_authentication_setup_email' );
150 formData.append( 'email_address', emailAddress );
151 formData.append( 'nonce', wp2faWizardData.sendEmailNonce );
152
153 fetch( wp2faWizardData.ajaxUrl, {
154 method: 'POST',
155 credentials: 'same-origin',
156 body: formData
157 } )
158 .then( function ( res ) { return res.json(); } )
159 .then( function ( data ) {
160 readyBtn.disabled = false;
161 window.wp2faWizard.utils.removeSpinner( spinner );
162 if ( data.success ) {
163 renderEmailVerify( emailAddress );
164 } else {
165 window.wp2faWizard.utils.showInlineError( container, window.wp2faWizard.utils.extractAjaxError( data, wp2faWizardData.i18n.emailSendError || 'Failed to send setup email.' ) );
166 }
167 } )
168 .catch( function () {
169 readyBtn.disabled = false;
170 window.wp2faWizard.utils.removeSpinner( spinner );
171 } );
172 } );
173 }
174
175 function renderEmailVerify( emailAddress ) {
176 container.innerHTML = '';
177
178 /* Title */
179 var titleEl = document.getElementById( 'wp2fa-wizard-title' );
180 if ( titleEl ) {
181 titleEl.innerHTML = hooks.applyFilters(
182 'wp2fa_wizard_step_title',
183 wp2faWizardData.i18n.verifyConfig || 'Verify configuration',
184 'configure-email-verify'
185 );
186 }
187
188 /* Render body via template */
189 var verifyTmpl = wp.template( 'wp2fa-email-verify' );
190 container.innerHTML = verifyTmpl( {
191 description: wp2faWizardData.i18n.emailVerifyIntro || 'Please enter the one-time code sent to your email address to complete setup.',
192 codeLabel: wp2faWizardData.i18n.verificationCode || 'Verification Code:',
193 placeholder: '000000',
194 resendLabel: wp2faWizardData.i18n.resendCode || 'Send me another code'
195 } );
196
197 var input = container.querySelector( '#wp2fa-email-authcode' );
198 var response = container.querySelector( '#wp2fa-email-verification-response' );
199
200 input.addEventListener( 'input', function () {
201 this.value = this.value.replace( /\s/g, '' );
202 } );
203
204 /* Resend button */
205 var resendBtn = container.querySelector( '#wp2fa-email-resend-code' );
206 resendBtn.addEventListener( 'click', function () {
207 resendBtn.disabled = true;
208
209 var formData = new FormData();
210 formData.append( 'action', 'send_authentication_setup_email' );
211 formData.append( 'email_address', emailAddress );
212 formData.append( 'nonce', wp2faWizardData.sendEmailNonce );
213
214 fetch( wp2faWizardData.ajaxUrl, {
215 method: 'POST',
216 credentials: 'same-origin',
217 body: formData
218 } )
219 .then( function ( res ) { return res.json(); } )
220 .then( function ( data ) {
221 resendBtn.disabled = false;
222 if ( data.success ) {
223 response.className = 'wp2fa-wizard-verification-response wp2fa-response-success';
224 response.textContent = wp2faWizardData.i18n.codeSent || 'A new code has been sent.';
225 setTimeout( function () {
226 response.textContent = '';
227 response.className = 'wp2fa-wizard-verification-response';
228 }, 3000 );
229 }
230 } )
231 .catch( function () {
232 resendBtn.disabled = false;
233 } );
234 } );
235
236 /* Footer via template */
237 var footer = document.getElementById( 'wp2fa-wizard-footer' );
238 var verifyFooterTmpl = wp.template( 'wp2fa-email-verify-footer' );
239 footer.innerHTML = verifyFooterTmpl( {
240 cancelLabel: wp2faWizardData.i18n.cancel || 'Cancel',
241 validateLabel: wp2faWizardData.i18n.validateSave || 'Validate & Save'
242 } );
243
244 footer.querySelector( '#wp2fa-email-verify-btn-cancel' ).addEventListener( 'click', function () {
245 window.wp2faWizard.close();
246 } );
247
248 var validateBtn = footer.querySelector( '#wp2fa-email-btn-validate' );
249 validateBtn.addEventListener( 'click', function () {
250 var code = input.value.trim();
251 if ( ! code ) {
252 window.wp2faWizard.utils.showResponse( response, 'error', wp2faWizardData.i18n.enterCode || 'Please enter the verification code.' );
253 return;
254 }
255
256 validateBtn.disabled = true;
257 var spinner = window.wp2faWizard.utils.addSpinner( validateBtn );
258
259 hooks.doAction( 'wp2fa_wizard_validate_method', METHOD_ID, container );
260
261 var formData = new FormData();
262 formData.append( 'action', 'validate_authcode_via_ajax' );
263 formData.append( 'form[wp-2fa-email-authcode]', code );
264 if ( emailAddress ) {
265 formData.append( 'form[custom-email-address]', emailAddress );
266 }
267 formData.append( '_wpnonce', context.nonce );
268
269 fetch( wp2faWizardData.ajaxUrl, {
270 method: 'POST',
271 credentials: 'same-origin',
272 body: formData
273 } )
274 .then( function ( res ) { return res.json(); } )
275 .then( function ( data ) {
276 validateBtn.disabled = false;
277 window.wp2faWizard.utils.removeSpinner( spinner );
278 if ( data.success ) {
279 window.wp2faWizard.utils.showResponse( response, 'success', wp2faWizardData.i18n.emailSuccess || 'Email 2FA configured successfully!' );
280 hooks.doAction( 'wp2fa_wizard_method_validated', METHOD_ID );
281 } else {
282 var errMsg = window.wp2faWizard.utils.extractAjaxError( data, wp2faWizardData.i18n.invalidCode || 'Invalid code. Please try again.' );
283 window.wp2faWizard.utils.showResponse( response, 'error', errMsg );
284 }
285 } )
286 .catch( function () {
287 validateBtn.disabled = false;
288 window.wp2faWizard.utils.removeSpinner( spinner );
289 window.wp2faWizard.utils.showResponse( response, 'error', wp2faWizardData.i18n.networkError || 'Network error. Please try again.' );
290 } );
291 } );
292
293 input.focus();
294 }
295
296 } );
297 }() );
298