PageSourceSearch

https://app.xtenav.com/static/js/twofactor-login.js?v=3

js xtenav.com collected 2026-10-02 10:55:01 UTC 21,584 bytes, 609 lines download raw bytes

1$(document).ready(function () {
2    const loginForm = $('.form-simple');
3    const loginButton = $('#loginSubmitButton');
4
5    // FontAwesome icons
6    const faExclamation = '<i class="fa fa-exclamation-circle" aria-hidden="true"></i>';
7    const faCheck = '<i class="fa fa-check" aria-hidden="true"></i>';
8
9    let enabled2fa = false;
10
11    let userEmailAddress;
12    let userMobileNumber;
13
14    let userPassword;
15
16    let number_or_email;
17
18    let otpChannel; // 'EMAIL' OR 'MOBILE'
19
20    let disableSendOtp = false;
21    let disableVerifyOtp = false;
22    let is_plivo_stop_received = false;
23
24
25    // Prevent form submit by default
26    loginForm.submit(function (event) {
27        event.preventDefault();
28    })
29
30    loginButton.on('click', function () {
31        // Don't proceed if email address or password field is empty
32        if ($('#user-email').val().trim() === '' || $('#user-password').val().trim() === '') {
33            loginForm.unbind('submit').submit();
34            return;
35        }
36
37        validatePasswordAndCheck2faStatus();
38    })
39
40    function validatePasswordAndCheck2faStatus() {
41        userEmailAddress = $('#user-email').val();
42        userPassword = $('#user-password').val();
43
44        /*
45        // Send password as an encoded string
46        if (userPassword != '') {
47            try {
48                userPassword = window.btoa(userPassword);
49            } catch (error) {
50                userPassword = '';
51            }
52        }
53        */
54
55        $.ajax({
56            type: 'GET',
57            url: base_url + 'validate_password_2fa/',
58            dataType: 'json',
59            data: {
60                'user-email': userEmailAddress,
61                'user-password': userPassword
62            },
63            statusCode: {
64                // Incorrect password entered
65                422: function () {
66                    loginForm.unbind('submit').submit();
67                }
68            },
69            success: function (resp) {
70                is_plivo_stop_received = resp['context']['is_plivo_stop_received']
71                final_url = resp['context']['final_url']
72                user_id = resp['context']['user_id']
73
74                via_plivo_webhook = resp['context']['via_plivo_webhook']
75                via_xten_av_link = resp['context']['via_xten_av_link']
76                
77                enabled2fa = resp['context']['enabled_2fa'];
78                const device_remembered = resp['context']['device_remembered']
79                const incorrectAttemptsLock = resp['context']['incorrect_attempts_lock']
80                const otpRequestsLock = resp['context']['otp_requests_lock']
81                const resetRequestsLockTime = new Date(resp['context']['otp_requests_lock_reset_time']);
82                const twoFaRender = resp['context']['two_fa_render'];
83                const ssoEnable= resp['context']['sso_enabled'];
84
85                userMobileNumber = resp['context']['number'];
86
87                // Continue with login if user has not enabled 2FA or has remembered this device
88
89                if (!enabled2fa || (enabled2fa && device_remembered) || ssoEnable) {
90                    loginForm.unbind('submit').submit();
91                    return;
92                }
93                // else{
94                //     loginForm.unbind('submit').submit();
95                //     window.location.href = base_url + 'enable_2fa_login_form';
96                // }
97
98
99                // Prepend Two Factor form to login section and hide login form
100                // $('.login-section').prepend(twoFaRender);
101
102                // Show appropriate message to user and disable access to account
103                // if user is locked out of his account
104                if (enabled2fa && incorrectAttemptsLock) {
105                    tooManyIncorrectAttemptsLock();
106                } else if (enabled2fa && otpRequestsLock) {
107                    disableAdditionalOtpRequests();
108                    createRequestLockTimer(resetRequestsLockTime);
109                } else {
110                    // Only add event listeners to buttons if user is not locked out of accessing his account
111                    addTwoFactorEventListeners();
112                }
113
114                $('.login-section .login-section-main').addClass('d-none');
115                $('#twoFactorForm2fa').removeClass('d-none');
116
117                $('.right-sec .content-box .head').text('Two-Factor Authentication');
118                $('.right-sec .content-box .text').text('You have enabled-Two-Factor Authentication for your account. Follow the instructions to log in to the system.');
119                $('.right-sec .content-box .btn-wrapper').addClass('d-none');
120            }
121        });
122    }
123
124    function sendEmailOtp() {
125        // Do nothing if send OTP action is disabled
126        if (disableSendOtp) {
127            return;
128        }
129
130        otpChannel = 'EMAIL';
131
132        // Initiate send OTP procedure if all checks above are passed
133        sendOtp();
134    }
135
136    function sendMobileOtp() {
137        // Do nothing if send OTP action is disabled
138        if (disableSendOtp) {
139            return;
140        }
141
142        otpChannel = 'MOBILE';
143
144        // Initiate send OTP procedure if all checks above are passed
145        sendOtp();
146    }
147
148     function resubscribeNotifications() {
149      $.ajax({
150        type: 'POST',
151        url: base_url + 'user-opt-in-status/',
152        contentType: 'application/json',
153        data: JSON.stringify({
154          user_id: user_id,  // encoded uid
155          is_subscribed: true
156        }),
157        success: function(response) {
158          if (response.success) {
159            swal("Success!", "OTP sent successfully!", "success");
160            swal.close();
161            window.location.reload();
162          } else {
163            swal("Error!", "Something went wrong. Please try again.", "error");
164          }
165        },
166        error: function(xhr) {
167          console.log('Error:', xhr.status, xhr.responseText);
168          swal("Error!", "Something went wrong. Please try again.", "error");
169        }
170      });
171    }
172
173    function sendOtp() {  
174        $('.otp-status').html('');
175
176        // Clear error message span
177        $('.otp-verify-info small').html('');
178
179        // Clear input field
180        $('.digit-group input').each(function () {
181            $(this).val('');
182        })
183
184        otpChannel === 'EMAIL' ? number_or_email = userEmailAddress : number_or_email = userMobileNumber;
185
186        if (otpChannel === 'MOBILE') {
187
188            let manageXtenLink = "";
189            let managePlivoLink = "";
190            let isXtenCase = false;
191
192            /* -------------------------
193                CASE 1: PLIVO STOP
194            --------------------------*/
195            if (via_plivo_webhook) {
196                managePlivoLink = `
197                You’ve opted out of SMS messages on this number, so we can’t send OTP right now.
198                To continue, reply <strong>START</strong> to 
199                <span style="white-space:nowrap; font-weight:600;">+1 (747-399-7227)</span>
200                from your phone. Once reactivated, you can verify your number.
201                `;
202            }
203
204            /* -------------------------
205                CASE 2: XTEN STOP
206            --------------------------*/
207            if (!via_plivo_webhook && via_xten_av_link) {
208                isXtenCase = true;
209
210                manageXtenLink = `
211                <div style="margin-top:12px; text-align:center;">
212                    <div style="text-align:left; margin-bottom:10px;">
213                        You’ve opted out of SMS messages on this number, so we can’t send OTP right now.
214                        To continue, Click <strong>Enable SMS</strong> to start receiving OTPs and important account updates again.
215                    </div>
216
217                    <button id="resubscribeBtn" 
218                    style="
219                        background-color: #2863a7;
220                        color: white;
221                        border: none;
222                        padding: 10px 16px;
223                        border-radius: 6px;
224                        cursor: pointer;
225                    ">
226                    Enable SMS
227                    </button>
228                </div>
229                `;
230            }
231
232
233            /* -------------------------
234                SHOW MODAL
235            --------------------------*/
236            if (String(is_plivo_stop_received).toLowerCase() === "true") {
237
238                swal({
239                title: "SMS Notifications Paused",
240                content: {
241                    element: "div",
242                    attributes: {
243                    innerHTML: `
244                        <div style="text-align:left;">
245                        ${managePlivoLink}
246                        ${manageXtenLink}
247                        </div>
248                    `
249                    }
250                },
251                icon: "warning",
252
253                button: isXtenCase ? null : "OK"  
254                }).then(() => {
255                if (!isXtenCase) {
256                    location.reload();
257                }
258                });
259
260
261                if (isXtenCase) {
262                setTimeout(function() {
263                    var btn = document.getElementById('resubscribeBtn');
264                    if (btn) {
265                    btn.addEventListener('click', function() {
266                        btn.innerText = "Enabling...";
267                        btn.disabled = true;
268
269                        resubscribeNotifications(); 
270                    });
271                    }
272                }, 300);
273                }
274
275                return;
276            }
277        }
278        
279        // Disable multiple OTP sends if user clicks multiple times simultaneously
280        $('.otp-btn-group button').addClass('no-pointer');
281
282        $.ajax({
283            type: 'GET',
284            url: base_url + 'send_otp_2fa/',
285            dataType: 'json',
286            data: {
287                'user-email': userEmailAddress,
288                'user-password': userPassword,
289                'channel': otpChannel,
290                'process': 'LOGIN',
291                'number_or_email': number_or_email
292            },
293            statusCode: {
294                403: function (resp) {
295                    disableAdditionalOtpRequests();
296
297                    const minutes = 60;
298                    const currentTime = new Date();
299                    const resetTime = new Date(currentTime.getTime() + minutes * 60 * 1000);
300
301                    createRequestLockTimer(resetTime)
302                },
303                404: function () {
304                    showOtpMessage(action = 'sendOtp', messageType = 'otpSendFailure');
305                },
306                422: function () {
307                    // Invalid Email/ Phone Number
308                    alert('Invalid email/ phone..error message yet to handle');
309                },
310                500: function () {
311                    showOtpMessage(action = 'sendOtp', messageType = 'otpSendFailure');
312                }
313            },
314            success: function () {
315                showOtpMessage(action = 'sendOtp', messageType = 'otpSendSuccess');
316
317                // Create resend timer
318                createResendTimer();
319            }
320        }).always(function() {
321            $('.otp-btn-group button').removeClass('no-pointer');
322        })
323    }
324
325    function verifyOtp() {
326        // Do nothing if verify OTP action is disabled
327        if (disableVerifyOtp) {
328            return;
329        }
330        
331        $('.otp-verify-info small').html('');
332
333        let otpInput = '';
334
335        $('.digit-group input').each(function () {
336            otpInput += $(this).val();
337        })
338
339        // Disable user to send multiple verify requests simultaneously
340        $('#verifyOtp2fa').addClass('no-pointer');
341
342        $.ajax({
343            type: 'GET',
344            url: base_url + 'verify_otp_2fa',
345            dataType: 'json',
346            data: {
347                'user-email': userEmailAddress,
348                'user-password': userPassword,
349                'channel': otpChannel,
350                'process': 'LOGIN',
351                'code': otpInput,
352                'number_or_email': otpChannel === 'EMAIL' ? userEmailAddress : userMobileNumber
353            },
354            statusCode: {
355                403: function () {
356                    // Too many incorrect OTP attempts
357                    tooManyIncorrectAttemptsLock();
358                },
359                404: function () {
360                    showOtpMessage(action = 'verifyOtp', messageType = 'otpAuthenticationFailed');
361                },
362                409: function () {
363                    showOtpMessage(action = 'verifyOtp', messageType = 'otpExpired');
364                },
365                422: function () {
366                    showOtpMessage(action = 'verifyOtp', messageType = 'otpIncorrect');
367                },
368                500: function () {
369                    showOtpMessage(action = 'verifyOtp', messageType = 'otpAuthenticationFailed');
370                }
371            },
372            success: function () {
373                showOtpMessage(action = 'verifyOtp', messageType = 'otpVerified');
374
375                if ($('#rememberDevice').prop('checked')) {
376                    loginForm[0].rememberDevice2fa.value = true;
377
378                    const deviceDescription = platform.toString();
379                    loginForm[0].deviceDescription.value = deviceDescription;
380
381                }
382
383                // Submit form and login the user if OTP is successfully verified
384                loginForm.unbind('submit').submit();
385            },
386            error: function() {
387                $('#verifyOtp2fa').removeClass('no-pointer');
388            }
389        });
390    }
391
392    function showOtpMessage(action = null, messageType = null) {
393        /*
394        action: On what action did the error occur: Valid values are: 'sendOtp', 'verifyOtp'
395        messageType: What kind of message to show. Valid values are:
396        action === 'sendOtp':
397        	Error Messages: otpSendFailure
398        	Success Messages: otpSendSuccess
399        action === 'verifyOtp':
400        	Error Messages: otpIncorrect, otpExpired, otpAuthenticationFailed
401        	Success Messages: otpVerified
402        */
403
404        const otpStatusSpan = $('.otp-status');
405        const verifyOtpInfoSpan = $('.otp-verify-info small');
406
407        const messageObject = {
408            'sendOtp': {
409                // Error Messages
410                'otpSendFailure': `${faExclamation} Unable to send OTP. Please try again after some time`,
411
412                // Success Messages
413                'otpSendSuccess': `${faCheck} OTP sent successfully`
414            },
415            'verifyOtp': {
416                // Error Messages
417                'otpIncorrect': `${faExclamation} Incorrect OTP entered`,
418                'otpExpired': `${faExclamation} Your OTP has expired. Please resend OTP and try again`,
419                'otpAuthenticationFailed': `${faExclamation} Unable to verify OTP. Please resend OTP and try again`,
420
421                // Success Messages
422                'otpVerified': `${faCheck} OTP verified! Signing you in`
423            }
424        }
425
426        if (action === 'sendOtp') {
427            if (messageType === 'otpSendSuccess') {
428                otpStatusSpan.removeClass('red').addClass('green');
429            } else {
430                otpStatusSpan.removeClass('green').addClass('red');
431            }
432
433            otpStatusSpan.html(messageObject[action][messageType]);
434
435        } else if (action === 'verifyOtp') {
436            if (messageType === 'otpVerified') {
437                verifyOtpInfoSpan.removeClass('red').addClass('green');
438            } else {
439                verifyOtpInfoSpan.removeClass('green').addClass('red');
440            }
441
442            verifyOtpInfoSpan.html(messageObject[action][messageType]);
443        }
444    }
445
446    function createResendTimer() {
447        const resendTimerSpan = $('.otp-status');
448
449        let seconds = 57;
450
451        // Disable user to send OTP until timer expires
452        disableSendOtp = true;
453        $('#sendEmailOtp').prop('disabled', true);
454        $('#sendMobileOtp').prop('disabled', true);
455
456        setTimeout(function () {
457            let timer = setInterval(function () {
458                resendTimerSpan.removeClass('green');
459                resendTimerSpan.text(`Resend in ${seconds}s`);
460
461                // Remove timer and enable send OTP buttons when timer expires
462                if ((seconds <= 0) && disableVerifyOtp) {
463                    clearInterval(timer);
464                    resendTimerSpan.text('');
465                } else if ((seconds <= 0 && !disableVerifyOtp)) {
466                    clearInterval(timer);
467                    
468                    disableSendOtp = false;
469
470                    $('#sendEmailOtp').prop('disabled', false);
471                    $('#sendMobileOtp').prop('disabled', false);
472
473                    resendTimerSpan.text('');
474                }
475
476                seconds --;
477
478            }, 1000) // Update timer every second
479        }, 3000); // Show resend timer 3 secs after being initialized
480    }
481
482    function disableAdditionalOtpRequests() {
483        /*
484        Disables user to send additional OTP requests if he/ she has exceeded the number of
485        OTP requests allowed within a certain time period
486        */
487
488        // Clear other message spans
489        $('.otp-verify-info span').html('');
490        $('.otp-attempts-warning span').html('');
491
492        // Disable user to send and verify OTP
493        disableSendOtp = true;
494        disableVerifyOtp = true;
495
496        $('#sendEmailOtp').prop('disabled', true);
497        $('#sendMobileOtp').prop('disabled', true);
498        $('#verifyOtp2fa').prop('disabled', true);
499
500        const otpStatusSpan = $('.otp-status');
501        otpStatusSpan.removeClass('red');
502        $('.otp-status-container').addClass('status-container-card');
503        
504        otpStatusSpan.html(`
505			Your sign in has been blocked because you have exceeded the number of OTP requests available to you at this time
506			<br>
507			Please try signing in after
508			<span class="otp-requests-lock-timer"></span>
509		`)
510    }
511
512    function tooManyIncorrectAttemptsLock() {
513        const contactSupportAnchor = '<a href="mailto:[email protected]">[email protected]</a>';
514
515        // Clear other error message spans
516        $('.otp-status span').html('');
517        $('.otp-verify-info span').html('');
518
519        // Remove sign in button and footer
520        $('#twoFactorForm2fa .card-body .btn-wrapper').remove();
521        $('.login-section .footer-text').remove();
522
523        disableSendOtp = true;
524        disableVerifyOtp = true;
525
526        $('#sendEmailOtp').prop('disabled', true);
527        $('#sendMobileOtp').prop('disabled', true);
528
529        // Update locked out status card text and unhide it
530        $('.locked-status-card .card-body p:nth-child(1)').html(`
531			You are locked out of your account because you have made too many incorrect login attempts
532		`)
533
534        $('.locked-status-card .card-body p:nth-child(2)').html(`
535			Please contact at ${contactSupportAnchor} to unblock your account
536		`)
537
538        $('.locked-status-card').removeClass('d-none');
539    }
540
541    function processKeyUp(e) {
542        const keyCode = e.keyCode;
543        const index = $('.digit-group').find('input').index(this);
544        const length = (this.id === 'userNumber' ? this.innerText.length : this.value.length);
545
546        if ($(this).parent().hasClass('digit-group') && [8, 37, 46].includes(keyCode)) {
547            if (index != 0) {
548                $('.digit-group').find('input').eq(index - 1).focus();
549            }
550        } else if ($(this).parent().hasClass('digit-group') && (length == 1 || keyCode == 39)) {
551            $(this).next('input').focus();
552        }
553
554    }
555
556    function processKeyDown(e) {
557        const key = e.key;
558        const keyCode = e.keyCode;
559        const acceptedKeyCodes = [8, 37, 39, 46]; // Accepted characters apart from number inputs
560        this.id === 'userNumber' ? acceptedKeyCodes.push(61) : ''; // accept '+' key if input field is for mobile
561        const isValid = (keyCode >= 48 && keyCode <= 57) || (keyCode >= 96 && keyCode <= 105) || acceptedKeyCodes.includes(keyCode)
562
563        const accepted = acceptedKeyCodes.includes(keyCode);
564
565        if (isNaN(key) && !accepted || keyCode == 32) {
566            e.preventDefault();
567        }
568
569    }
570
571    function createRequestLockTimer(resetTime) {
572        let timeDelta = resetTime - new Date();
573
574        if (timeDelta > 0) {
575            let timer = setInterval(function () {
576                const seconds = Math.floor(timeDelta / 1000);
577                const minutes = Math.floor(seconds / 60);
578
579                if (minutes >= 1) {
580                    $('.otp-requests-lock-timer').text(`${minutes} minute(s)`);
581                } else {
582                    $('.otp-requests-lock-timer').text(`${seconds} second(s)`);
583                }
584
585
586                timeDelta -= 1000;
587
588                if (seconds <= 1) {
589                    clearInterval(timer);
590                    window.location.reload();
591                }
592            }, 1000);
593        }
594    }
595
596    function addTwoFactorEventListeners() {
597        // Send OTP buttons
598        $('#sendEmailOtp').on('click', sendEmailOtp);
599        $('#sendMobileOtp').on('click', sendMobileOtp);
600
601        // Verify OTP/ Sign in button
602        $('#verifyOtp2fa').on('click', verifyOtp);
603
604        // Handle keyup and keydown on otp input fields
605        $('.digit-group input').on('keyup', processKeyUp);
606        $('.digit-group input').on('keydown', processKeyDown);
607    }
608
609})

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.