@sowell/quasar-app-extension-auth 0.2.5 → 0.2.9
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/publish.yml +24 -0
- package/README.md +3 -3
- package/package.json +2 -1
- package/src/components/pages/Auth.vue +1 -0
- package/src/components/pages/ResetPassword.vue +124 -16
- package/src/i18n/en-US/index.ts +7 -0
- package/src/i18n/fr-FR/index.ts +8 -1
- package/src/prompts.js +2 -2
|
@@ -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.9",
|
|
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",
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"axios": "^0.21.1",
|
|
42
|
+
"buffer": "^6.0.3",
|
|
42
43
|
"qs": "^6.10.1",
|
|
43
44
|
"validator": "^13.6.0",
|
|
44
45
|
"vue-i18n": "^9.1.0",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
no-caps
|
|
42
42
|
data-cy="send-new-password"
|
|
43
43
|
class="full-width"
|
|
44
|
-
:disable="canSubmit"
|
|
44
|
+
:disable="!canSubmit"
|
|
45
45
|
/>
|
|
46
46
|
</div>
|
|
47
47
|
</q-form>
|
|
@@ -52,50 +52,115 @@
|
|
|
52
52
|
|
|
53
53
|
<script lang="ts">
|
|
54
54
|
//@ts-ignore
|
|
55
|
-
import { computed, defineComponent, ref } from "vue";
|
|
55
|
+
import { computed, defineComponent, ref, Ref, onMounted } from "vue";
|
|
56
56
|
import { useQuasar } from "quasar";
|
|
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/' // NOTE: the trailing '/' is intentional and required by 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
82
|
const loading = ref(false);
|
|
74
83
|
const { t } = useI18n();
|
|
75
84
|
|
|
76
|
-
const
|
|
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
|
+
|
|
92
|
+
const canSubmit = computed(() => {
|
|
93
|
+
return decodedToken.value !== null && password.value.length >= 6
|
|
94
|
+
});
|
|
77
95
|
|
|
78
96
|
const validations = {
|
|
79
97
|
passwordRules: [
|
|
80
98
|
(val: string) => !!val || t("auth.passwordPresenceError"),
|
|
99
|
+
(val: string) => val.length >= 6 || t("auth.passwordLengthError")
|
|
100
|
+
],
|
|
101
|
+
passwordConfirmationRules: [
|
|
102
|
+
(val: string) =>
|
|
103
|
+
val === password.value || t("auth.newPasswordConfirmationFailed"),
|
|
104
|
+
(val: string) => !!val || t("auth.passwordPresenceError"),
|
|
81
105
|
(val: string) => val.length >= 6 || t("auth.passwordLengthError"),
|
|
82
106
|
],
|
|
83
107
|
};
|
|
84
108
|
|
|
109
|
+
const decodeKey = (key: string) => {
|
|
110
|
+
try {
|
|
111
|
+
// remove backslash to avoid decoding error
|
|
112
|
+
key = key.replace(/\\n/g, '')
|
|
113
|
+
const decodedKey = Buffer.from(key, 'base64').toString('utf-8').split(',')
|
|
114
|
+
// split instead of JSON.parse as JSON.parse doesn't work
|
|
115
|
+
|
|
116
|
+
if (decodedKey.length !== 2) {
|
|
117
|
+
throw 'Invalid encoded key'
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const email = decodedKey[0].replace('{', '').split(':')[1]
|
|
121
|
+
const tmpToken = decodedKey[1].replace('}', '').split(':')[1]
|
|
122
|
+
return {
|
|
123
|
+
decoded: true,
|
|
124
|
+
email,
|
|
125
|
+
tmpToken
|
|
126
|
+
}
|
|
127
|
+
} catch (err) {
|
|
128
|
+
console.log('decodeKey err', err)
|
|
129
|
+
return {
|
|
130
|
+
decoded: false
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
85
135
|
const onSubmit = () => {
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
136
|
+
const updatePasswordPayload = {
|
|
137
|
+
data: {
|
|
138
|
+
type: "users",
|
|
139
|
+
id: decodedToken.value.sub,
|
|
140
|
+
attributes:
|
|
141
|
+
{
|
|
142
|
+
password: password.value
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const updatePasswordHeaders = {
|
|
147
|
+
headers: {
|
|
148
|
+
'Authorization': `Bearer ${token.value}`
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
patch(
|
|
153
|
+
AUTH_SERVER_UPDATE_PASSWORD_ROUTE.replace(':userId', String(decodedToken.value.sub)),
|
|
154
|
+
updatePasswordPayload,
|
|
155
|
+
updatePasswordHeaders
|
|
156
|
+
).then(() => {
|
|
91
157
|
$q.notify({
|
|
92
158
|
message: t("auth.passwordRecoverySuccess"),
|
|
93
159
|
color: "positive",
|
|
94
160
|
position: "top",
|
|
95
161
|
});
|
|
96
|
-
router.push(
|
|
97
|
-
})
|
|
98
|
-
.catch(() => {
|
|
162
|
+
router.push(LOCAL_LOGIN_ROUTE);
|
|
163
|
+
}).catch(() => {
|
|
99
164
|
$q.notify({
|
|
100
165
|
message: t("auth.passwordRecoveryFailed"),
|
|
101
166
|
color: "negative",
|
|
@@ -104,6 +169,49 @@ export default defineComponent({
|
|
|
104
169
|
});
|
|
105
170
|
};
|
|
106
171
|
|
|
172
|
+
onMounted(() => {
|
|
173
|
+
let key = $route.query.key as string
|
|
174
|
+
const { decoded, email, tmpToken } = decodeKey(key)
|
|
175
|
+
|
|
176
|
+
if (!decoded) {
|
|
177
|
+
return $q.notify({
|
|
178
|
+
message: t("auth.passwordRecoveryInvalidLink"),
|
|
179
|
+
color: "negative",
|
|
180
|
+
position: "top",
|
|
181
|
+
})
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const tmpAuthPayload = {
|
|
185
|
+
auth: {
|
|
186
|
+
email: email,
|
|
187
|
+
token: tmpToken
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
post(AUTH_SERVER_SIGNING_ROUTE, tmpAuthPayload)
|
|
192
|
+
.then((response) => {
|
|
193
|
+
token.value = response.data.jwt
|
|
194
|
+
if (!token.value) throw 'Error reading the temporary token'
|
|
195
|
+
})
|
|
196
|
+
.catch((error) => {
|
|
197
|
+
if (error.response) {
|
|
198
|
+
// Request made and server responded
|
|
199
|
+
if (error.response.status === 404) {
|
|
200
|
+
return $q.notify({
|
|
201
|
+
message: t("auth.passwordRecoveryExpiredLink"),
|
|
202
|
+
color: "negative",
|
|
203
|
+
position: "top",
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return $q.notify({
|
|
208
|
+
message: t("auth.passwordRecoveryFailed"),
|
|
209
|
+
color: "negative",
|
|
210
|
+
position: "top",
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
})
|
|
214
|
+
|
|
107
215
|
return {
|
|
108
216
|
password,
|
|
109
217
|
validations,
|
package/src/i18n/en-US/index.ts
CHANGED
|
@@ -15,6 +15,13 @@ export default {
|
|
|
15
15
|
tapEmail: "Please enter your email",
|
|
16
16
|
404: "No user found with the provided credentials",
|
|
17
17
|
403: "Insufficient rights, your account does not allow access to this interface",
|
|
18
|
+
passwordRecovery: 'Password recovery',
|
|
19
|
+
newPassword: 'New password',
|
|
20
|
+
newPasswordConfirmationFailed: "The passwords you typed don't match",
|
|
21
|
+
passwordRecoverySuccess: 'Your password has been updated',
|
|
22
|
+
passwordRecoveryFailed: 'An error occured while updating your password',
|
|
23
|
+
passwordRecoveryExpiredLink: 'The link expired',
|
|
24
|
+
passwordRecoveryInvalidLink: 'the link is invalid'
|
|
18
25
|
},
|
|
19
26
|
common: {
|
|
20
27
|
register: "Save",
|
package/src/i18n/fr-FR/index.ts
CHANGED
|
@@ -15,9 +15,16 @@ export default {
|
|
|
15
15
|
tapEmail: "Entrer votre email",
|
|
16
16
|
404: "Aucun utilisateur trouvé avec les identifiants donnés",
|
|
17
17
|
403: "Droits insuffisants, votre compte ne permet pas d'acceder à cette interface",
|
|
18
|
+
passwordRecovery: 'Récupération de mot de passe',
|
|
19
|
+
newPassword: 'Nouveau mot de passe',
|
|
20
|
+
newPasswordConfirmationFailed: 'Les mot de passes que vous avez saisi ne correspondent pas',
|
|
21
|
+
passwordRecoverySuccess: 'Votre mot de passe a été mis à jour',
|
|
22
|
+
passwordRecoveryFailed: "Une erreur s'est produite lors de la mise à jour de votre mot de passe",
|
|
23
|
+
passwordRecoveryExpiredLink: 'Lien de récupération expiré',
|
|
24
|
+
passwordRecoveryInvalidLink: 'Lien de récupération invalide'
|
|
18
25
|
},
|
|
19
26
|
common: {
|
|
20
|
-
register: "
|
|
27
|
+
register: "Enregistrer",
|
|
21
28
|
send: "Envoyer",
|
|
22
29
|
cancel: "Annuler",
|
|
23
30
|
},
|
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",
|