PluginProbe
Atomic Edge Security – Firewall, Malware Scan and Login Security / 2.2.2
Atomic Edge Security – Firewall, Malware Scan and Login Security v2.2.2
trunk 2.0.0 2.1.0 2.2.0 2.2.1 2.2.2 2.3.0 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.4.7 2.4.8 2.4.9 2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 All 29 releases
atomic-edge-security / admin / js / two-factor.js

two-factor.js in Atomic Edge Security – Firewall, Malware Scan and Login Security 2.2.2, at admin/js/two-factor.js

410 lines 9.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * AtomicEdge Two-Factor Authentication JavaScript
3 *
4 * Handles enrollment, verification, and management UI.
5 *
6 * @package AtomicEdge
7 * @since 1.7.0
8 */
9
10 /* global jQuery, atomicedge2fa, QRCode */
11
12 (function($) {
13 'use strict';
14
15 var AtomicEdge2FA = {
16 /**
17 * Current enrollment data.
18 */
19 enrollmentData: null,
20
21 /**
22 * Backup codes download content.
23 */
24 downloadContent: null,
25
26 /**
27 * Initialize the 2FA module.
28 */
29 init: function() {
30 this.bindEvents();
31 this.handleHashScroll();
32 },
33
34 /**
35 * Handle scrolling to 2FA section if hash is present.
36 * WordPress profile pages may delay rendering, so we use a timeout.
37 */
38 handleHashScroll: function() {
39 if (window.location.hash === '#atomicedge-2fa-section') {
40 // Delay to ensure DOM is fully rendered
41 setTimeout(function() {
42 var $section = $('#atomicedge-2fa-section');
43 if ($section.length) {
44 $('html, body').animate({
45 scrollTop: $section.offset().top - 50
46 }, 300);
47 }
48 }, 100);
49 }
50 },
51
52 /**
53 * Bind event handlers.
54 */
55 bindEvents: function() {
56 var self = this;
57
58 // Enrollment flow.
59 $('#atomicedge-2fa-start').on('click', function() {
60 self.startEnrollment();
61 });
62
63 $('#atomicedge-2fa-verify').on('click', function() {
64 self.verifyEnrollment();
65 });
66
67 $('#atomicedge-2fa-cancel').on('click', function() {
68 self.cancelEnrollment();
69 });
70
71 $('#atomicedge-2fa-finish').on('click', function() {
72 window.location.reload();
73 });
74
75 // Code input - auto-submit on 6 digits.
76 $('#atomicedge-2fa-verify-code').on('input', function() {
77 var code = $(this).val().replace(/\D/g, '');
78 $(this).val(code);
79 if (code.length === 6) {
80 self.verifyEnrollment();
81 }
82 });
83
84 // Backup codes download.
85 $('#atomicedge-2fa-download-new-codes, #atomicedge-2fa-download-codes').on('click', function() {
86 self.downloadBackupCodes();
87 });
88
89 // Disable 2FA.
90 $('#atomicedge-2fa-disable').on('click', function() {
91 if (confirm(atomicedge2fa.strings.confirm_disable)) {
92 $('#atomicedge-2fa-enabled').hide();
93 $('#atomicedge-2fa-disable-confirm').show();
94 $('#atomicedge-2fa-password').focus();
95 }
96 });
97
98 $('#atomicedge-2fa-disable-cancel').on('click', function() {
99 $('#atomicedge-2fa-disable-confirm').hide();
100 $('#atomicedge-2fa-enabled').show();
101 $('#atomicedge-2fa-password').val('');
102 });
103
104 $('#atomicedge-2fa-disable-confirm-btn').on('click', function() {
105 self.disable2FA();
106 });
107
108 // Regenerate backup codes.
109 $('#atomicedge-2fa-regenerate-codes').on('click', function() {
110 if (confirm(atomicedge2fa.strings.confirm_regenerate)) {
111 self.regenerateCodes();
112 }
113 });
114
115 $('#atomicedge-2fa-codes-done').on('click', function() {
116 $('#atomicedge-2fa-codes-display').hide();
117 $('#atomicedge-2fa-enabled').show();
118 window.location.reload();
119 });
120 },
121
122 /**
123 * Show loading state.
124 */
125 showLoading: function() {
126 $('#atomicedge-2fa-loading').show();
127 },
128
129 /**
130 * Hide loading state.
131 */
132 hideLoading: function() {
133 $('#atomicedge-2fa-loading').hide();
134 },
135
136 /**
137 * Show a step in the enrollment flow.
138 *
139 * @param {string} step Step ID (intro, setup, codes).
140 */
141 showStep: function(step) {
142 $('.atomicedge-2fa-step').removeClass('active');
143 $('#atomicedge-2fa-step-' + step).addClass('active');
144 },
145
146 /**
147 * Start 2FA enrollment.
148 */
149 startEnrollment: function() {
150 var self = this;
151
152 this.showLoading();
153
154 $.ajax({
155 url: atomicedge2fa.ajax_url,
156 type: 'POST',
157 data: {
158 action: 'atomicedge_2fa_start_enrollment',
159 nonce: atomicedge2fa.nonce,
160 user_id: atomicedge2fa.user_id
161 },
162 success: function(response) {
163 self.hideLoading();
164
165 if (response.success) {
166 self.enrollmentData = response.data;
167 self.renderQRCode(response.data.provisioning_uri);
168 $('#atomicedge-2fa-secret-display').text(
169 self.formatSecret(response.data.secret)
170 );
171 self.showStep('setup');
172 $('#atomicedge-2fa-verify-code').focus();
173 } else {
174 alert(response.data.message || atomicedge2fa.strings.verification_failed);
175 }
176 },
177 error: function() {
178 self.hideLoading();
179 alert(atomicedge2fa.strings.verification_failed);
180 }
181 });
182 },
183
184 /**
185 * Verify enrollment code.
186 */
187 verifyEnrollment: function() {
188 var self = this;
189 var code = $('#atomicedge-2fa-verify-code').val().trim();
190
191 if (code.length !== 6) {
192 $('#atomicedge-2fa-verify-error').text('Please enter a 6-digit code.').show();
193 return;
194 }
195
196 $('#atomicedge-2fa-verify-error').hide();
197 this.showLoading();
198
199 $.ajax({
200 url: atomicedge2fa.ajax_url,
201 type: 'POST',
202 data: {
203 action: 'atomicedge_2fa_verify_enrollment',
204 nonce: atomicedge2fa.nonce,
205 user_id: atomicedge2fa.user_id,
206 code: code
207 },
208 success: function(response) {
209 self.hideLoading();
210
211 if (response.success) {
212 self.downloadContent = response.data.download_content;
213 self.renderBackupCodes(response.data.backup_codes, '#atomicedge-2fa-new-codes');
214 self.showStep('codes');
215 } else {
216 $('#atomicedge-2fa-verify-error').text(response.data.message).show();
217 $('#atomicedge-2fa-verify-code').val('').focus();
218 }
219 },
220 error: function() {
221 self.hideLoading();
222 $('#atomicedge-2fa-verify-error').text(atomicedge2fa.strings.verification_failed).show();
223 }
224 });
225 },
226
227 /**
228 * Cancel enrollment.
229 */
230 cancelEnrollment: function() {
231 var self = this;
232
233 $.ajax({
234 url: atomicedge2fa.ajax_url,
235 type: 'POST',
236 data: {
237 action: 'atomicedge_2fa_cancel_enrollment',
238 nonce: atomicedge2fa.nonce,
239 user_id: atomicedge2fa.user_id
240 },
241 complete: function() {
242 self.enrollmentData = null;
243 $('#atomicedge-2fa-verify-code').val('');
244 $('#atomicedge-2fa-verify-error').hide();
245 self.showStep('intro');
246 }
247 });
248 },
249
250 /**
251 * Disable 2FA.
252 */
253 disable2FA: function() {
254 var self = this;
255 var password = $('#atomicedge-2fa-password').val();
256
257 if (!password) {
258 alert('Please enter your password.');
259 return;
260 }
261
262 this.showLoading();
263
264 $.ajax({
265 url: atomicedge2fa.ajax_url,
266 type: 'POST',
267 data: {
268 action: 'atomicedge_2fa_disable',
269 nonce: atomicedge2fa.nonce,
270 user_id: atomicedge2fa.user_id,
271 password: password
272 },
273 success: function(response) {
274 self.hideLoading();
275
276 if (response.success) {
277 window.location.reload();
278 } else {
279 alert(response.data.message);
280 $('#atomicedge-2fa-password').val('').focus();
281 }
282 },
283 error: function() {
284 self.hideLoading();
285 alert(atomicedge2fa.strings.verification_failed);
286 }
287 });
288 },
289
290 /**
291 * Regenerate backup codes.
292 */
293 regenerateCodes: function() {
294 var self = this;
295
296 this.showLoading();
297
298 $.ajax({
299 url: atomicedge2fa.ajax_url,
300 type: 'POST',
301 data: {
302 action: 'atomicedge_2fa_regenerate_codes',
303 nonce: atomicedge2fa.nonce,
304 user_id: atomicedge2fa.user_id
305 },
306 success: function(response) {
307 self.hideLoading();
308
309 if (response.success) {
310 self.downloadContent = response.data.download_content;
311 self.renderBackupCodes(response.data.backup_codes, '#atomicedge-2fa-codes-list');
312 $('#atomicedge-2fa-enabled').hide();
313 $('#atomicedge-2fa-codes-display').show();
314 } else {
315 alert(response.data.message);
316 }
317 },
318 error: function() {
319 self.hideLoading();
320 alert(atomicedge2fa.strings.verification_failed);
321 }
322 });
323 },
324
325 /**
326 * Render QR code.
327 *
328 * @param {string} uri The otpauth:// URI.
329 */
330 renderQRCode: function(uri) {
331 var canvas = document.getElementById('atomicedge-2fa-qrcode');
332 if (!canvas) {
333 return;
334 }
335
336 // Clear existing QR code.
337 var ctx = canvas.getContext('2d');
338 ctx.clearRect(0, 0, canvas.width, canvas.height);
339
340 // Generate QR code using the bundled library.
341 if (typeof QRCode !== 'undefined') {
342 QRCode.toCanvas(canvas, uri, {
343 width: 200,
344 margin: 2,
345 color: {
346 dark: '#000000',
347 light: '#ffffff'
348 }
349 }, function(error) {
350 if (error) {
351 console.error('QR Code generation error:', error);
352 }
353 });
354 }
355 },
356
357 /**
358 * Render backup codes.
359 *
360 * @param {Array} codes Array of backup codes.
361 * @param {string} selector Container selector.
362 */
363 renderBackupCodes: function(codes, selector) {
364 var $container = $(selector);
365 $container.empty();
366
367 codes.forEach(function(code) {
368 $container.append('<code>' + code + '</code>');
369 });
370 },
371
372 /**
373 * Format secret for display (add spaces).
374 *
375 * @param {string} secret The raw secret.
376 * @return {string} Formatted secret.
377 */
378 formatSecret: function(secret) {
379 return secret.match(/.{1,4}/g).join(' ');
380 },
381
382 /**
383 * Download backup codes as a text file.
384 */
385 downloadBackupCodes: function() {
386 if (!this.downloadContent) {
387 return;
388 }
389
390 var blob = new Blob([this.downloadContent], { type: 'text/plain' });
391 var url = URL.createObjectURL(blob);
392 var a = document.createElement('a');
393 a.href = url;
394 a.download = 'atomicedge-backup-codes.txt';
395 document.body.appendChild(a);
396 a.click();
397 document.body.removeChild(a);
398 URL.revokeObjectURL(url);
399 }
400 };
401
402 // Initialize on document ready.
403 $(document).ready(function() {
404 if ($('#atomicedge-2fa-section').length) {
405 AtomicEdge2FA.init();
406 }
407 });
408
409 })(jQuery);
410