@sowell/quasar-app-extension-auth 0.3.21 → 0.3.23

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.21",
3
+ "version": "0.3.23",
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",
@@ -38,6 +38,7 @@
38
38
  "test": "test"
39
39
  },
40
40
  "dependencies": {
41
+ "@capacitor/core": "^4.6.2",
41
42
  "@quasar/extras": "^1.13.4",
42
43
  "axios": "^0.21.1",
43
44
  "buffer": "^6.0.3",
@@ -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") }}
@@ -86,7 +83,11 @@
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,7 +158,9 @@ 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,
@@ -153,6 +169,7 @@ export default defineComponent({
153
169
 
154
170
  const IS_MANAGER = ["manager", "clean_manager"].includes(X_REQUESTED_ROLES);
155
171
  const isNotification = ref(false);
172
+ const requestedOtp = ref(false);
156
173
  const infoDialog = ref({
157
174
  title: "",
158
175
  content: "",
@@ -166,32 +183,40 @@ export default defineComponent({
166
183
  isEmail(email.value) &&
167
184
  password.value &&
168
185
  password.value.length > 5
169
- ),
186
+ )
170
187
  );
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
 
@@ -210,6 +235,8 @@ export default defineComponent({
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
  }
@@ -1,4 +1,4 @@
1
- import { ref, onMounted } from "vue";
1
+ import { ref, onMounted, inject } from "vue";
2
2
  import isEmail from "validator/es/lib/isEmail";
3
3
  import { useI18n } from "vue-i18n";
4
4
  import {
@@ -11,7 +11,7 @@ import {
11
11
  } from "@quasar/extras/ionicons-v5";
12
12
  import { Platform } from "quasar";
13
13
 
14
- import { $prompts, $router, useAuthState } from "../injects";
14
+ import { $prompts, $router, SignInPayload, useAuthState } from "../injects";
15
15
 
16
16
  interface HeadersInterface {
17
17
  headers: {
@@ -21,6 +21,13 @@ interface HeadersInterface {
21
21
 
22
22
  export function useAuth() {
23
23
  const authState = useAuthState();
24
+ const device = inject("Device")
25
+ let deviceInfos = {
26
+ cleanManufacturer: "",
27
+ cleanModel: "",
28
+ cleanPlatform: "",
29
+ cleanVersion: ""
30
+ }
24
31
 
25
32
  const { t } = useI18n();
26
33
  const {
@@ -29,6 +36,9 @@ export function useAuth() {
29
36
  X_REQUESTED_ROLES,
30
37
  WELCOME_TITLE,
31
38
  WELCOME_SUBTITLE,
39
+ IS_OTP,
40
+ LOCAL_OTP_ROUTE,
41
+ APP_VERSION
32
42
  } = $prompts();
33
43
  const { push, replace } = $router();
34
44
 
@@ -69,38 +79,60 @@ export function useAuth() {
69
79
  forgottenPassword.value = true;
70
80
  };
71
81
 
82
+ const deviceInfo = async () => {
83
+ if (device) {
84
+ const info = await device.getInfo();
85
+ deviceInfos = {
86
+ cleanManufacturer: info.manufacturer,
87
+ cleanModel: info.model,
88
+ cleanPlatform: info.platform,
89
+ cleanVersion: info.osVersion
90
+ }
91
+ }
92
+ }
93
+
72
94
  const onSubmit = async () => {
73
95
  loading.value = true;
74
- const data = {
96
+ const data: SignInPayload = {
75
97
  auth: {
76
98
  email: email.value,
77
99
  password: password.value,
78
100
  },
79
101
  };
102
+ if (device) {
103
+ data.device = {
104
+ cleanManufacturer: deviceInfos.cleanManufacturer,
105
+ cleanModel: deviceInfos.cleanModel,
106
+ cleanPlatform: deviceInfos.cleanPlatform,
107
+ cleanVersion: deviceInfos.cleanVersion,
108
+ },
109
+ data.app = {
110
+ cleanVersionNumber: APP_VERSION
111
+ }
112
+ };
80
113
  const options: HeadersInterface = {
81
114
  headers: {},
82
115
  };
83
116
  // @ts-ignore
84
117
  let requestedRoles = window.Cypress
85
- ? window.Cypress.requestedRoles
118
+ ? // @ts-ignore
119
+ window.Cypress.requestedRoles
86
120
  : X_REQUESTED_ROLES;
87
121
  if (requestedRoles) options.headers["x-requested-roles"] = requestedRoles;
88
122
 
89
123
  try {
90
- await authState.signin(data, options)
124
+ await authState.signin(data, options);
91
125
  loading.value = false;
92
126
  // @ts-ignore
93
127
  if (!window.Cypress) {
94
128
  // FIXME: encrypt password
95
- localStorage.setItem("credentials", JSON.stringify(data.auth))
129
+ localStorage.setItem("credentials", JSON.stringify(data.auth));
96
130
  }
97
131
  push(LOCAL_SUCCESS_REDIRECTION_ROUTE);
98
- }
99
- catch(err) {
100
- console.error("onSubmit", err)
132
+ } catch (err) {
133
+ console.error("onSubmit", err);
101
134
  loading.value = false;
102
135
  }
103
-
104
136
  };
105
137
 
106
138
  const isSubmitBtnDisabled = () => {
@@ -121,7 +153,12 @@ export function useAuth() {
121
153
  }
122
154
  };
123
155
 
124
- onMounted(() => {
156
+ const goToOtpValidation = () => {
157
+ push(LOCAL_OTP_ROUTE);
158
+ };
159
+
160
+ onMounted(async () => {
161
+ await deviceInfo()
125
162
  if (authState.raw) {
126
163
  replace(LOCAL_SUCCESS_REDIRECTION_ROUTE);
127
164
  }
@@ -147,6 +184,9 @@ export function useAuth() {
147
184
  WELCOME_TITLE,
148
185
  WELCOME_SUBTITLE,
149
186
  X_REQUESTED_ROLES,
187
+ IS_OTP,
188
+ LOCAL_OTP_ROUTE,
189
+ goToOtpValidation,
150
190
  onSubmit,
151
191
  retypeEmail,
152
192
  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,11 @@ 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;
25
+ APP_VERSION: string;
21
26
  }
22
27
  export const $prompts = (): PromptsInterface => {
23
28
  const $prompts = inject<Record<"@sowell/auth", PromptsInterface>>("$prompts");
@@ -23,9 +23,18 @@ export interface RequestOptions {
23
23
 
24
24
  export interface SignInPayload {
25
25
  auth: {
26
- email: string;
27
- password: string;
28
- };
26
+ email: string
27
+ password: string
28
+ }
29
+ device?: {
30
+ cleanManufacturer: string
31
+ cleanModel: string
32
+ cleanPlatform: string,
33
+ cleanVersion: string
34
+ }
35
+ app?: {
36
+ cleanVersionNumber: string
37
+ }
29
38
  }
30
39
 
31
40
  export interface ResetPasswordPayload {
package/src/prompts.js CHANGED
@@ -148,5 +148,32 @@ 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
+ },
172
+ {
173
+ name: "APP_VERSION",
174
+ required: true,
175
+ message: "Version de l'application",
176
+ default: "",
177
+ },
151
178
  ];
152
179
  };
@@ -8,6 +8,7 @@ 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
 
@@ -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;