@sowell/quasar-app-extension-auth 0.2.3 → 0.2.7
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/.github/workflows/main.yml +1 -1
- package/.github/workflows/publish.yml +24 -0
- package/README.md +3 -3
- package/package.json +4 -3
- package/src/assets/error.svg +5 -0
- package/src/assets/success.svg +6 -0
- package/src/boot/router-guards.ts +6 -6
- package/src/components/dialogs/Message.vue +81 -0
- package/src/components/dialogs/SendEmail.vue +69 -0
- package/src/components/pages/Auth.vue +27 -48
- package/src/components/pages/ResetPassword.vue +166 -78
- package/src/injects/prompts.ts +1 -0
- package/src/prompts.js +2 -2
- package/src/routes/index.ts +1 -0
- package/src/store/index.ts +16 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: npm-publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- master
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
npm-publish:
|
|
10
|
+
name: npm-publish
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- name: Checkout repository
|
|
14
|
+
uses: actions/checkout@v2
|
|
15
|
+
- name: Nodejs
|
|
16
|
+
uses: actions/setup-node@v2
|
|
17
|
+
with:
|
|
18
|
+
node-version: "14"
|
|
19
|
+
cache: yarn
|
|
20
|
+
registry-url: https://registry.npmjs.org
|
|
21
|
+
- name: publish
|
|
22
|
+
run: npm publish --access public
|
|
23
|
+
env:
|
|
24
|
+
NODE_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}}
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
# Install
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
|
-
quasar ext add
|
|
11
|
+
quasar ext add @sowell/auth
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
Quasar CLI will retrieve it from NPM and install the extension.
|
|
@@ -39,7 +39,7 @@ The default is http://localhost:3000, you must point it to your server base url
|
|
|
39
39
|
|
|
40
40
|
6. Server route for authentication
|
|
41
41
|
|
|
42
|
-
The default is /
|
|
42
|
+
The default is /auth
|
|
43
43
|
|
|
44
44
|
7. Server route to reset password
|
|
45
45
|
|
|
@@ -78,7 +78,7 @@ The default is auth/getInfosUser and return email or phone number
|
|
|
78
78
|
# Uninstall
|
|
79
79
|
|
|
80
80
|
```bash
|
|
81
|
-
quasar ext remove
|
|
81
|
+
quasar ext remove @sowell/auth
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
# Info
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sowell/quasar-app-extension-auth",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
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",
|
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
"lint": "cd dev && eslint --ext .js,.ts,.vue ./",
|
|
9
9
|
"dev": "cd dev && quasar dev",
|
|
10
10
|
"dep": "cd dev && yarn",
|
|
11
|
-
"test:
|
|
12
|
-
"test:
|
|
11
|
+
"test:link": "cd dev && yarn test:link",
|
|
12
|
+
"test:browser": "cd dev && yarn test:browser",
|
|
13
|
+
"test": "cd dev && yarn test"
|
|
13
14
|
},
|
|
14
15
|
"keywords": [],
|
|
15
16
|
"author": "SoWell SAS",
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg id="noun_Error_1688921" xmlns="http://www.w3.org/2000/svg" width="63" height="63" viewBox="0 0 63 63">
|
|
2
|
+
<circle id="Ellipse_90" data-name="Ellipse 90" cx="31.5" cy="31.5" r="31.5" fill="#f2d7d7"/>
|
|
3
|
+
<path id="Tracé_727" data-name="Tracé 727" d="M14.477,29.163h0A4.078,4.078,0,0,1,10.5,25.186V9.277A4.078,4.078,0,0,1,14.477,5.3h0a4.078,4.078,0,0,1,3.977,3.977V25.186A4.078,4.078,0,0,1,14.477,29.163Z" transform="translate(17.34 8.753)" fill="#ce0101"/>
|
|
4
|
+
<path id="Tracé_728" data-name="Tracé 728" d="M14.477,23.654h0A4.078,4.078,0,0,1,10.5,19.677h0A4.078,4.078,0,0,1,14.477,15.7h0a4.078,4.078,0,0,1,3.977,3.977h0A4.078,4.078,0,0,1,14.477,23.654Z" transform="translate(17.34 25.927)" fill="#ce0101"/>
|
|
5
|
+
</svg>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="62" height="62" viewBox="0 0 62 62">
|
|
2
|
+
<g id="noun_validation_1683882" transform="translate(0.1 -0.4)">
|
|
3
|
+
<circle id="Ellipse_89" data-name="Ellipse 89" cx="31" cy="31" r="31" transform="translate(-0.1 0.4)" fill="#dff2d6"/>
|
|
4
|
+
<path id="Tracé_724" data-name="Tracé 724" d="M36.165,13.221a1.189,1.189,0,0,0-1.868.311L21.84,31.906l-8.253-8.253a1.169,1.169,0,0,0-1.868.156,1.667,1.667,0,0,0,.156,2.18l9.5,9.342h0c.156.156.156.156.311.156a.152.152,0,0,1,.156.156c.156,0,.311.156.623.156h0c.156,0,.467,0,.467-.156l.156-.156c.156,0,.156-.156.311-.156h0L36.632,15.713A2.752,2.752,0,0,0,36.165,13.221Z" transform="translate(6.399 6.953)" fill="#3b9843"/>
|
|
5
|
+
</g>
|
|
6
|
+
</svg>
|
|
@@ -5,17 +5,17 @@ import { BootInterface } from "../interfaces";
|
|
|
5
5
|
|
|
6
6
|
export default boot(({ router, store }: BootInterface) => {
|
|
7
7
|
const {
|
|
8
|
-
"@sowell/auth": { LOCAL_LOGIN_ROUTE
|
|
8
|
+
"@sowell/auth": { LOCAL_LOGIN_ROUTE },
|
|
9
9
|
} = prompts;
|
|
10
10
|
|
|
11
11
|
router.beforeEach((to, from, next) => {
|
|
12
|
-
let isValid = store.getters["token/getIsValid"]
|
|
13
|
-
let token = store.getters["token/getRaw"]
|
|
12
|
+
let isValid = store.getters["token/getIsValid"];
|
|
13
|
+
let token = store.getters["token/getRaw"];
|
|
14
14
|
if (
|
|
15
|
-
!isValid
|
|
16
|
-
|
|
15
|
+
(!isValid ||
|
|
16
|
+
!token ) &&
|
|
17
17
|
to.path !== LOCAL_LOGIN_ROUTE &&
|
|
18
|
-
to.
|
|
18
|
+
to.name !== "update_password"
|
|
19
19
|
) {
|
|
20
20
|
next(LOCAL_LOGIN_ROUTE);
|
|
21
21
|
} else {
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<q-card style="min-width: 350px; border-radius: 30px">
|
|
3
|
+
<q-toolbar>
|
|
4
|
+
<q-space />
|
|
5
|
+
<q-btn
|
|
6
|
+
class="q-pt-md"
|
|
7
|
+
flat
|
|
8
|
+
dense
|
|
9
|
+
color="primary"
|
|
10
|
+
icon="close"
|
|
11
|
+
v-close-popup
|
|
12
|
+
/>
|
|
13
|
+
</q-toolbar>
|
|
14
|
+
<q-card-section class="text-center">
|
|
15
|
+
<img v-if="sendEmailSuccess" src="../../assets/success.svg" />
|
|
16
|
+
<img v-else src="../../assets/error.svg" />
|
|
17
|
+
</q-card-section>
|
|
18
|
+
|
|
19
|
+
<q-card-section class="q-pt-none q-pl-lg q-pr-lg">
|
|
20
|
+
<div
|
|
21
|
+
class="text-subtitle1 q-mt-sm text-center"
|
|
22
|
+
style="font-size: 14px; font-weigth: 300; line-height: 20px"
|
|
23
|
+
data-cy="resetMessage"
|
|
24
|
+
>
|
|
25
|
+
{{
|
|
26
|
+
sendEmailSuccess ? $t("auth.emailSent") : $t("auth.emailNotRecognize")
|
|
27
|
+
}}
|
|
28
|
+
</div>
|
|
29
|
+
<div
|
|
30
|
+
class="q-mt-sm text-center q-mb-xl"
|
|
31
|
+
style="font-size: 15px; color: #737676"
|
|
32
|
+
v-if="sendEmailSuccess"
|
|
33
|
+
>
|
|
34
|
+
{{ emailResetPassword }}
|
|
35
|
+
</div>
|
|
36
|
+
</q-card-section>
|
|
37
|
+
<q-card-actions class="q-ml-lg q-mr-lg q-mb-lg">
|
|
38
|
+
<q-btn
|
|
39
|
+
v-if="!sendEmailSuccess"
|
|
40
|
+
data-cy="submit-login"
|
|
41
|
+
rounded
|
|
42
|
+
:label="$t('auth.reSend')"
|
|
43
|
+
color="primary"
|
|
44
|
+
padding="10px 5px 10px 5px"
|
|
45
|
+
:loading="loading"
|
|
46
|
+
no-caps
|
|
47
|
+
type="submit"
|
|
48
|
+
@click="reSendEmail"
|
|
49
|
+
class="full-width"
|
|
50
|
+
/>
|
|
51
|
+
</q-card-actions>
|
|
52
|
+
</q-card>
|
|
53
|
+
</template>
|
|
54
|
+
|
|
55
|
+
<script lang="ts">
|
|
56
|
+
import { defineComponent } from "vue";
|
|
57
|
+
|
|
58
|
+
export default defineComponent({
|
|
59
|
+
props: {
|
|
60
|
+
emailResetPassword: {
|
|
61
|
+
type: String,
|
|
62
|
+
required: true,
|
|
63
|
+
},
|
|
64
|
+
sendEmailSuccess: {
|
|
65
|
+
type: Boolean,
|
|
66
|
+
required: true,
|
|
67
|
+
},
|
|
68
|
+
loading: {
|
|
69
|
+
type: Boolean,
|
|
70
|
+
required: true,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
emit: ["resetPassword"],
|
|
74
|
+
setup(props: any, { emit }: any) {
|
|
75
|
+
const reSendEmail = () => {
|
|
76
|
+
emit("reSendEmail");
|
|
77
|
+
};
|
|
78
|
+
return { reSendEmail };
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
</script>
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<q-card style="min-width: 350px; border-radius: 30px">
|
|
3
|
+
<q-toolbar>
|
|
4
|
+
<q-space />
|
|
5
|
+
<q-btn
|
|
6
|
+
class="q-pt-md"
|
|
7
|
+
flat
|
|
8
|
+
dense
|
|
9
|
+
color="primary"
|
|
10
|
+
icon="close"
|
|
11
|
+
v-close-popup
|
|
12
|
+
/>
|
|
13
|
+
</q-toolbar>
|
|
14
|
+
<q-card-section>
|
|
15
|
+
<p
|
|
16
|
+
class="resetPasswordTitle text-center"
|
|
17
|
+
style="font-size: 20px; font-weigth: 600"
|
|
18
|
+
>
|
|
19
|
+
{{ $t("auth.tapEmail") }}
|
|
20
|
+
</p>
|
|
21
|
+
</q-card-section>
|
|
22
|
+
|
|
23
|
+
<q-card-section class="q-pt-none q-pl-lg q-pr-lg">
|
|
24
|
+
<q-input
|
|
25
|
+
v-model="email"
|
|
26
|
+
dense
|
|
27
|
+
outlined
|
|
28
|
+
data-cy="emailReset"
|
|
29
|
+
:placeholder="$t('auth.email')"
|
|
30
|
+
autofocus
|
|
31
|
+
/>
|
|
32
|
+
<div
|
|
33
|
+
class="text-subtitle1 q-mt-sm"
|
|
34
|
+
style="font-size: 13px; font-weigth: 300; line-height: 20px"
|
|
35
|
+
>
|
|
36
|
+
{{ $t("auth.resetText") }}
|
|
37
|
+
</div>
|
|
38
|
+
</q-card-section>
|
|
39
|
+
|
|
40
|
+
<q-card-actions align="center" class="q-ml-lg q-mr-lg q-mb-lg">
|
|
41
|
+
<q-btn
|
|
42
|
+
rounded
|
|
43
|
+
color="primary"
|
|
44
|
+
data-cy="sendEmails"
|
|
45
|
+
no-caps
|
|
46
|
+
:label="$t('common.reset')"
|
|
47
|
+
class="btn-min full-width"
|
|
48
|
+
@click="sendEmail"
|
|
49
|
+
v-close-popup
|
|
50
|
+
/>
|
|
51
|
+
</q-card-actions>
|
|
52
|
+
</q-card>
|
|
53
|
+
</template>
|
|
54
|
+
|
|
55
|
+
<script lang="ts">
|
|
56
|
+
// @ts-ignore
|
|
57
|
+
import { defineComponent, ref } from "vue";
|
|
58
|
+
|
|
59
|
+
export default defineComponent({
|
|
60
|
+
emit: ["resetPassword"],
|
|
61
|
+
setup(props: any, { emit }: any) {
|
|
62
|
+
const email = ref("");
|
|
63
|
+
const sendEmail = () => {
|
|
64
|
+
emit("resetPassword", email.value);
|
|
65
|
+
};
|
|
66
|
+
return { sendEmail, email };
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
</script>
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
<div
|
|
44
44
|
class="q-mb-xl"
|
|
45
45
|
@click="forgottenPassword = true"
|
|
46
|
+
data-cy="forgot-password"
|
|
46
47
|
style="color: #40c4ff; font-weight: 500; cursor: pointer"
|
|
47
48
|
>
|
|
48
49
|
{{ $t("auth.forgottenPassword") }}
|
|
@@ -63,41 +64,15 @@
|
|
|
63
64
|
</div>
|
|
64
65
|
<div>
|
|
65
66
|
<q-dialog v-model="forgottenPassword" persistent>
|
|
66
|
-
<
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
v-model="emailResetPassword"
|
|
76
|
-
dense
|
|
77
|
-
rounded
|
|
78
|
-
outlined
|
|
79
|
-
:placeholder="$t('auth.email')"
|
|
80
|
-
autofocus
|
|
81
|
-
/>
|
|
82
|
-
</q-card-section>
|
|
83
|
-
|
|
84
|
-
<q-card-actions align="right">
|
|
85
|
-
<q-btn
|
|
86
|
-
outline
|
|
87
|
-
rounded
|
|
88
|
-
:label="$t('common.cancel')"
|
|
89
|
-
class="btn-min"
|
|
90
|
-
v-close-popup
|
|
91
|
-
/>
|
|
92
|
-
<q-btn
|
|
93
|
-
rounded
|
|
94
|
-
color="primary"
|
|
95
|
-
:label="$t('common.send')"
|
|
96
|
-
class="btn-min"
|
|
97
|
-
@click="resetPassword"
|
|
98
|
-
/>
|
|
99
|
-
</q-card-actions>
|
|
100
|
-
</q-card>
|
|
67
|
+
<send-email @resetPassword="resetPassword" />
|
|
68
|
+
</q-dialog>
|
|
69
|
+
<q-dialog v-model="isReset" persistent>
|
|
70
|
+
<sowell-message
|
|
71
|
+
:emailResetPassword="emailResetPassword"
|
|
72
|
+
:sendEmailSuccess="sendEmailSuccess"
|
|
73
|
+
:loading="loading"
|
|
74
|
+
@reSendEmail="resetPassword"
|
|
75
|
+
/>
|
|
101
76
|
</q-dialog>
|
|
102
77
|
</div>
|
|
103
78
|
</q-page>
|
|
@@ -105,7 +80,6 @@
|
|
|
105
80
|
</template>
|
|
106
81
|
|
|
107
82
|
<script lang="ts">
|
|
108
|
-
//import prompts from "app/quasar.extensions.json";
|
|
109
83
|
import isEmail from "validator/es/lib/isEmail";
|
|
110
84
|
import { defineComponent, ref, onMounted } from "vue";
|
|
111
85
|
import { $api, $prompts, $router, $store } from "../../injects";
|
|
@@ -113,6 +87,8 @@ import { useQuasar } from "quasar";
|
|
|
113
87
|
import { useI18n } from "vue-i18n";
|
|
114
88
|
import { AxiosError, AxiosResponse } from "axios";
|
|
115
89
|
import prompts from "app/quasar.extensions.json";
|
|
90
|
+
import SendEmail from "../dialogs/SendEmail.vue";
|
|
91
|
+
import SowellMessage from "../dialogs/Message.vue";
|
|
116
92
|
|
|
117
93
|
interface HeadersInterface {
|
|
118
94
|
headers: {
|
|
@@ -122,6 +98,7 @@ interface HeadersInterface {
|
|
|
122
98
|
|
|
123
99
|
export default defineComponent({
|
|
124
100
|
name: "Auth",
|
|
101
|
+
components: { SendEmail, SowellMessage },
|
|
125
102
|
setup() {
|
|
126
103
|
const {
|
|
127
104
|
"@sowell/auth": { X_REQUESTED_ROLES },
|
|
@@ -131,12 +108,15 @@ export default defineComponent({
|
|
|
131
108
|
const loading = ref(false);
|
|
132
109
|
const showPassword = ref(false);
|
|
133
110
|
const forgottenPassword = ref(false);
|
|
111
|
+
const isReset = ref(false);
|
|
112
|
+
const sendEmailSuccess = ref(false);
|
|
134
113
|
const emailResetPassword = ref("");
|
|
135
114
|
const { post } = $api();
|
|
136
115
|
const {
|
|
137
116
|
LOCAL_SUCCESS_REDIRECTION_ROUTE,
|
|
138
117
|
AUTH_SERVER_SIGNING_ROUTE,
|
|
139
118
|
AUTH_SERVER_RESET_PASSWORD_ROUTE,
|
|
119
|
+
APP_SERVER_BASE_URL,
|
|
140
120
|
} = $prompts();
|
|
141
121
|
const $q = useQuasar();
|
|
142
122
|
const { push, replace } = $router();
|
|
@@ -157,26 +137,23 @@ export default defineComponent({
|
|
|
157
137
|
password: [(val: string) => !!val || t("auth.passwordPresenceError")],
|
|
158
138
|
});
|
|
159
139
|
|
|
160
|
-
const resetPassword = () => {
|
|
140
|
+
const resetPassword = (email: string) => {
|
|
161
141
|
const data = {
|
|
162
|
-
|
|
142
|
+
auth: {
|
|
143
|
+
email,
|
|
144
|
+
base: APP_SERVER_BASE_URL,
|
|
145
|
+
},
|
|
163
146
|
};
|
|
164
147
|
|
|
148
|
+
|
|
165
149
|
post(AUTH_SERVER_RESET_PASSWORD_ROUTE, data)
|
|
166
150
|
.then(() => {
|
|
167
|
-
$q.notify({
|
|
168
|
-
message: t("auth.emailSent"),
|
|
169
|
-
color: "positive",
|
|
170
|
-
position: "top",
|
|
171
|
-
});
|
|
172
151
|
forgottenPassword.value = false;
|
|
152
|
+
isReset.value = true;
|
|
153
|
+
sendEmailSuccess.value = true;
|
|
173
154
|
})
|
|
174
155
|
.catch(() => {
|
|
175
|
-
|
|
176
|
-
message: t("auth.emailNotRecognize"),
|
|
177
|
-
color: "negative",
|
|
178
|
-
position: "top",
|
|
179
|
-
});
|
|
156
|
+
isReset.value = true;
|
|
180
157
|
});
|
|
181
158
|
};
|
|
182
159
|
|
|
@@ -251,6 +228,8 @@ export default defineComponent({
|
|
|
251
228
|
forgottenPassword,
|
|
252
229
|
emailResetPassword,
|
|
253
230
|
resetPassword,
|
|
231
|
+
isReset,
|
|
232
|
+
sendEmailSuccess,
|
|
254
233
|
};
|
|
255
234
|
},
|
|
256
235
|
});
|
|
@@ -1,111 +1,162 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<q-
|
|
3
|
-
<q-
|
|
4
|
-
<
|
|
5
|
-
<div class="
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
<q-form class="q-gutter-md" @submit="onSubmit">
|
|
11
|
-
<q-card-section>
|
|
12
|
-
<q-input
|
|
13
|
-
id="password"
|
|
14
|
-
v-model="password"
|
|
15
|
-
type="password"
|
|
16
|
-
:label="$t('auth.newPassword')"
|
|
17
|
-
:rules="validations['passwordRules']"
|
|
18
|
-
rounded
|
|
19
|
-
outlined
|
|
20
|
-
dense
|
|
21
|
-
class="full-width q-mb-md"
|
|
22
|
-
>
|
|
23
|
-
</q-input>
|
|
24
|
-
<q-input
|
|
25
|
-
id="passwordConfirmation"
|
|
26
|
-
v-model="passwordConfirmation"
|
|
27
|
-
type="password"
|
|
28
|
-
:label="$t('auth.newPasswordConfirmation')"
|
|
29
|
-
:rules="validations['passwordConfirmationRules']"
|
|
30
|
-
rounded
|
|
31
|
-
outlined
|
|
32
|
-
dense
|
|
33
|
-
class="full-width q-mb-md"
|
|
34
|
-
>
|
|
35
|
-
</q-input>
|
|
36
|
-
</q-card-section>
|
|
37
|
-
|
|
38
|
-
<q-card-actions>
|
|
39
|
-
<q-btn
|
|
40
|
-
rounded
|
|
41
|
-
:label="$t('common.register')"
|
|
42
|
-
color="primary"
|
|
43
|
-
:loading="loading"
|
|
44
|
-
type="submit"
|
|
45
|
-
class="full-width"
|
|
46
|
-
:disable="canSubmit"
|
|
2
|
+
<q-layout>
|
|
3
|
+
<q-page class="flex flex-center">
|
|
4
|
+
<div>
|
|
5
|
+
<div class="column items-center q-mb-xl">
|
|
6
|
+
<img
|
|
7
|
+
style="width: 40px"
|
|
8
|
+
src="../../assets/Logo.svg"
|
|
9
|
+
class="q-mb-sm"
|
|
47
10
|
/>
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
11
|
+
<div
|
|
12
|
+
class="text-h5 text-center head q-mt-sm"
|
|
13
|
+
style="font-size: 17px; font-weight: 500"
|
|
14
|
+
>
|
|
15
|
+
{{ $t("auth.passwordRecovery") }}
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
<q-form class="column items-center" @submit="onSubmit">
|
|
19
|
+
<div>
|
|
20
|
+
<div class="q-mb-sm" style="font-size: 14px; font-weight: 400">
|
|
21
|
+
{{ $t("auth.newPassword") }}
|
|
22
|
+
</div>
|
|
23
|
+
<q-input
|
|
24
|
+
id="password"
|
|
25
|
+
v-model="password"
|
|
26
|
+
data-cy="reset-password"
|
|
27
|
+
:rules="validations['passwordRules']"
|
|
28
|
+
outlined
|
|
29
|
+
dense
|
|
30
|
+
class="full-width q-mb-md"
|
|
31
|
+
/>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<div class="full-width">
|
|
35
|
+
<q-btn
|
|
36
|
+
rounded
|
|
37
|
+
:label="$t('common.register')"
|
|
38
|
+
color="primary"
|
|
39
|
+
:loading="loading"
|
|
40
|
+
type="submit"
|
|
41
|
+
no-caps
|
|
42
|
+
data-cy="send-new-password"
|
|
43
|
+
class="full-width"
|
|
44
|
+
:disable="!canSubmit"
|
|
45
|
+
/>
|
|
46
|
+
</div>
|
|
47
|
+
</q-form>
|
|
48
|
+
</div>
|
|
49
|
+
</q-page>
|
|
50
|
+
</q-layout>
|
|
52
51
|
</template>
|
|
53
52
|
|
|
54
53
|
<script lang="ts">
|
|
54
|
+
//@ts-ignore
|
|
55
|
+
import { computed, defineComponent, ref, Ref, onMounted } from "vue";
|
|
55
56
|
import { useQuasar } from "quasar";
|
|
56
|
-
import { computed, defineComponent, ref } from "vue";
|
|
57
57
|
import { useI18n } from "vue-i18n";
|
|
58
|
-
import {
|
|
59
|
-
import
|
|
58
|
+
import { $api, $prompts, $router } from "../../injects";
|
|
59
|
+
import jwt from 'jwt-decode'
|
|
60
|
+
import { Buffer } from 'buffer'
|
|
61
|
+
|
|
62
|
+
interface DecodedToken { // FIXME: consider to move to its own file
|
|
63
|
+
sub: number,
|
|
64
|
+
email: string,
|
|
65
|
+
token: string
|
|
66
|
+
}
|
|
60
67
|
|
|
61
68
|
export default defineComponent({
|
|
62
69
|
name: "ResetPassword",
|
|
63
70
|
|
|
64
71
|
setup() {
|
|
65
|
-
const { patch } = $api();
|
|
72
|
+
const { patch, post } = $api();
|
|
66
73
|
const {
|
|
74
|
+
AUTH_SERVER_SIGNING_ROUTE,
|
|
67
75
|
AUTH_SERVER_UPDATE_PASSWORD_ROUTE,
|
|
68
|
-
|
|
76
|
+
LOCAL_LOGIN_ROUTE,
|
|
69
77
|
} = $prompts();
|
|
70
78
|
const $q = useQuasar();
|
|
71
|
-
const router =
|
|
79
|
+
const router = $router()
|
|
80
|
+
const $route = router.currentRoute.value
|
|
72
81
|
const password = ref("");
|
|
73
|
-
const passwordConfirmation = ref("");
|
|
74
82
|
const loading = ref(false);
|
|
75
83
|
const { t } = useI18n();
|
|
76
84
|
|
|
85
|
+
const token = ref("")
|
|
86
|
+
|
|
87
|
+
const decodedToken = computed(() => {
|
|
88
|
+
if (!token.value) return null
|
|
89
|
+
return jwt(token.value) as DecodedToken
|
|
90
|
+
}) as Ref<DecodedToken | null>
|
|
91
|
+
|
|
77
92
|
const canSubmit = computed(() => {
|
|
78
|
-
return
|
|
79
|
-
password.value === passwordConfirmation.value
|
|
80
|
-
? false
|
|
81
|
-
: true;
|
|
93
|
+
return decodedToken.value !== null && password.value.length >= 6
|
|
82
94
|
});
|
|
83
95
|
|
|
84
96
|
const validations = {
|
|
85
97
|
passwordRules: [
|
|
86
|
-
(val: string) => !!val || t("auth.
|
|
98
|
+
(val: string) => !!val || t("auth.passwordPresenceError"),
|
|
99
|
+
(val: string) => val.length >= 6 || t("auth.passwordLengthError")
|
|
87
100
|
],
|
|
88
101
|
passwordConfirmationRules: [
|
|
89
102
|
(val: string) =>
|
|
90
103
|
val === password.value || t("auth.newPasswordConfirmationFailed"),
|
|
104
|
+
(val: string) => !!val || t("auth.resetPasswordPresenceError"),
|
|
105
|
+
(val: string) => val.length >= 6 || t("auth.resetPasswordLengthError"),
|
|
91
106
|
],
|
|
92
107
|
};
|
|
93
108
|
|
|
109
|
+
const decodeKey = (key: string) => {
|
|
110
|
+
try {
|
|
111
|
+
const decodedKey = Buffer.from(key, "base64").toString("utf-8").split(',')
|
|
112
|
+
if (decodedKey.length !== 2) {
|
|
113
|
+
throw 'Invalid encoded key'
|
|
114
|
+
}
|
|
115
|
+
console.log('decodedKey', decodedKey)
|
|
116
|
+
const email = decodedKey[0].replace('{', '').split(':')[1]
|
|
117
|
+
const tmpToken = decodedKey[1].replace('}', '').split(':')[1]
|
|
118
|
+
return {
|
|
119
|
+
decoded: true,
|
|
120
|
+
email,
|
|
121
|
+
tmpToken
|
|
122
|
+
}
|
|
123
|
+
} catch (err) {
|
|
124
|
+
console.log('decodeKey err', err)
|
|
125
|
+
return {
|
|
126
|
+
decoded: false
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
94
131
|
const onSubmit = () => {
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
132
|
+
const updatePasswordPayload = {
|
|
133
|
+
data: {
|
|
134
|
+
type: "users",
|
|
135
|
+
id: decodedToken.value.sub,
|
|
136
|
+
attributes:
|
|
137
|
+
{
|
|
138
|
+
password: password.value
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const updatePasswordHeaders = {
|
|
143
|
+
headers: {
|
|
144
|
+
'Authorization': `Bearer ${token.value}`
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
patch(
|
|
149
|
+
AUTH_SERVER_UPDATE_PASSWORD_ROUTE.replace(':userId', String(decodedToken.value.sub)),
|
|
150
|
+
updatePasswordPayload,
|
|
151
|
+
updatePasswordHeaders
|
|
152
|
+
).then(() => {
|
|
101
153
|
$q.notify({
|
|
102
154
|
message: t("auth.passwordRecoverySuccess"),
|
|
103
155
|
color: "positive",
|
|
104
156
|
position: "top",
|
|
105
157
|
});
|
|
106
|
-
router.push(
|
|
107
|
-
})
|
|
108
|
-
.catch(() => {
|
|
158
|
+
router.push(LOCAL_LOGIN_ROUTE);
|
|
159
|
+
}).catch(() => {
|
|
109
160
|
$q.notify({
|
|
110
161
|
message: t("auth.passwordRecoveryFailed"),
|
|
111
162
|
color: "negative",
|
|
@@ -114,9 +165,51 @@ export default defineComponent({
|
|
|
114
165
|
});
|
|
115
166
|
};
|
|
116
167
|
|
|
168
|
+
onMounted(() => {
|
|
169
|
+
let key = $route.query.key as string
|
|
170
|
+
const { decoded, email, tmpToken } = decodeKey(key)
|
|
171
|
+
|
|
172
|
+
if (!decoded) {
|
|
173
|
+
return $q.notify({
|
|
174
|
+
message: t("auth.passwordRecoveryInvalidLink"),
|
|
175
|
+
color: "negative",
|
|
176
|
+
position: "top",
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const tmpAuthPayload = {
|
|
181
|
+
auth: {
|
|
182
|
+
email: email,
|
|
183
|
+
token: tmpToken
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
post(AUTH_SERVER_SIGNING_ROUTE, tmpAuthPayload)
|
|
188
|
+
.then((response) => {
|
|
189
|
+
token.value = response.data.jwt
|
|
190
|
+
if (!token.value) throw 'Error reading the temporary token'
|
|
191
|
+
})
|
|
192
|
+
.catch((error) => {
|
|
193
|
+
if (error.response) {
|
|
194
|
+
// Request made and server responded
|
|
195
|
+
if (error.response.status === 404) {
|
|
196
|
+
return $q.notify({
|
|
197
|
+
message: t("auth.passwordRecoveryExpiredLink"),
|
|
198
|
+
color: "negative",
|
|
199
|
+
position: "top",
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return $q.notify({
|
|
204
|
+
message: t("auth.passwordRecoveryFailed"),
|
|
205
|
+
color: "negative",
|
|
206
|
+
position: "top",
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
})
|
|
210
|
+
|
|
117
211
|
return {
|
|
118
212
|
password,
|
|
119
|
-
passwordConfirmation,
|
|
120
213
|
validations,
|
|
121
214
|
loading,
|
|
122
215
|
onSubmit,
|
|
@@ -127,13 +220,8 @@ export default defineComponent({
|
|
|
127
220
|
</script>
|
|
128
221
|
|
|
129
222
|
<style scoped>
|
|
130
|
-
.
|
|
223
|
+
@import url("https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600&display=swap");
|
|
131
224
|
.q-page {
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
.q-card {
|
|
136
|
-
width: 400px;
|
|
137
|
-
padding: 50px;
|
|
225
|
+
font-family: "Montserrat", sans-serif;
|
|
138
226
|
}
|
|
139
227
|
</style>
|
package/src/injects/prompts.ts
CHANGED
package/src/prompts.js
CHANGED
|
@@ -27,7 +27,7 @@ module.exports = function () {
|
|
|
27
27
|
type: "input",
|
|
28
28
|
required: true,
|
|
29
29
|
message: "Route to update the password (page)",
|
|
30
|
-
default: "/
|
|
30
|
+
default: "/ResetPassword/",
|
|
31
31
|
},
|
|
32
32
|
{
|
|
33
33
|
name: "AUTH_SERVER_BASE_URL",
|
|
@@ -41,7 +41,7 @@ module.exports = function () {
|
|
|
41
41
|
type: "input",
|
|
42
42
|
required: true,
|
|
43
43
|
message: "API endpoint for signin (without host)",
|
|
44
|
-
default: "/
|
|
44
|
+
default: "/auth",
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
name: "AUTH_SERVER_RESET_PASSWORD_ROUTE",
|
package/src/routes/index.ts
CHANGED
package/src/store/index.ts
CHANGED
|
@@ -34,13 +34,28 @@ const mutations: MutationTree<tokenStateInterface> = {
|
|
|
34
34
|
},
|
|
35
35
|
setPayload(state, value: payload) {
|
|
36
36
|
state.payload = {...value}
|
|
37
|
+
},
|
|
38
|
+
reset(state) {
|
|
39
|
+
state.raw = ""
|
|
40
|
+
state.payload = {
|
|
41
|
+
company_hp: undefined,
|
|
42
|
+
exp: undefined,
|
|
43
|
+
gm_key: "",
|
|
44
|
+
roles: [],
|
|
45
|
+
sub: null
|
|
46
|
+
}
|
|
37
47
|
}
|
|
38
48
|
}
|
|
39
49
|
|
|
40
50
|
const actions: ActionTree<tokenStateInterface, any> = {
|
|
41
51
|
updateRaw({ commit }, value: string) {
|
|
42
52
|
commit("setRaw", value);
|
|
43
|
-
|
|
53
|
+
let parseJwt: string
|
|
54
|
+
try {
|
|
55
|
+
parseJwt = JSON.parse(window.atob(value.split(".")[1]));
|
|
56
|
+
} catch (error) {
|
|
57
|
+
parseJwt = ""
|
|
58
|
+
}
|
|
44
59
|
commit("setPayload", parseJwt)
|
|
45
60
|
}
|
|
46
61
|
}
|