@mindfulauth/core 2.0.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/authScripts/ForgotPasswordScript.astro +64 -0
  2. package/dist/authScripts/LoginScript.astro +209 -0
  3. package/dist/authScripts/MagicLoginScript.astro +62 -0
  4. package/dist/authScripts/MagicRegisterScript.astro +73 -0
  5. package/dist/authScripts/MainScript.astro +236 -0
  6. package/dist/authScripts/RegisterPasswordScript.astro +118 -0
  7. package/dist/authScripts/ResendVerificationScript.astro +51 -0
  8. package/dist/authScripts/ResetPasswordScript.astro +155 -0
  9. package/dist/authScripts/SecurityScript.astro +449 -0
  10. package/dist/authScripts/TurnstileInit.astro +112 -0
  11. package/dist/authScripts/VerifyEmailScript.astro +72 -0
  12. package/dist/authScripts/VerifyMagicLinkScript.astro +195 -0
  13. package/dist/authScripts/index.d.ts +13 -0
  14. package/dist/authScripts/index.d.ts.map +1 -0
  15. package/dist/authScripts/index.js +15 -0
  16. package/dist/components/MAuthEmailInput.astro +12 -0
  17. package/dist/components/MAuthForm.astro +10 -0
  18. package/dist/components/MAuthMessage.astro +8 -0
  19. package/dist/components/MAuthNameInput.astro +11 -0
  20. package/dist/components/MAuthPasswordChangePending.astro +8 -0
  21. package/dist/components/MAuthPasswordInput.astro +12 -0
  22. package/dist/components/MAuthSubmitButton.astro +8 -0
  23. package/dist/components/MAuthTurnstile.astro +11 -0
  24. package/dist/components/MAuthTwoFACodeInput.astro +13 -0
  25. package/dist/components/MAuthTwoFASection.astro +11 -0
  26. package/dist/components/index.d.ts +11 -0
  27. package/dist/components/index.d.ts.map +1 -0
  28. package/dist/components/index.js +13 -0
  29. package/dist/core/csp.d.ts +2 -2
  30. package/dist/core/csp.js +3 -3
  31. package/dist/layouts/MAuthProtected.astro +56 -0
  32. package/dist/layouts/MAuthPublic.astro +68 -0
  33. package/package.json +15 -9
@@ -0,0 +1,118 @@
1
+ ---
2
+ // Mindful Auth - Register Password Script Component
3
+ // Provides: Password registration form handling
4
+ ---
5
+ <script is:inline>
6
+ // Register password script - Astro Optimized
7
+ async function handleRegisterSubmit(event) {
8
+ event.preventDefault();
9
+ const form = event.target;
10
+ const messageEl = document.querySelector('[data-mindfulauth-field="message"]');
11
+ const nameInput = form.querySelector('[data-mindfulauth-field="name"]');
12
+ const emailInput = form.querySelector('[data-mindfulauth-field="email"]');
13
+ const passwordInput = form.querySelector('[data-mindfulauth-field="password"]');
14
+ const confirmPasswordInput = form.querySelector('[data-mindfulauth-field="confirm-password"]');
15
+ const submitBtn = form.querySelector('button[type="submit"]');
16
+
17
+ // Skip API call on localhost to prevent production logs pollution
18
+ const hostname = window.location.hostname;
19
+ if (hostname === 'localhost' || hostname === '127.0.0.1' || hostname.endsWith('.local')) {
20
+ messageEl.textContent = 'Registration is disabled on localhost. Use a real domain for testing.';
21
+ console.log('[Register] Blocked form submission on localhost');
22
+ return;
23
+ }
24
+
25
+ // Clear previous messages
26
+ messageEl.textContent = '';
27
+
28
+ // Client-side validation
29
+ if (!nameInput.value.trim()) {
30
+ messageEl.textContent = 'Name is required.';
31
+ return;
32
+ }
33
+
34
+ if (!emailInput.value.trim()) {
35
+ messageEl.textContent = 'Email is required.';
36
+ return;
37
+ }
38
+
39
+ if (passwordInput.value.length < 8) {
40
+ messageEl.textContent = 'Password must be at least 8 characters.';
41
+ return;
42
+ }
43
+
44
+ if (passwordInput.value !== confirmPasswordInput.value) {
45
+ messageEl.textContent = 'Passwords do not match.';
46
+ return;
47
+ }
48
+
49
+ messageEl.textContent = 'Validating password...';
50
+ submitBtn.disabled = true;
51
+
52
+ try {
53
+ // First validate password against policy
54
+ const passwordValidateResponse = await window.apiFetch('/auth/validate-password', {
55
+ body: JSON.stringify({
56
+ password: passwordInput.value
57
+ })
58
+ });
59
+
60
+ const passwordValidateResult = await passwordValidateResponse.json();
61
+ if (!passwordValidateResult.success) {
62
+ throw new Error(passwordValidateResult.message);
63
+ }
64
+
65
+ messageEl.textContent = 'Creating account...';
66
+
67
+ // Get Turnstile token
68
+ const turnstileResponse = form.querySelector('[name="cf-turnstile-response"]')?.value;
69
+ if (!turnstileResponse) {
70
+ throw new Error('Bot protection validation required.');
71
+ }
72
+
73
+ // Register user via Mindful Auth API (backend validates Turnstile)
74
+ const registerResponse = await window.apiFetch('/auth/register-password', {
75
+ body: JSON.stringify({
76
+ name: nameInput.value.trim(),
77
+ email: emailInput.value.trim(),
78
+ password: passwordInput.value,
79
+ 'cf-turnstile-response': turnstileResponse
80
+ })
81
+ });
82
+
83
+ const registerResult = await registerResponse.json();
84
+ if (!registerResult.success) {
85
+ throw new Error(registerResult.message || 'Registration failed. Please try again.');
86
+ }
87
+
88
+ // Success
89
+ messageEl.textContent = registerResult.message || 'Account created successfully! Please check your email to verify your account.';
90
+ form.reset();
91
+
92
+ // Don't auto-redirect - user needs to verify email first
93
+ // Show link to login page (using safe DOM manipulation)
94
+ setTimeout(() => {
95
+ const loginLink = document.createElement('p');
96
+ const anchor = document.createElement('a');
97
+ anchor.href = '/login';
98
+ anchor.textContent = 'Return to login page';
99
+ loginLink.appendChild(anchor);
100
+ messageEl.appendChild(loginLink);
101
+ }, 2000);
102
+
103
+ } catch (error) {
104
+ messageEl.textContent = `Error: ${error.message}`;
105
+ } finally {
106
+ submitBtn.disabled = false;
107
+ window.turnstile?.reset();
108
+ }
109
+ }
110
+
111
+ // --- MAIN EXECUTION ---
112
+ document.addEventListener('DOMContentLoaded', function() {
113
+ const form = document.querySelector('[data-mindfulauth-form="register"]');
114
+ if (!form) return;
115
+
116
+ form.addEventListener('submit', handleRegisterSubmit);
117
+ });
118
+ </script>
@@ -0,0 +1,51 @@
1
+ ---
2
+ // Mindful Auth - Resend Verification Script Component
3
+ // Provides: Resend verification email form handling
4
+ ---
5
+ <script is:inline>
6
+ // Resend verification Script - Astro Optimized
7
+ async function handleResendVerificationSubmit(event) {
8
+ event.preventDefault();
9
+ const form = event.target;
10
+ const emailInput = form.querySelector('[data-mindfulauth-field="email"]');
11
+ const messageEl = document.querySelector('[data-mindfulauth-field="message"]');
12
+ const submitBtn = form.querySelector('button[type="submit"]');
13
+
14
+ // Skip API call on localhost to prevent production logs pollution
15
+ const hostname = window.location.hostname;
16
+ if (hostname === 'localhost' || hostname === '127.0.0.1' || hostname.endsWith('.local')) {
17
+ messageEl.textContent = 'Resend verification is disabled on localhost. Use a real domain for testing.';
18
+ console.log('[Resend Verification] Blocked form submission on localhost');
19
+ return;
20
+ }
21
+
22
+ messageEl.textContent = 'Sending...';
23
+ submitBtn.disabled = true;
24
+
25
+ try {
26
+ const response = await window.apiFetch('/auth/resend-verification', {
27
+ body: JSON.stringify({
28
+ 'cf-turnstile-response': form.querySelector('[name="cf-turnstile-response"]')?.value,
29
+ email: emailInput.value
30
+ })
31
+ });
32
+
33
+ const result = await response.json();
34
+ messageEl.textContent = result.message;
35
+
36
+ } catch (error) {
37
+ messageEl.textContent = 'An error occurred. Please try again.';
38
+ } finally {
39
+ submitBtn.disabled = false;
40
+ window.turnstile?.reset();
41
+ }
42
+ }
43
+
44
+ // --- MAIN EXECUTION ---
45
+ document.addEventListener('DOMContentLoaded', function() {
46
+ const form = document.querySelector('[data-mindfulauth-form="resend-verification"]');
47
+ if (!form) return;
48
+
49
+ form.addEventListener('submit', handleResendVerificationSubmit);
50
+ });
51
+ </script>
@@ -0,0 +1,155 @@
1
+ ---
2
+ // Mindful Auth - Reset Password Script Component
3
+ // Provides: Reset password form handling with 2FA support
4
+ ---
5
+ <script is:inline>
6
+ // Reset password script - Astro Optimized
7
+ function getPathParams() {
8
+ const pathParts = window.location.pathname.split('/');
9
+ return {
10
+ recordid: pathParts[2],
11
+ token: pathParts[3]
12
+ };
13
+ }
14
+
15
+ async function handleResetSubmit(event) {
16
+ event.preventDefault();
17
+ const form = event.target;
18
+ const messageEl = document.querySelector('[data-mindfulauth-field="message"]');
19
+ const newPasswordEl = form.querySelector('[data-mindfulauth-field="new-password"]');
20
+ const confirmPasswordEl = form.querySelector('[data-mindfulauth-field="confirm-password"]');
21
+ const twoFACodeEl = form.querySelector('[data-mindfulauth-field="twofa-code"]');
22
+ const submitBtn = form.querySelector('button[type="submit"]');
23
+
24
+ // Skip API call on localhost to prevent production logs pollution
25
+ const hostname = window.location.hostname;
26
+ if (hostname === 'localhost' || hostname === '127.0.0.1' || hostname.endsWith('.local')) {
27
+ messageEl.textContent = 'Password reset is disabled on localhost. Use a real domain for testing.';
28
+ console.log('[Reset Password] Blocked form submission on localhost');
29
+ return;
30
+ }
31
+
32
+ if (newPasswordEl.value !== confirmPasswordEl.value) {
33
+ messageEl.textContent = "Passwords do not match.";
34
+ return;
35
+ }
36
+
37
+ const { recordid, token } = getPathParams();
38
+
39
+ if (!recordid || !token) {
40
+ messageEl.textContent = "Invalid or expired link. Please request a new one.";
41
+ form.style.display = 'none';
42
+ return;
43
+ }
44
+
45
+ if (newPasswordEl.value !== confirmPasswordEl.value) {
46
+ messageEl.textContent = "Passwords do not match.";
47
+ return;
48
+ }
49
+
50
+ messageEl.textContent = "Validating password...";
51
+ submitBtn.disabled = true;
52
+
53
+ try {
54
+ // First validate password against policy
55
+ const passwordValidateResponse = await window.apiFetch('/auth/validate-password', {
56
+ body: JSON.stringify({ password: newPasswordEl.value })
57
+ });
58
+
59
+ const passwordValidateResult = await passwordValidateResponse.json();
60
+ if (!passwordValidateResult.success) {
61
+ throw new Error(passwordValidateResult.message);
62
+ }
63
+
64
+ // Check if user has 2FA enabled
65
+ messageEl.textContent = 'Checking security settings...';
66
+ const statusResponse = await window.apiFetch('/auth/get-2fa-status-by-record', {
67
+ method: 'POST',
68
+ headers: { 'Content-Type': 'application/json' },
69
+ body: JSON.stringify({ recordid })
70
+ });
71
+ const statusResult = await statusResponse.json();
72
+
73
+ // If 2FA is enabled and code not provided yet, show the input and wait
74
+ let twoFACode = null;
75
+ if (statusResult.isEnabled) {
76
+ if (!twoFACodeEl || !twoFACodeEl.value) {
77
+ // Show the 2FA input field aggressively
78
+ const twoFAContainer = form.querySelector('[data-mindfulauth-field="twofa-code-container"]');
79
+ if (twoFAContainer) {
80
+ twoFAContainer.removeAttribute('hidden');
81
+ twoFAContainer.classList && twoFAContainer.classList.remove('hidden');
82
+ twoFAContainer.style.setProperty('display', 'block', 'important');
83
+ // Try common display values
84
+ if (window.getComputedStyle(twoFAContainer).display === 'none') {
85
+ twoFAContainer.style.setProperty('display', 'flex', 'important');
86
+ }
87
+ }
88
+ if (twoFACodeEl) {
89
+ twoFACodeEl.focus();
90
+ }
91
+ messageEl.textContent = '2FA code is required for password reset.';
92
+ submitBtn.textContent = 'Reset Password with 2FA';
93
+ submitBtn.disabled = false;
94
+ return;
95
+ }
96
+ twoFACode = twoFACodeEl.value;
97
+ if (twoFACode.length !== 6) {
98
+ messageEl.textContent = 'Please enter a valid 6-digit 2FA code.';
99
+ submitBtn.disabled = false;
100
+ return;
101
+ }
102
+ }
103
+
104
+ messageEl.textContent = 'Resetting password...';
105
+
106
+ const requestBody = {
107
+ 'cf-turnstile-response': form.querySelector('[name="cf-turnstile-response"]')?.value,
108
+ recordid,
109
+ token,
110
+ newPassword: newPasswordEl.value
111
+ };
112
+
113
+ if (twoFACode) {
114
+ requestBody.twoFACode = twoFACode;
115
+ }
116
+
117
+ const response = await window.apiFetch('/auth/reset-password', {
118
+ body: JSON.stringify(requestBody)
119
+ });
120
+
121
+ const result = await response.json();
122
+ if (result.success) {
123
+ messageEl.textContent = result.message;
124
+ form.style.display = 'none';
125
+ // Redirect to login page after successful reset
126
+ if (result.redirect) {
127
+ setTimeout(() => {
128
+ window.location.href = result.redirect;
129
+ }, 2000);
130
+ }
131
+ } else {
132
+ throw new Error(result.message);
133
+ }
134
+ } catch (error) {
135
+ messageEl.textContent = `Error: ${error.message}`;
136
+ } finally {
137
+ submitBtn.disabled = false;
138
+ window.turnstile?.reset();
139
+ }
140
+ }
141
+
142
+ // --- MAIN EXECUTION ---
143
+ document.addEventListener('DOMContentLoaded', function() {
144
+ const form = document.querySelector('[data-mindfulauth-form="reset-password"]');
145
+ if (!form) return;
146
+
147
+ // Initially hide 2FA field - it will be shown during submit if needed
148
+ const twoFAContainer = form.querySelector('[data-mindfulauth-field="twofa-code-container"]');
149
+ if (twoFAContainer) {
150
+ twoFAContainer.style.display = 'none';
151
+ }
152
+
153
+ form.addEventListener('submit', handleResetSubmit);
154
+ });
155
+ </script>