PageSourceSearch

https://www.landmarkoutreach.org/wp-content/plugins/wp-2fa-premium…/passkeys/assets/js/user-login.js?ver=4.0.0

js landmarkoutreach.org collected 2026-10-02 11:06:14 UTC 7,316 bytes, 268 lines download raw bytes

1import { browserSupportsWebAuthn, browserSupportsWebAuthnAutofill, startAuthentication } from "./index.js";
2
3/**
4 * Authenticate Passkey.
5 */
6async function authenticate( username, redirectTo ) {
7	let asseResp;
8	let requestId;
9	try {
10		const response = await wp.apiFetch({
11			path: '/wp-2fa-passkeys/v1/singin/request',
12			method: 'POST',
13			data: { 'user': username },
14		});
15
16		const { options, request_id } = response;
17
18		requestId = request_id;
19		asseResp = await startAuthentication(options);
20	} catch (error) {
21		throw error;
22	}
23
24	// POST the response to the endpoint that calls.
25	try {
26		const response = await wp.apiFetch({
27			path: '/wp-2fa-passkeys/v1/singin/response',
28			method: 'POST',
29			data: {
30				request_id: requestId,
31				asseResp,
32				'user': username,
33				'redirect_to': redirectTo,
34			},
35		});
36
37		if (response.status !== 'verified') {
38			throw new Error('Passkey authentication failed. Method is not set?');
39		}
40
41		let iframe = !(window === window.parent); // interim login ?
42
43		if (iframe) {
44			var someIframe = window.parent.document.getElementById('wp-auth-check-wrap');
45			someIframe.parentNode.removeChild(someIframe);
46		} else {
47
48			let redirect_to = '';
49
50			if (response.redirect_to && '' !== response.redirect_to) {
51				redirect_to = response.redirect_to;
52			} else {
53				// Get redirect_to from query string.
54				const urlParams = new URLSearchParams(window.location.search);
55				redirect_to = urlParams.get('redirect_to') || '/wp-admin';
56			}
57
58			// Validate redirect is same-origin to prevent open redirect attacks.
59			try {
60				const parsed = new URL(redirect_to, window.location.origin);
61				if (parsed.origin !== window.location.origin) {
62					redirect_to = '/wp-admin';
63				}
64			} catch (e) {
65				if (!redirect_to.startsWith('/')) {
66					redirect_to = '/wp-admin';
67				}
68			}
69
70			// Redirect to redirect url or wp-admin as default.
71			window.location.href = redirect_to;
72		}
73	} catch (error) {
74		throw error;
75	}
76}
77
78/**
79 * Show error message.
80 *
81 * @param {string} message Error message.
82 */
83function showError(message) {
84	let loginForm = document.getElementById('loginform');
85
86	if ( !loginForm ) {
87		loginForm = document.getElementsByClassName('woocommerce-form woocommerce-form-login login')[0];
88	}
89
90	if ( !loginForm ) {
91		return;
92	}
93
94	// Create Error element if not exists.
95	const errorElement = document.createElement('div');
96	errorElement.id = 'login_error';
97	errorElement.className = 'notice notice-error';
98	errorElement.textContent = message;
99
100	// Add error element before login form.
101	loginForm.parentNode.insertBefore(errorElement, loginForm);
102
103	loginForm.classList.add('shake');
104}
105
106async function delay(time) {
107	return new Promise(resolve => setTimeout(resolve, time));
108}
109
110function onClick() {
111
112	// create invisible dummy input to receive the focus first
113	const fakeInput = document.createElement('input')
114	fakeInput.setAttribute('type', 'text')
115	fakeInput.style.position = 'absolute'
116	fakeInput.style.opacity = 0
117	fakeInput.style.height = 0
118	fakeInput.style.fontSize = '16px' // disable auto zoom
119
120	// you may need to append to another element depending on the browser's auto 
121	// zoom/scroll behavior
122	document.body.prepend(fakeInput)
123
124	// focus so that subsequent async focus will work
125	fakeInput.focus()
126
127	setTimeout(() => {
128
129		// now we can focus on the target input
130		document.getElementById('user_login').focus()
131
132		// cleanup
133		fakeInput.remove()
134
135	}, 1000)
136
137}
138
139wp.domReady(async () => {
140	// If the browser doesn't support WebAuthn, don't do anything.
141	if (!browserSupportsWebAuthn()) {
142		return;
143	}
144
145	// var $user_password = jQuery( '.user-pass-wrap' );
146	// $user_password.hide();
147
148	let usernameField = document.getElementById('user_login');
149
150	if ( ! usernameField ) {
151		usernameField = document.getElementById('username');
152	}
153
154	if ( !usernameField ) {
155		return;
156	}
157
158	// add autocomplete="webauthn" to the username field.
159	if (usernameField) {
160		usernameField.setAttribute('autocomplete', 'username webauthn');
161	}
162
163	if (browserSupportsWebAuthnAutofill()) {
164
165		const usePasskeysButton = document.querySelector('.wp-2fa-login-via-passkey');
166		const useStandardButton = document.querySelector('.wp-2fa-login-standard');
167
168		// Helper to detect if the password field is currently visible
169		const isPasswordVisible = () => {
170			let $user_password = jQuery('.user-pass-wrap');
171			if (!$user_password.length) {
172				$user_password = jQuery(jQuery('.woocommerce-form-row.woocommerce-form-row--wide.form-row.form-row-wide')[1]);
173			}
174			return $user_password.length ? $user_password.is(':visible') : false;
175		};
176
177		if ( usePasskeysButton ) {
178			usePasskeysButton.addEventListener('click', async () => {
179
180				if ( useStandardButton ) {
181					const standardLoginWrap = jQuery( '#wp-2fa-standard-login-wrapper' );
182					standardLoginWrap.show();
183				}
184
185				let $user_password = jQuery( '.user-pass-wrap' );
186
187				if ( ! $user_password.length ) {
188					$user_password = jQuery(jQuery( '.woocommerce-form-row.woocommerce-form-row--wide.form-row.form-row-wide')[1]);
189				}
190				if ($user_password.is(":visible")) {
191					$user_password.hide();
192
193					jQuery( 'p.forgetmenot' ).hide();
194					jQuery( 'p.submit' ).hide();
195
196					jQuery( 'button[name="login"]' ).parent().hide();
197
198					return;
199				}
200
201				jQuery( '#user_login' ).prop( 'required', false );
202				jQuery( '#user_pass' ).prop( 'required', false );
203
204				if ('' === usernameField.value) {
205					showError('Please enter your username or email address to use Passkey login.');
206
207					return;
208				}
209
210				// Collect redirect input value with fallbacks: redirect_to -> redirect -> 'wp-admin/'
211				let redirectTo = '';
212				const redirectInput = document.querySelector('input[name="redirect_to"]') || document.querySelector('input[name="redirect"]');
213				if (redirectInput && redirectInput.value && redirectInput.value.trim() !== '') {
214					redirectTo = redirectInput.value;
215				} else {
216					redirectTo = '';
217				}
218
219				try {
220					await authenticate( usernameField.value, redirectTo );
221				} catch (error) {
222					showError(error.message);
223				}
224			});
225		}
226		if ( useStandardButton ) {
227			useStandardButton.addEventListener('click', async () => {
228
229				var $user_password = jQuery( '.user-pass-wrap' );
230
231				if ( ! $user_password.length ) {
232					$user_password = jQuery(jQuery( '.woocommerce-form-row.woocommerce-form-row--wide.form-row.form-row-wide')[1]);
233				}
234
235				$user_password.show();
236
237				jQuery( '#user_login' ).prop( 'required', true );
238				jQuery( '#user_pass' ).prop( 'required', true );
239
240				jQuery( 'p.forgetmenot' ).show();
241				jQuery( 'p.submit' ).show();
242
243				jQuery( 'button[name="login"]' ).parent().show();
244
245				const standardLoginWrap = jQuery( '#wp-2fa-standard-login-wrapper' );
246				standardLoginWrap.hide();
247			});
248		}
249
250		// Trigger Passkey authentication when pressing Enter on the username field
251		// if the password field is hidden (i.e., passkey flow is active).
252		if ( usePasskeysButton && usernameField ) {
253			usernameField.addEventListener('keydown', (e) => {
254				const isEnter = (e.key && e.key.toLowerCase() === 'enter') || e.keyCode === 13;
255				if (!isEnter) return;
256
257				if (!isPasswordVisible()) {
258					e.preventDefault();
259					usePasskeysButton.click();
260				}
261			});
262		}
263
264	} else {
265		const passkeyUseWrap = document.getElementById('wp-2fa-login-wrapper');
266		passkeyUseWrap.style.display = 'none';
267	}
268});

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.