@sowell/quasar-app-extension-auth 1.7.0 → 1.7.1

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sowell/quasar-app-extension-auth",
3
- "version": "1.7.0",
3
+ "version": "1.7.1",
4
4
  "description": "A Quasar app extension to manage Authentication",
5
5
  "main": "src/index.js",
6
6
  "repository": "https://github.com/sowell-ai/quasar.extension.auth",
@@ -93,7 +93,7 @@
93
93
  margin: 12px 0;
94
94
  text-decoration: underline;
95
95
  "
96
- @click="isMagicLinkEnabled ? goToForgotPassword() : (forgottenPassword = true)"
96
+ @click="forgottenPassword = true"
97
97
  data-testid="ask-password-change-btn"
98
98
  data-cy="forgot-password">
99
99
  {{ $t("auth.forgottenPassword") }}
@@ -247,8 +247,7 @@ export default defineComponent({
247
247
  retypeEmail,
248
248
  isSubmitBtnDisabled,
249
249
  goToOtpValidation,
250
- goToFirstLogin,
251
- goToForgotPassword
250
+ goToFirstLogin
252
251
  } = useAuth()
253
252
 
254
253
  const { localStorage } = useQuasar()
@@ -336,8 +335,7 @@ export default defineComponent({
336
335
  handleSubmit,
337
336
  resetPassword,
338
337
  goToPrivacyPolicy,
339
- goToFirstLogin,
340
- goToForgotPassword
338
+ goToFirstLogin
341
339
  }
342
340
  }
343
341
  })
@@ -151,8 +151,7 @@
151
151
  :loading="loading"
152
152
  type="submit"
153
153
  class="full-width q-mt-lg submit-button"
154
- :class="{ 'submit-button--soft': mode !== 'otp' && !canSubmit }"
155
- :disable="mode === 'otp' ? !canSubmit : false">
154
+ :class="{ 'submit-button--soft': !canSubmit }">
156
155
  <q-icon name="lock_open" size="16px" class="q-mr-sm" />
157
156
  {{ $t("common.saveNewPassword") }}
158
157
  </q-btn>
@@ -420,18 +419,23 @@ export default defineComponent({
420
419
  border-radius: 8px;
421
420
  padding: 16px;
422
421
  display: grid;
423
- grid-template-columns: repeat(2, 1fr);
422
+ grid-template-columns: repeat(2, minmax(0, 1fr));
424
423
  gap: 10px 16px;
425
424
  }
426
425
 
427
426
  .requirement-item {
428
427
  display: flex;
429
428
  align-items: center;
429
+ min-width: 0;
430
430
  font-size: 12px;
431
431
  font-weight: 500;
432
432
  color: #3e484f;
433
433
  }
434
434
 
435
+ .requirement-item span {
436
+ overflow-wrap: anywhere;
437
+ }
438
+
435
439
  .requirement-item .q-icon {
436
440
  color: #bdc8d1;
437
441
  }
@@ -61,10 +61,6 @@ export function useAuth() {
61
61
  push(`${LOCAL_FIRST_LOGIN_ROUTE || "/first-login"}?context=activate`)
62
62
  }
63
63
 
64
- const goToForgotPassword = () => {
65
- push(LOCAL_FIRST_LOGIN_ROUTE || "/first-login")
66
- }
67
-
68
64
  const email = ref("")
69
65
  const password = ref("")
70
66
  const msalToken = ref("")
@@ -285,7 +281,6 @@ export function useAuth() {
285
281
  msalAuth,
286
282
  goToOtpValidation,
287
283
  goToFirstLogin,
288
- goToForgotPassword,
289
284
  onSubmit,
290
285
  retypeEmail,
291
286
  isSubmitBtnDisabled,
@@ -34,6 +34,7 @@ export function useResetPassword() {
34
34
  }
35
35
  type ValidatableInput = { resetValidation: () => void }
36
36
  const confirmPasswordInput = ref<ValidatableInput | null>(null)
37
+ const highlightInvalidRequirements = ref(false)
37
38
 
38
39
  const { patch, post } = $api()
39
40
  const router = $router()
@@ -71,19 +72,36 @@ export function useResetPassword() {
71
72
  hasSpecialChar.value
72
73
  )
73
74
 
74
- // NOTE: `invalid` always false here (highlight-on-invalid-submit is a magiclink-only
75
- // affordance, see useValidateMagicLink) - kept on every item so ResetPassword.vue can
76
- // bind the same class regardless of which composable (OTP vs magiclink) it's using.
77
75
  const passwordRequirements = computed(() => [
78
- { label: t("auth.passwordRequirement.minLength"), valid: hasMinLength.value, invalid: false },
79
- { label: t("auth.passwordRequirement.uppercase"), valid: hasUppercase.value, invalid: false },
80
- { label: t("auth.passwordRequirement.lowercase"), valid: hasLowercase.value, invalid: false },
81
- { label: t("auth.passwordRequirement.number"), valid: hasNumber.value, invalid: false },
82
- { label: t("auth.passwordRequirement.letter"), valid: hasLetter.value, invalid: false },
76
+ {
77
+ label: t("auth.passwordRequirement.minLength"),
78
+ valid: hasMinLength.value,
79
+ invalid: highlightInvalidRequirements.value && !hasMinLength.value
80
+ },
81
+ {
82
+ label: t("auth.passwordRequirement.uppercase"),
83
+ valid: hasUppercase.value,
84
+ invalid: highlightInvalidRequirements.value && !hasUppercase.value
85
+ },
86
+ {
87
+ label: t("auth.passwordRequirement.lowercase"),
88
+ valid: hasLowercase.value,
89
+ invalid: highlightInvalidRequirements.value && !hasLowercase.value
90
+ },
91
+ {
92
+ label: t("auth.passwordRequirement.number"),
93
+ valid: hasNumber.value,
94
+ invalid: highlightInvalidRequirements.value && !hasNumber.value
95
+ },
96
+ {
97
+ label: t("auth.passwordRequirement.letter"),
98
+ valid: hasLetter.value,
99
+ invalid: highlightInvalidRequirements.value && !hasLetter.value
100
+ },
83
101
  {
84
102
  label: t("auth.passwordRequirement.specialChar"),
85
103
  valid: hasSpecialChar.value,
86
- invalid: false
104
+ invalid: highlightInvalidRequirements.value && !hasSpecialChar.value
87
105
  }
88
106
  ])
89
107
 
@@ -162,6 +180,11 @@ export function useResetPassword() {
162
180
  }
163
181
 
164
182
  const onSubmit = () => {
183
+ if (!canSubmit.value) {
184
+ highlightInvalidRequirements.value = true
185
+ return
186
+ }
187
+
165
188
  let idUser = decodedToken?.value?.sub
166
189
  if (isOtp.value && userInfos?.uid) {
167
190
  idUser = userInfos?.uid
@@ -233,14 +256,16 @@ export function useResetPassword() {
233
256
  router.push(LOCAL_LOGIN_ROUTE)
234
257
  }
235
258
 
236
- // NOTE: OTP flow never expires this way / never blocks submit / has nowhere to send
237
- // the user back to "first login" - these are inert magiclink-only affordances, kept
238
- // here only so ResetPassword.vue has a uniform shape regardless of composable used.
259
+ // NOTE: OTP flow never expires this way / has nowhere to send the user back to
260
+ // "first login" - these are inert magiclink-only affordances, kept here only so
261
+ // ResetPassword.vue has a uniform shape regardless of composable used.
239
262
  const linkExpired = ref(false)
240
263
  const serverError = ref("")
241
- const highlightInvalidRequirements = ref(false)
242
264
  const goToFirstLogin = goToLoginPage
243
- const handleInvalidSubmit = () => {}
265
+
266
+ const handleInvalidSubmit = () => {
267
+ highlightInvalidRequirements.value = true
268
+ }
244
269
 
245
270
  onMounted(() => {
246
271
  if (!isOtp.value) {
@@ -21,7 +21,8 @@ export function useMagicLink() {
21
21
  const {
22
22
  AUTH_SERVER_REQUEST_MAGICLINK_ROUTE,
23
23
  LOCAL_SET_PASSWORD_ROUTE,
24
- LOCAL_LOGIN_ROUTE
24
+ LOCAL_LOGIN_ROUTE,
25
+ APP_SERVER_BASE_URL
25
26
  } = $prompts()
26
27
 
27
28
  const email = ref("")
@@ -61,16 +62,13 @@ export function useMagicLink() {
61
62
  return true
62
63
  }
63
64
 
65
+ // Built from the configured APP_SERVER_BASE_URL (fixed per app in
66
+ // quasar.extensions.json), never from window.location: in a Capacitor build
67
+ // (APK/IPA) the WebView origin is something like capacitor://localhost, not the
68
+ // app's real public domain, so the magic link would otherwise be unusable once
69
+ // installed as a native app.
64
70
  const buildBaseUrl = (): string => {
65
- if (typeof window === "undefined") return ""
66
- // DEV-ONLY BYPASS: MAGICLINK_ALLOWED_HOSTS rejects a bare "localhost" base_url
67
- // (422). Substitute localhost.sowell.app (port kept) so the link is accepted -
68
- // requires the local DNS to resolve localhost.sowell.app -> 127.0.0.1.
69
- // Never let this reach production (see playbook piège #2).
70
- const { protocol, hostname, port } = window.location
71
- const effectiveHostname = hostname === "localhost" ? "localhost.sowell.app" : hostname
72
- const origin = `${protocol}//${effectiveHostname}${port ? `:${port}` : ""}`
73
- return `${origin}${LOCAL_SET_PASSWORD_ROUTE || "/set-password"}`
71
+ return `${APP_SERVER_BASE_URL}${LOCAL_SET_PASSWORD_ROUTE || "/set-password"}`
74
72
  }
75
73
 
76
74
  const readApiError = (error: unknown): string | undefined => {