@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.
@@ -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 mamy-auth
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 /users/sign_in
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 mamy-auth
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.5",
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",
@@ -145,6 +145,7 @@ export default defineComponent({
145
145
  },
146
146
  };
147
147
 
148
+
148
149
  post(AUTH_SERVER_RESET_PASSWORD_ROUTE, data)
149
150
  .then(() => {
150
151
  forgottenPassword.value = false;
@@ -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 { useRouter } from "vue-router";
59
- import { $api, $prompts } from "../../injects";
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
- LOCAL_SUCCESS_REDIRECTION_ROUTE,
76
+ LOCAL_LOGIN_ROUTE,
69
77
  } = $prompts();
70
78
  const $q = useQuasar();
71
- const router = useRouter();
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 canSubmit = computed(() => password.value.length < 6);
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 data = {
87
- password: password.value,
88
- };
89
- patch(AUTH_SERVER_UPDATE_PASSWORD_ROUTE, data)
90
- .then(() => {
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(LOCAL_SUCCESS_REDIRECTION_ROUTE);
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,
@@ -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",
@@ -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: "Enregistre",
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: "/reset_password",
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: "/users/sign_in",
44
+ default: "/auth",
45
45
  },
46
46
  {
47
47
  name: "AUTH_SERVER_RESET_PASSWORD_ROUTE",