@sowell/quasar-app-extension-auth 0.3.20 → 0.3.22

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": "0.3.20",
3
+ "version": "0.3.22",
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",
@@ -21,7 +21,8 @@ export default boot(({ app, router }: BootInterface) => {
21
21
  if (
22
22
  (!isValid || !token) &&
23
23
  to.path !== LOCAL_LOGIN_ROUTE &&
24
- to.name !== "update_password"
24
+ to.name !== "update_password" &&
25
+ to.name !== "otp_validation"
25
26
  ) {
26
27
  next(LOCAL_LOGIN_ROUTE);
27
28
  } else {
@@ -55,17 +55,19 @@
55
55
  @click="retypeEmail"
56
56
  class="full-width"
57
57
  />
58
- <q-btn
59
- class="q-mt-md full-width"
60
- unelevated
61
- style="background: #fff"
62
- flat
63
- @click="handlePasswordUpdate"
64
- no-caps
65
- :label="$t('auth.close')"
66
- color="dark"
67
- v-close-popup
68
- />
58
+ <slot>
59
+ <q-btn
60
+ class="q-mt-md full-width"
61
+ unelevated
62
+ style="background: #fff"
63
+ flat
64
+ @click="handlePasswordUpdate"
65
+ no-caps
66
+ :label="$t('auth.close')"
67
+ color="dark"
68
+ v-close-popup
69
+ />
70
+ </slot>
69
71
  </q-card-actions>
70
72
  </q-card>
71
73
  </template>
@@ -109,10 +111,10 @@ export default defineComponent({
109
111
  );
110
112
 
111
113
  const emailNotSend = computed(
112
- () => !props.sendEmailSuccess && !props.passwordUpdated,
114
+ () => !props.sendEmailSuccess && !props.passwordUpdated
113
115
  );
114
116
  const emailSended = computed(
115
- () => props.sendEmailSuccess && !props.passwordUpdated,
117
+ () => props.sendEmailSuccess && !props.passwordUpdated
116
118
  );
117
119
 
118
120
  const retypeEmail = () => {
@@ -41,9 +41,10 @@
41
41
  :label="$t('common.reset')"
42
42
  class="btn-min"
43
43
  @click="sendEmail"
44
+ :disable="!email.length"
44
45
  v-close-popup
45
46
  />
46
-
47
+
47
48
  <q-btn
48
49
  color="primary"
49
50
  style="max-width: 330px"
@@ -9,6 +9,7 @@
9
9
  round
10
10
  no-caps
11
11
  color="dark"
12
+ v-if="!isOtp"
12
13
  v-close-popup
13
14
  />
14
15
  </div>
@@ -26,16 +27,20 @@
26
27
  </div>
27
28
  </q-card-section>
28
29
  <q-card-actions class="row full-width justify-center q-mb-lg">
29
- <q-btn
30
- class="bg-white submitBtn"
31
- unelevated
32
- outline
33
- no-caps
34
- :label="$t('auth.close')"
35
- @click="passwordUpdated ? $emit('goToLoginPage') : ''"
36
- color="primary"
37
- v-close-popup
38
- />
30
+ <div>
31
+ <slot>
32
+ <q-btn
33
+ class="bg-white submitBtn"
34
+ unelevated
35
+ outline
36
+ no-caps
37
+ :label="$t('auth.close')"
38
+ @click="passwordUpdated ? $emit('goToLoginPage') : ''"
39
+ color="primary"
40
+ v-close-popup
41
+ />
42
+ </slot>
43
+ </div>
39
44
  </q-card-actions>
40
45
  </q-card>
41
46
  </template>
@@ -53,6 +58,7 @@ export default defineComponent({
53
58
  content: { type: String, required: true },
54
59
  image: { type: String, required: true },
55
60
  passwordUpdated: { type: Boolean },
61
+ isOtp: { type: Boolean, default: false, required: false },
56
62
  },
57
63
  emit: ["goToLoginPage"],
58
64
  setup(props: any) {
@@ -125,7 +125,20 @@
125
125
  :sendEmailSuccess="sendEmailSuccess"
126
126
  :loading="loading"
127
127
  @retypeEmail="retypeEmail"
128
- />
128
+ >
129
+ <q-btn
130
+ v-if="(sendEmailSuccess && isOtp)"
131
+ class="q-mt-md full-width"
132
+ unelevated
133
+ style="background: #fff"
134
+ flat
135
+ @click="goToOtpValidation"
136
+ no-caps
137
+ :label="$t('auth.next')"
138
+ color="dark"
139
+ v-close-popup
140
+ />
141
+ </sowell-message>
129
142
  </q-dialog>
130
143
  </div>
131
144
  </q-page>
@@ -142,7 +155,7 @@
142
155
  </template>
143
156
 
144
157
  <script lang="ts">
145
- import { defineComponent, reactive, onMounted, watch } from "vue";
158
+ import { defineComponent, reactive, onMounted, computed } from "vue";
146
159
  import * as packageInfo from "app/package.json";
147
160
  import SendEmail from "../dialogs/SendEmail.vue";
148
161
  import SowellMessage from "../dialogs/Message.vue";
@@ -166,15 +179,20 @@ export default defineComponent({
166
179
  sendEmailSuccess,
167
180
  authState,
168
181
  APP_SERVER_BASE_URL,
182
+ IS_OTP,
169
183
  onSubmit,
170
184
  retypeEmail,
171
185
  isSubmitBtnDisabled,
186
+ goToOtpValidation,
172
187
  icons,
173
188
  } = useAuth();
174
189
 
175
190
  const appVersion = reactive({
176
191
  versionNumber: "",
177
192
  });
193
+
194
+ const isOtp = computed(() => IS_OTP.toLowerCase() === "y")
195
+
178
196
  const resetPassword = async (email: string) => {
179
197
  const data = {
180
198
  auth: {
@@ -185,6 +203,7 @@ export default defineComponent({
185
203
  try {
186
204
  forgottenPassword.value = false;
187
205
  await authState.resetPassword(data).then(() => {
206
+ localStorage.setItem("emailResetPassword", email);
188
207
  sendEmailSuccess.value = true;
189
208
  isReset.value = true;
190
209
  });
@@ -212,10 +231,12 @@ export default defineComponent({
212
231
  sendEmailSuccess,
213
232
  onSubmit,
214
233
  resetPassword,
234
+ goToOtpValidation,
215
235
  isSubmitBtnDisabled,
216
236
  retypeEmail,
217
237
  icons,
218
238
  appVersion,
239
+ isOtp,
219
240
  };
220
241
  },
221
242
  });
@@ -0,0 +1,177 @@
1
+ <template>
2
+ <q-layout>
3
+ <q-page class="q-pa-md row justify-center">
4
+ <div>
5
+ <div class="q-mt-xl">
6
+ <img
7
+ src="../../assets/Sowell-logo.svg"
8
+ class="q-mb-sm q-mr-md"
9
+ style="width: 190px"
10
+ />
11
+ </div>
12
+ <div
13
+ class="column justify-center full-height"
14
+ style="max-width: 550px; margin-top: -90px"
15
+ >
16
+ <div></div>
17
+ <div
18
+ class="text-subtitle1 q-mt-lg"
19
+ data-cy="validationOtp"
20
+ style="font-size: 34px; font-weight: 600; line-height: 34px"
21
+ >
22
+ {{ $t("auth.titleOtpValidation") }}
23
+ </div>
24
+ <div
25
+ class="text-subtitle1 q-mt-lg q-mb-lg"
26
+ style="font-size: 16px; font-weight: 400; line-height: 24px"
27
+ >
28
+ {{ $t("auth.subtitleOtpValidation") }}
29
+ </div>
30
+ <q-form class="column form" @submit="onSubmit">
31
+ <div>
32
+ <div class="q-mb-sm" style="font-size: 14px; font-weight: 600">
33
+ {{ $t("auth.validationCode") }}
34
+ </div>
35
+ <q-input
36
+ v-model="otp"
37
+ dense
38
+ :borderless="isReporterUi"
39
+ :outlined="!isReporterUi"
40
+ data-cy="validate-otp"
41
+ bg-color="white"
42
+ class="q-mb-md"
43
+ ><template v-slot:prepend> </template>
44
+ </q-input>
45
+ </div>
46
+ <q-space />
47
+ <div class="row justify-between">
48
+ <q-btn
49
+ :label="$t('auth.next')"
50
+ color="primary"
51
+ :loading="loading"
52
+ type="submit"
53
+ no-caps
54
+ class="submitBtn"
55
+ data-cy="send-new-otp"
56
+ :disable="canSubmit"
57
+ @click="onSubmit"
58
+ />
59
+ <q-btn
60
+ :label="$t('auth.login')"
61
+ text-color="primary"
62
+ type="submit"
63
+ no-caps
64
+ data-cy="login-page"
65
+ @click="() => router.push(LOCAL_LOGIN_ROUTE)"
66
+ />
67
+ </div>
68
+ </q-form>
69
+ </div>
70
+ </div>
71
+ </q-page>
72
+ </q-layout>
73
+ </template>
74
+
75
+ <script lang="ts">
76
+ //@ts-ignore
77
+ import { defineComponent, ref, computed } from "vue";
78
+ import { $router, $prompts, $api } from "../../injects";
79
+ import { useQuasar } from "quasar";
80
+ import { useI18n } from "vue-i18n";
81
+ import jwt from "jwt-decode";
82
+
83
+ import {
84
+ ionCloseOutline,
85
+ ionAlertCircleOutline,
86
+ } from "@quasar/extras/ionicons-v5";
87
+
88
+ interface DecodedToken {
89
+ uid: string;
90
+ cid: string;
91
+ exp: number;
92
+ }
93
+
94
+ export default defineComponent({
95
+ name: "ResetPassword",
96
+ components: {},
97
+ setup() {
98
+ const { t } = useI18n();
99
+ const router = $router();
100
+ const $q = useQuasar();
101
+ const { post } = $api();
102
+ const {
103
+ LOCAL_LOGIN_ROUTE,
104
+ AUTH_SERVER_VERIFY_OTP,
105
+ LOCAL_UPDATE_PASSWORD_ROUTE,
106
+ UI_PAGE,
107
+ } = $prompts();
108
+
109
+ const otp = ref("");
110
+ const loading = ref(false);
111
+
112
+ const isReporterUi = computed(() => UI_PAGE === "reporter");
113
+
114
+ const onSubmit = () => {
115
+ if (canSubmit && localStorage.getItem("emailResetPassword")) {
116
+ const data = {
117
+ auth: {
118
+ email: localStorage.getItem("emailResetPassword"),
119
+ password: otp.value,
120
+ },
121
+ };
122
+ post(AUTH_SERVER_VERIFY_OTP, data)
123
+ .then((response) => {
124
+ if (response.data.auth) {
125
+ let decoded = jwt(response.data.auth) as DecodedToken;
126
+ if (decoded) {
127
+ localStorage.setItem(
128
+ "userInfos",
129
+ JSON.stringify({
130
+ uid: decoded.uid,
131
+ token: response.data.auth,
132
+ })
133
+ );
134
+ router.push(LOCAL_UPDATE_PASSWORD_ROUTE);
135
+ }
136
+ }
137
+ })
138
+ .catch((error) => {
139
+ console.log("error", error);
140
+ $q.notify({
141
+ message: t("auth.passwordRecoveryFailed"),
142
+ color: "negative",
143
+ icon: ionAlertCircleOutline,
144
+ position: "top",
145
+ actions: [
146
+ {
147
+ color: "white",
148
+ icon: ionCloseOutline,
149
+ },
150
+ ],
151
+ });
152
+ });
153
+ }
154
+ };
155
+
156
+ const canSubmit = computed(() => {
157
+ return !otp.value.length;
158
+ });
159
+
160
+ return {
161
+ otp,
162
+ loading,
163
+ router,
164
+ LOCAL_LOGIN_ROUTE,
165
+ isReporterUi,
166
+ onSubmit,
167
+ canSubmit,
168
+ };
169
+ },
170
+ });
171
+ </script>
172
+
173
+ <style scoped>
174
+ .submitBtn {
175
+ width: 254px;
176
+ }
177
+ </style>
@@ -3,12 +3,9 @@
3
3
  <div class="row window-height">
4
4
  <div class="col column content-center bgWhite">
5
5
  <div class="q-mt-xl">
6
- <img
7
- src="../../assets/Sowell-logo.svg"
8
- style="width: 210px"
9
- />
6
+ <img src="../../assets/Sowell-logo.svg" style="width: 210px" />
10
7
  </div>
11
- <div class="column col content-center justify-center center" >
8
+ <div class="column col content-center justify-center center">
12
9
  <div class="q-pl-md q-pr-md" style="max-width: 500px">
13
10
  <h4 class="title">{{ WELCOME_TITLE }}</h4>
14
11
  <div class="subtitle">{{ WELCOME_SUBTITLE }}</div>
@@ -22,7 +19,7 @@
22
19
  type="email"
23
20
  :rules="validations['email']"
24
21
  lazy-rules
25
- data-cy="email"
22
+ data-cy="email"
26
23
  dense
27
24
  outlined
28
25
  input-class="text-subtitle2 text-weight-medium"
@@ -66,7 +63,7 @@
66
63
  />
67
64
  <div
68
65
  class="cursor-pointer text-primary text-subtitle2"
69
- data-cy="forgot-password"
66
+ data-cy="forgot-password"
70
67
  @click="forgottenPassword = true"
71
68
  >
72
69
  {{ $t("auth.forgottenPassword") }}
@@ -78,15 +75,19 @@
78
75
  </div>
79
76
  <div
80
77
  class="column col justify-center content-center mobile"
81
- :style="isManager ? 'background: #eef8fd' : ''"
82
- :class="!isManager ? 'bgBlue' : ''"
78
+ :style="IS_MANAGER ? 'background: #eef8fd' : ''"
79
+ :class="!IS_MANAGER ? 'bgBlue' : ''"
83
80
  >
84
81
  <img
85
- v-if="isManager"
82
+ v-if="IS_MANAGER"
86
83
  src="../../assets/illustration-login.svg"
87
84
  class="q-pl-md q-pr-md logoWhite"
88
85
  />
89
- <img v-else src="../../assets/SowellWhite.svg" class="q-pl-md q-pr-md logoWhite" />
86
+ <img
87
+ v-else
88
+ src="../../assets/SowellWhite.svg"
89
+ class="q-pl-md q-pr-md logoWhite"
90
+ />
90
91
  </div>
91
92
  </div>
92
93
  <div>
@@ -105,7 +106,20 @@
105
106
  :title="infoDialog.title"
106
107
  :content="infoDialog.content"
107
108
  :image="infoDialog.image"
108
- />
109
+ :isOtp="true"
110
+ >
111
+ <q-btn
112
+ v-if="requestedOtp"
113
+ class="bg-white submitBtn"
114
+ unelevated
115
+ outline
116
+ no-caps
117
+ :label="$t('auth.next')"
118
+ color="primary"
119
+ @click="goToOtpValidation"
120
+ v-close-popup
121
+ />
122
+ </sw-info-dialog>
109
123
  </q-dialog>
110
124
  </div>
111
125
  </q-layout>
@@ -144,14 +158,18 @@ export default defineComponent({
144
158
  WELCOME_TITLE,
145
159
  WELCOME_SUBTITLE,
146
160
  X_REQUESTED_ROLES,
161
+ IS_OTP,
147
162
  APP_SERVER_BASE_URL,
163
+ goToOtpValidation,
148
164
  onSubmit,
149
165
  retypeEmail,
150
166
  isSubmitBtnDisabled,
151
167
  icons,
152
168
  } = useAuth();
153
169
 
170
+ const IS_MANAGER = ["manager", "clean_manager"].includes(X_REQUESTED_ROLES);
154
171
  const isNotification = ref(false);
172
+ const requestedOtp = ref(false);
155
173
  const infoDialog = ref({
156
174
  title: "",
157
175
  content: "",
@@ -165,33 +183,40 @@ export default defineComponent({
165
183
  isEmail(email.value) &&
166
184
  password.value &&
167
185
  password.value.length > 5
168
- ),
186
+ )
169
187
  );
170
- const isManager = computed(() => X_REQUESTED_ROLES === "clean_manager");
171
188
  const resetPassword = async (email: string) => {
172
- const data = {
173
- auth: {
174
- email,
175
- base: APP_SERVER_BASE_URL,
176
- },
177
- };
178
- try {
179
- await authState.resetPassword(data);
180
- forgottenPassword.value = false;
181
- infoDialog.value = {
182
- title: t("auth.mailVerificationTitle"),
183
- content: t("auth.mailVerificationSubtitle"),
184
- image: "emailVerification",
189
+ if (email) {
190
+ const data = {
191
+ auth: {
192
+ email,
193
+ base: APP_SERVER_BASE_URL,
194
+ },
185
195
  };
186
- forgottenPassword.value = false;
187
- isNotification.value = true;
188
- } catch {
189
- infoDialog.value = {
190
- title: t("auth.userNotFoundTitle"),
191
- content: t("auth.userNotFoundSubtitle"),
192
- image: "userNotFound",
193
- };
194
- isNotification.value = true;
196
+ try {
197
+ await authState.resetPassword(data).then(() => {
198
+ localStorage.setItem("emailResetPassword", email);
199
+ infoDialog.value = {
200
+ title: t("auth.mailVerificationTitle"),
201
+ content:
202
+ IS_OTP.toLowerCase() === "y"
203
+ ? t("auth.otpValidationCode")
204
+ : t("auth.mailVerificationSubtitle"),
205
+ image: "emailVerification",
206
+ };
207
+ forgottenPassword.value = false;
208
+ isNotification.value = true;
209
+ requestedOtp.value = true;
210
+ });
211
+ } catch {
212
+ requestedOtp.value = false;
213
+ infoDialog.value = {
214
+ title: t("auth.userNotFoundTitle"),
215
+ content: t("auth.userNotFoundSubtitle"),
216
+ image: "userNotFound",
217
+ };
218
+ isNotification.value = true;
219
+ }
195
220
  }
196
221
  };
197
222
 
@@ -206,10 +231,12 @@ export default defineComponent({
206
231
  forgottenPassword,
207
232
  emailResetPassword,
208
233
  isNotification,
209
- isManager,
234
+ IS_MANAGER,
210
235
  WELCOME_SUBTITLE,
211
236
  WELCOME_TITLE,
212
237
  isSubmitBtnDisabled,
238
+ requestedOtp,
239
+ goToOtpValidation,
213
240
  resetPassword,
214
241
  retypeEmail,
215
242
  onSubmit,
@@ -220,7 +247,6 @@ export default defineComponent({
220
247
  </script>
221
248
 
222
249
  <style scoped>
223
-
224
250
  .center {
225
251
  margin-top: -115px;
226
252
  }
@@ -29,6 +29,8 @@ export function useAuth() {
29
29
  X_REQUESTED_ROLES,
30
30
  WELCOME_TITLE,
31
31
  WELCOME_SUBTITLE,
32
+ IS_OTP,
33
+ LOCAL_OTP_ROUTE,
32
34
  } = $prompts();
33
35
  const { push, replace } = $router();
34
36
 
@@ -87,19 +89,18 @@ export function useAuth() {
87
89
  if (requestedRoles) options.headers["x-requested-roles"] = requestedRoles;
88
90
 
89
91
  try {
90
- await authState.signin(data, options)
92
+ await authState.signin(data, options);
91
93
  loading.value = false;
92
94
  // @ts-ignore
93
95
  if (!window.Cypress) {
94
96
  // FIXME: encrypt password
95
- localStorage.setItem("credentials", JSON.stringify(data.auth))
97
+ localStorage.setItem("credentials", JSON.stringify(data.auth));
96
98
  }
97
99
  push(LOCAL_SUCCESS_REDIRECTION_ROUTE);
98
- }
99
- catch(err) {
100
+ } catch (err) {
101
+ console.error("onSubmit", err);
100
102
  loading.value = false;
101
103
  }
102
-
103
104
  };
104
105
 
105
106
  const isSubmitBtnDisabled = () => {
@@ -120,6 +121,10 @@ export function useAuth() {
120
121
  }
121
122
  };
122
123
 
124
+ const goToOtpValidation = () => {
125
+ push(LOCAL_OTP_ROUTE);
126
+ };
127
+
123
128
  onMounted(() => {
124
129
  if (authState.raw) {
125
130
  replace(LOCAL_SUCCESS_REDIRECTION_ROUTE);
@@ -146,6 +151,9 @@ export function useAuth() {
146
151
  WELCOME_TITLE,
147
152
  WELCOME_SUBTITLE,
148
153
  X_REQUESTED_ROLES,
154
+ IS_OTP,
155
+ LOCAL_OTP_ROUTE,
156
+ goToOtpValidation,
149
157
  onSubmit,
150
158
  retypeEmail,
151
159
  isSubmitBtnDisabled,
@@ -27,6 +27,7 @@ export function useResetPassword() {
27
27
  const passwordUpdated = ref(false);
28
28
  const showPassword = ref(false);
29
29
  const token = ref("");
30
+ const userInfos = JSON.parse(localStorage.getItem("userInfos") || "{}");
30
31
 
31
32
  const { patch, post } = $api();
32
33
  const router = $router();
@@ -34,6 +35,7 @@ export function useResetPassword() {
34
35
  AUTH_SERVER_SIGNING_ROUTE,
35
36
  AUTH_SERVER_UPDATE_PASSWORD_ROUTE,
36
37
  LOCAL_LOGIN_ROUTE,
38
+ IS_OTP,
37
39
  } = $prompts();
38
40
  const $route = router.currentRoute.value;
39
41
 
@@ -42,8 +44,12 @@ export function useResetPassword() {
42
44
  return jwt(token.value) as DecodedToken;
43
45
  }) as Ref<DecodedToken | null>;
44
46
 
47
+ const isOtp = computed(() => IS_OTP.toLowerCase() === "y");
45
48
  const canSubmit = computed(() => {
46
- return decodedToken.value !== null && password.value.length >= 6;
49
+ return (
50
+ (decodedToken.value !== null || userInfos?.uid) &&
51
+ password.value.length >= 6
52
+ );
47
53
  });
48
54
 
49
55
  const validations = {
@@ -87,10 +93,12 @@ export function useResetPassword() {
87
93
  };
88
94
 
89
95
  const onSubmit = () => {
96
+ let idUser =
97
+ isOtp && userInfos?.uid ? userInfos?.uid : decodedToken?.value?.sub;
90
98
  const updatePasswordPayload = {
91
99
  data: {
92
100
  type: "users",
93
- id: decodedToken.value.sub,
101
+ id: idUser,
94
102
  attributes: {
95
103
  password: password.value,
96
104
  },
@@ -98,7 +106,7 @@ export function useResetPassword() {
98
106
  };
99
107
  const updatePasswordHeaders = {
100
108
  headers: {
101
- Authorization: `Bearer ${token.value}`,
109
+ Authorization: `Bearer ${isOtp ? userInfos?.token : token.value}`,
102
110
  },
103
111
  };
104
112
  loading.value = true;
@@ -106,14 +114,16 @@ export function useResetPassword() {
106
114
  patch(
107
115
  AUTH_SERVER_UPDATE_PASSWORD_ROUTE.replace(
108
116
  ":userId",
109
- String(decodedToken.value.sub),
117
+ String(isOtp ? userInfos?.uid : decodedToken.value.sub)
110
118
  ),
111
119
  updatePasswordPayload,
112
- updatePasswordHeaders,
120
+ updatePasswordHeaders
113
121
  )
114
122
  .then(() => {
115
123
  loading.value = false;
116
124
  passwordUpdated.value = true;
125
+ localStorage.removeItem("emailResetPassword");
126
+ localStorage.removeItem("userInfos");
117
127
  })
118
128
  .catch(() => {
119
129
  loading.value = false;
@@ -137,56 +147,13 @@ export function useResetPassword() {
137
147
  };
138
148
 
139
149
  onMounted(() => {
140
- let key = $route.query.key as string;
141
- const { decoded, email, tmpToken } = decodeKey(key);
142
-
143
- if (!decoded) {
144
- return $q.notify({
145
- message: t("auth.passwordRecoveryInvalidLink"),
146
- color: "negative",
147
- position: "top",
148
- icon: ionAlertCircleOutline,
149
- actions: [
150
- {
151
- color: "white",
152
- icon: ionCloseOutline,
153
- },
154
- ],
155
- });
156
- }
157
-
158
- const tmpAuthPayload = {
159
- auth: {
160
- email: email,
161
- token: tmpToken,
162
- },
163
- };
150
+ if (IS_OTP.toLowerCase() === "n") {
151
+ let key = $route.query.key as string;
152
+ const { decoded, email, tmpToken } = decodeKey(key);
164
153
 
165
- post(AUTH_SERVER_SIGNING_ROUTE, tmpAuthPayload)
166
- .then((response) => {
167
- token.value = response.data.jwt;
168
- if (!token.value) throw "Error reading the temporary token";
169
- })
170
- .catch((error) => {
171
- if (error.response) {
172
- // Request made and server responded
173
- if (error.response.status === 404) {
174
- return $q.notify({
175
- message: t("auth.passwordRecoveryExpiredLink"),
176
- color: "negative",
177
- icon: ionAlertCircleOutline,
178
- position: "top",
179
- actions: [
180
- {
181
- color: "white",
182
- icon: ionCloseOutline,
183
- },
184
- ],
185
- });
186
- }
187
- }
154
+ if (!decoded) {
188
155
  return $q.notify({
189
- message: t("auth.passwordRecoveryFailed"),
156
+ message: t("auth.passwordRecoveryInvalidLink"),
190
157
  color: "negative",
191
158
  position: "top",
192
159
  icon: ionAlertCircleOutline,
@@ -197,7 +164,52 @@ export function useResetPassword() {
197
164
  },
198
165
  ],
199
166
  });
200
- });
167
+ }
168
+
169
+ const tmpAuthPayload = {
170
+ auth: {
171
+ email: email,
172
+ token: tmpToken,
173
+ },
174
+ };
175
+
176
+ post(AUTH_SERVER_SIGNING_ROUTE, tmpAuthPayload)
177
+ .then((response) => {
178
+ token.value = response.data.jwt;
179
+ if (!token.value) throw "Error reading the temporary token";
180
+ })
181
+ .catch((error) => {
182
+ if (error.response) {
183
+ // Request made and server responded
184
+ if (error.response.status === 404) {
185
+ return $q.notify({
186
+ message: t("auth.passwordRecoveryExpiredLink"),
187
+ color: "negative",
188
+ icon: ionAlertCircleOutline,
189
+ position: "top",
190
+ actions: [
191
+ {
192
+ color: "white",
193
+ icon: ionCloseOutline,
194
+ },
195
+ ],
196
+ });
197
+ }
198
+ }
199
+ return $q.notify({
200
+ message: t("auth.passwordRecoveryFailed"),
201
+ color: "negative",
202
+ position: "top",
203
+ icon: ionAlertCircleOutline,
204
+ actions: [
205
+ {
206
+ color: "white",
207
+ icon: ionCloseOutline,
208
+ },
209
+ ],
210
+ });
211
+ });
212
+ }
201
213
  });
202
214
 
203
215
  return {
@@ -18,6 +18,10 @@ export interface PromptsInterface {
18
18
  X_REQUESTED_ROLES: string;
19
19
  WELCOME_TITLE: string;
20
20
  WELCOME_SUBTITLE: string;
21
+ IS_OTP: string;
22
+ LOCAL_OTP_ROUTE: string;
23
+ AUTH_SERVER_VERIFY_OTP: string;
24
+ UI_PAGE: string;
21
25
  }
22
26
  export const $prompts = (): PromptsInterface => {
23
27
  const $prompts = inject<Record<"@sowell/auth", PromptsInterface>>("$prompts");
package/src/prompts.js CHANGED
@@ -148,5 +148,26 @@ module.exports = function () {
148
148
  message: "Subtitle on login page",
149
149
  default: "",
150
150
  },
151
+ {
152
+ name: "IS_OTP",
153
+ type: "input",
154
+ required: true,
155
+ message: "Use OTP system reset password (Y/N or y/n)",
156
+ default: "N",
157
+ },
158
+ {
159
+ name: "LOCAL_OTP_ROUTE",
160
+ type: "input",
161
+ required: true,
162
+ message: "Route to validation otp (page)",
163
+ default: "/otp-validation",
164
+ },
165
+ {
166
+ name: "AUTH_SERVER_VERIFY_OTP",
167
+ type: "input",
168
+ required: true,
169
+ message: "API endpoint to send OTP (without host)",
170
+ default: "/sign-in-otp",
171
+ },
151
172
  ];
152
173
  };
@@ -8,11 +8,12 @@ const {
8
8
  LOCAL_UPDATE_PASSWORD_ROUTE,
9
9
  X_REQUESTED_ROLES,
10
10
  UI_PAGE,
11
+ LOCAL_OTP_ROUTE,
11
12
  },
12
13
  } = prompts;
13
14
 
14
15
  // FIXME: Utiliser le UI_PAGE pour choisir les page a utiliser pour la version web ou mobile (auth et reset password)
15
- const webAuthRoles = ["superadmin", "clean_manager"];
16
+ const webAuthRoles = ["superadmin", "clean_manager", "manager"];
16
17
 
17
18
  let mobileAuthPage = "";
18
19
 
@@ -54,6 +55,13 @@ const routes: RouteRecordRaw[] = [
54
55
  }`
55
56
  ),
56
57
  },
58
+ {
59
+ path: LOCAL_OTP_ROUTE,
60
+ name: "otp_validation",
61
+ component: () =>
62
+ // @ts-ignore
63
+ import("@sowell/auth/pages/OtpValidation.vue"),
64
+ },
57
65
  ];
58
66
 
59
67
  export default routes;