@sowell/quasar-app-extension-auth 0.4.1 → 0.4.3

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.4.1",
3
+ "version": "0.4.3",
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",
@@ -0,0 +1,13 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g clip-path="url(#clip0_9080_32853)">
3
+ <path d="M11.4062 11.4062H0V0H11.4062V11.4062Z" fill="#F1511B"/>
4
+ <path d="M24 11.4062H12.5939V0H24V11.4062Z" fill="#80CC28"/>
5
+ <path d="M11.4059 24.0002H0V12.594H11.4059V24.0002Z" fill="#00ADEF"/>
6
+ <path d="M24 24.0002H12.5939V12.594H24V24.0002Z" fill="#FBBC09"/>
7
+ </g>
8
+ <defs>
9
+ <clipPath id="clip0_9080_32853">
10
+ <rect width="24" height="24" fill="white"/>
11
+ </clipPath>
12
+ </defs>
13
+ </svg>
@@ -9,7 +9,7 @@
9
9
  <div class="q-pl-md q-pr-md" style="max-width: 500px">
10
10
  <h4 class="title">{{ WELCOME_TITLE }}</h4>
11
11
  <div class="subtitle">{{ WELCOME_SUBTITLE }}</div>
12
- <q-form @submit="onSubmit">
12
+ <q-form @submit="handleSubmit">
13
13
  <div>
14
14
  <div class="q-ml-xs q-mb-xs text-subtitle2 text-weight-bold">
15
15
  {{ $t("auth.email") }}
@@ -160,6 +160,10 @@ export default defineComponent({
160
160
  icons,
161
161
  } = useAuth();
162
162
 
163
+ const handleSubmit = () => {
164
+ onSubmit()
165
+ }
166
+
163
167
  const IS_ADMIN = ["admin"].includes(X_REQUESTED_ROLES);
164
168
  const isNotification = ref(false);
165
169
  const requestedOtp = ref(false);
@@ -232,7 +236,7 @@ export default defineComponent({
232
236
  goToOtpValidation,
233
237
  resetPassword,
234
238
  retypeEmail,
235
- onSubmit,
239
+ handleSubmit,
236
240
  icons,
237
241
  };
238
242
  },
@@ -28,7 +28,7 @@
28
28
  {{ $t("auth.loginHelp") }}
29
29
  </div>
30
30
  </div>
31
- <q-form @submit="onSubmit">
31
+ <q-form @submit="handleSubmit">
32
32
  <div>
33
33
  <div
34
34
  class="q-ml-xs q-mb-xs"
@@ -82,15 +82,6 @@
82
82
  </template>
83
83
  </q-input>
84
84
  </div>
85
-
86
- <div
87
- class="q-mb-lg q-mt-xl text-primary"
88
- @click="forgottenPassword = true"
89
- data-cy="forgot-password"
90
- style="font-size: 16px; font-weight: 500; cursor: pointer"
91
- >
92
- {{ $t("auth.forgottenPassword") }}
93
- </div>
94
85
  <q-btn
95
86
  data-cy="submit-login"
96
87
  :label="$t('auth.login')"
@@ -103,6 +94,44 @@
103
94
  class="full-width q-mt-lg"
104
95
  :disable="isSubmitBtnDisabled()"
105
96
  />
97
+ <div
98
+ class="q-mb-lg q-mt-xl text-primary"
99
+ @click="forgottenPassword = true"
100
+ data-cy="forgot-password"
101
+ style="font-size: 16px; font-weight: 500; cursor: pointer"
102
+ >
103
+ {{ $t("auth.forgottenPassword") }}
104
+ </div>
105
+ <div v-if="USE_SSO">
106
+ <div
107
+ style="
108
+ display: flex;
109
+ justify-content: space-between;
110
+ align-items: center;
111
+ "
112
+ >
113
+ <div
114
+ style="width: 35%; border: 1px solid #bcc3c7; height: 1px"
115
+ ></div>
116
+ <div class="text-weight-bold">{{ $t("common.or") }}</div>
117
+ <div
118
+ style="width: 35%; border: 1px solid #bcc3c7; height: 1px"
119
+ ></div>
120
+ </div>
121
+ <q-btn
122
+ data-cy="submit-sso-login"
123
+ :label="$t('auth.msalLogin')"
124
+ color="primary"
125
+ no-caps
126
+ unelevated
127
+ text-color="black"
128
+ :loading="loading"
129
+ :icon="`img:${MicrosoftLogo}`"
130
+ class="full-width q-mt-lg"
131
+ @click="msalAuth"
132
+ outline
133
+ />
134
+ </div>
106
135
  </q-form>
107
136
  </div>
108
137
  <div>
@@ -144,6 +173,7 @@ import * as packageInfo from "app/package.json";
144
173
  import SendEmail from "../dialogs/SendEmail.vue";
145
174
  import SowellMessage from "../dialogs/Message.vue";
146
175
  import { useAuth } from "composables/auth";
176
+ import MicrosoftLogo from "../../assets/logo-microsoft.svg";
147
177
 
148
178
  export default defineComponent({
149
179
  name: "Auth",
@@ -165,6 +195,8 @@ export default defineComponent({
165
195
  retypeEmail,
166
196
  isSubmitBtnDisabled,
167
197
  icons,
198
+ USE_SSO,
199
+ msalAuth,
168
200
  } = useAuth();
169
201
 
170
202
  const appVersion = reactive({
@@ -189,6 +221,10 @@ export default defineComponent({
189
221
  }
190
222
  };
191
223
 
224
+ const handleSubmit = () => {
225
+ onSubmit("SoWell");
226
+ };
227
+
192
228
  onMounted(() => {
193
229
  appVersion.versionNumber = packageInfo.version;
194
230
  });
@@ -209,6 +245,10 @@ export default defineComponent({
209
245
  retypeEmail,
210
246
  icons,
211
247
  appVersion,
248
+ USE_SSO,
249
+ msalAuth,
250
+ handleSubmit,
251
+ MicrosoftLogo,
212
252
  };
213
253
  },
214
254
  });
@@ -17,7 +17,7 @@
17
17
  </div>
18
18
  </div>
19
19
  <div class="">
20
- <q-form @submit="onSubmit">
20
+ <q-form @submit="handleSubmit">
21
21
  <div>
22
22
  <div
23
23
  class="q-ml-xs q-mb-xs"
@@ -187,6 +187,10 @@ export default defineComponent({
187
187
  icons,
188
188
  } = useAuth();
189
189
 
190
+ const handleSubmit = () => {
191
+ onSubmit()
192
+ }
193
+
190
194
  const appVersion = reactive({
191
195
  versionNumber: "",
192
196
  });
@@ -229,7 +233,7 @@ export default defineComponent({
229
233
  emailResetPassword,
230
234
  isReset,
231
235
  sendEmailSuccess,
232
- onSubmit,
236
+ handleSubmit,
233
237
  resetPassword,
234
238
  goToOtpValidation,
235
239
  isSubmitBtnDisabled,
@@ -9,7 +9,48 @@
9
9
  <div class="q-pl-md q-pr-md" style="max-width: 500px">
10
10
  <h4 class="title">{{ WELCOME_TITLE }}</h4>
11
11
  <div class="subtitle">{{ WELCOME_SUBTITLE }}</div>
12
- <q-form @submit="onSubmit">
12
+ <div v-if="USE_SSO">
13
+ <q-btn
14
+ data-cy="submit-sso-login"
15
+ :label="$t('auth.msalLogin')"
16
+ color="primary"
17
+ no-caps
18
+ unelevated
19
+ text-color="black"
20
+ :loading="loading"
21
+ :icon="`img:${MicrosoftLogo}`"
22
+ class="full-width q-mt-lg"
23
+ @click="msalAuth"
24
+ outline
25
+ />
26
+ <div
27
+ style="
28
+ display: flex;
29
+ justify-content: center;
30
+ align-items: center;
31
+ margin-top: 16px;
32
+ "
33
+ >
34
+ <div
35
+ style="
36
+ width: 20%;
37
+ border: 1px solid #bcc3c7;
38
+ height: 1px;
39
+ margin-right: 16px;
40
+ "
41
+ ></div>
42
+ <div class="text-weight-bold">{{ $t("common.or") }}</div>
43
+ <div
44
+ style="
45
+ width: 20%;
46
+ border: 1px solid #bcc3c7;
47
+ height: 1px;
48
+ margin-left: 16px;
49
+ "
50
+ ></div>
51
+ </div>
52
+ </div>
53
+ <q-form @submit="handleSubmit">
13
54
  <div>
14
55
  <div class="q-ml-xs q-mb-xs text-subtitle2 text-weight-bold">
15
56
  {{ $t("auth.email") }}
@@ -134,6 +175,7 @@ import SwInfoDialog from "../dialogs/sw-info-dialog.vue";
134
175
  import WebSendEmail from "../dialogs/WebSendEmail.vue";
135
176
 
136
177
  import { useAuth } from "composables/auth";
178
+ import MicrosoftLogo from "../../assets/logo-microsoft.svg";
137
179
 
138
180
  interface HeadersInterface {
139
181
  headers: {
@@ -165,9 +207,13 @@ export default defineComponent({
165
207
  retypeEmail,
166
208
  isSubmitBtnDisabled,
167
209
  icons,
210
+ USE_SSO,
211
+ msalAuth,
168
212
  } = useAuth();
169
213
 
170
- const IS_MANAGER = ["manager", "clean_manager", "admin"].includes(X_REQUESTED_ROLES);
214
+ const IS_MANAGER = ["manager", "clean_manager", "admin"].includes(
215
+ X_REQUESTED_ROLES
216
+ );
171
217
  const isNotification = ref(false);
172
218
  const requestedOtp = ref(false);
173
219
  const infoDialog = ref({
@@ -220,6 +266,10 @@ export default defineComponent({
220
266
  }
221
267
  };
222
268
 
269
+ const handleSubmit = () => {
270
+ onSubmit("SoWell");
271
+ };
272
+
223
273
  return {
224
274
  email,
225
275
  infoDialog,
@@ -241,6 +291,10 @@ export default defineComponent({
241
291
  retypeEmail,
242
292
  onSubmit,
243
293
  icons,
294
+ USE_SSO,
295
+ MicrosoftLogo,
296
+ handleSubmit,
297
+ msalAuth,
244
298
  };
245
299
  },
246
300
  });
@@ -264,7 +318,7 @@ export default defineComponent({
264
318
  margin: 0px 0px 16px 0px;
265
319
  }
266
320
  .subtitle {
267
- margin-bottom: 80px;
321
+ margin-bottom: 40px;
268
322
  }
269
323
  .logoWhite {
270
324
  max-width: 600px;
@@ -11,7 +11,13 @@ import {
11
11
  } from "@quasar/extras/ionicons-v5";
12
12
  import { Platform } from "quasar";
13
13
 
14
- import { $prompts, $router, SignInPayload, useAuthState } from "../injects";
14
+ import {
15
+ $prompts,
16
+ $router,
17
+ SignInPayload,
18
+ useAuthState,
19
+ useMsalState,
20
+ } from "../injects";
15
21
 
16
22
  interface HeadersInterface {
17
23
  headers: {
@@ -21,13 +27,14 @@ interface HeadersInterface {
21
27
 
22
28
  export function useAuth() {
23
29
  const authState = useAuthState();
24
- const device = inject("Device")
30
+ const msalState = useMsalState();
31
+ const device = inject("Device");
25
32
  let deviceInfos = {
26
33
  manufacturer: "",
27
34
  model: "",
28
35
  platform: "",
29
- version: ""
30
- }
36
+ version: "",
37
+ };
31
38
 
32
39
  const { t } = useI18n();
33
40
  const {
@@ -38,12 +45,14 @@ export function useAuth() {
38
45
  WELCOME_SUBTITLE,
39
46
  IS_OTP,
40
47
  LOCAL_OTP_ROUTE,
41
- APP_VERSION
48
+ APP_VERSION,
49
+ USE_SSO,
42
50
  } = $prompts();
43
51
  const { push, replace } = $router();
44
52
 
45
53
  const email = ref("");
46
54
  const password = ref("");
55
+ const msalToken = ref("");
47
56
  const loading = ref(false);
48
57
  const showPassword = ref(false);
49
58
  const forgottenPassword = ref(false);
@@ -86,17 +95,18 @@ export function useAuth() {
86
95
  manufacturer: info.manufacturer,
87
96
  model: info.model,
88
97
  platform: info.platform,
89
- version: info.osVersion
90
- }
98
+ version: info.osVersion,
99
+ };
91
100
  }
92
- }
101
+ };
93
102
 
94
- const onSubmit = async () => {
103
+ const onSubmit = async (provider = "SoWell") => {
95
104
  loading.value = true;
96
105
  const data: SignInPayload = {
97
106
  auth: {
98
107
  email: email.value,
99
- password: password.value,
108
+ password: provider === "SoWell" ? password.value : undefined,
109
+ msalToken: provider === "MSAL" ? msalToken.value : undefined,
100
110
  },
101
111
  };
102
112
  if (device && Platform.is.capacitor) {
@@ -104,10 +114,10 @@ export function useAuth() {
104
114
  manufacturer: deviceInfos.manufacturer,
105
115
  model: deviceInfos.model,
106
116
  platform: deviceInfos.platform,
107
- version: deviceInfos.version
117
+ version: deviceInfos.version,
108
118
  }),
109
119
  (data["app-clean"] = {
110
- versionNumber: APP_VERSION
120
+ versionNumber: APP_VERSION,
111
121
  });
112
122
  }
113
123
  const options: HeadersInterface = {
@@ -126,7 +136,10 @@ export function useAuth() {
126
136
  // @ts-ignore
127
137
  if (!window.Cypress) {
128
138
  // FIXME: encrypt password
129
- localStorage.setItem("credentials", JSON.stringify(data.auth));
139
+ localStorage.setItem(
140
+ "credentials",
141
+ JSON.stringify({ ...data.auth, provider: provider })
142
+ );
130
143
  }
131
144
  push(LOCAL_SUCCESS_REDIRECTION_ROUTE);
132
145
  } catch (err) {
@@ -157,8 +170,19 @@ export function useAuth() {
157
170
  push(LOCAL_OTP_ROUTE);
158
171
  };
159
172
 
173
+ const msalAuth = () => {
174
+ msalState.signIn().then(
175
+ async (result) => {
176
+ email.value = result.email;
177
+ msalToken.value = result.token;
178
+ await onSubmit("MSAL");
179
+ },
180
+ () => msalState.reset()
181
+ );
182
+ };
183
+
160
184
  onMounted(async () => {
161
- await deviceInfo()
185
+ await deviceInfo();
162
186
  if (authState.raw) {
163
187
  replace(LOCAL_SUCCESS_REDIRECTION_ROUTE);
164
188
  }
@@ -186,6 +210,7 @@ export function useAuth() {
186
210
  X_REQUESTED_ROLES,
187
211
  IS_OTP,
188
212
  LOCAL_OTP_ROUTE,
213
+ USE_SSO,
189
214
  goToOtpValidation,
190
215
  onSubmit,
191
216
  retypeEmail,
@@ -198,5 +223,7 @@ export function useAuth() {
198
223
  ionEyeSharp,
199
224
  ionEyeOffSharp,
200
225
  },
226
+ msalState,
227
+ msalAuth,
201
228
  };
202
229
  }
@@ -5,6 +5,7 @@ export default {
5
5
  password: "Password",
6
6
  forgottenPassword: "Forgotten password",
7
7
  login: "Login",
8
+ msalLogin: "Sign in with Microsoft",
8
9
  emailPresenceError: "The email must be provided",
9
10
  emailValidationError: "The email is not valid",
10
11
  passwordPresenceError: "The password must be provided",
@@ -27,5 +28,6 @@ export default {
27
28
  register: "Save",
28
29
  send: "Send",
29
30
  cancel: "Cancel",
31
+ or: "or"
30
32
  },
31
33
  };
@@ -5,6 +5,7 @@ export default {
5
5
  password: "Mot de passe",
6
6
  forgottenPassword: "Mot de passe oublié",
7
7
  login: "Connexion",
8
+ msalLogin: "Se connecter avec Microsoft",
8
9
  emailPresenceError: "L'adresse email doit être fournie",
9
10
  emailValidationError: "L'adresse email fournie est invalide",
10
11
  passwordPresenceError: "Le mot de passe doit être fourni",
@@ -27,5 +28,6 @@ export default {
27
28
  register: "Enregistrer",
28
29
  send: "Envoyer",
29
30
  cancel: "Annuler",
31
+ or: "ou"
30
32
  },
31
33
  };
@@ -23,6 +23,7 @@ export interface PromptsInterface {
23
23
  AUTH_SERVER_VERIFY_OTP: string;
24
24
  UI_PAGE: string;
25
25
  APP_VERSION: string;
26
+ USE_SSO: boolean
26
27
  }
27
28
  export const $prompts = (): PromptsInterface => {
28
29
  const $prompts = inject<Record<"@sowell/auth", PromptsInterface>>("$prompts");
@@ -1,6 +1,7 @@
1
1
  import { inject } from "vue";
2
2
  import { Store } from "pinia";
3
3
  import { AxiosResponse } from "axios";
4
+ import { $prompts } from "../injects";
4
5
 
5
6
  export interface AuthStatePayload {
6
7
  company_hp: string;
@@ -14,7 +15,33 @@ export interface AuthState {
14
15
  raw?: string;
15
16
  payload?: AuthStatePayload;
16
17
  }
18
+ interface ConfigMobileAuth {
19
+ clientId: string;
20
+ scopes: ["email", "User.Read"];
21
+ keyHash: "2jmj7l5rSw0yVb/vlWAYkK/YBwk=";
22
+ }
17
23
 
24
+ export interface MsalState {
25
+ configMobile: {
26
+ auth: ConfigMobileAuth;
27
+ cache: {
28
+ cacheLocation: "localStorage";
29
+ };
30
+ };
31
+ configWeb: {
32
+ auth: {
33
+ clientId: string;
34
+ authority: "https://login.microsoftonline.com/organizations";
35
+ redirectUri: string;
36
+ };
37
+ cache: {
38
+ cacheLocation: "localStorage";
39
+ };
40
+ };
41
+ accessToken: string;
42
+ idToken: string;
43
+ email: string;
44
+ }
18
45
  export interface RequestOptions {
19
46
  headers: {
20
47
  "x-requested-roles"?: string;
@@ -23,18 +50,19 @@ export interface RequestOptions {
23
50
 
24
51
  export interface SignInPayload {
25
52
  auth: {
26
- email: string
27
- password: string
28
- }
53
+ email: string;
54
+ password?: string;
55
+ msalToken?: string;
56
+ };
29
57
  "device-clean"?: {
30
- manufacturer: string
31
- model: string
32
- platform: string,
33
- version: string
34
- }
58
+ manufacturer: string;
59
+ model: string;
60
+ platform: string;
61
+ version: string;
62
+ };
35
63
  "app-clean"?: {
36
- versionNumber: string
37
- }
64
+ versionNumber: string;
65
+ };
38
66
  }
39
67
 
40
68
  export interface ResetPasswordPayload {
@@ -55,6 +83,7 @@ export const useAuthState = (): Store<
55
83
  data: SignInPayload,
56
84
  options: RequestOptions
57
85
  ): Promise<AxiosResponse>;
86
+ msalSignin(): Promise<AxiosResponse>;
58
87
  resetPassword(data: ResetPasswordPayload): Promise<AxiosResponse>;
59
88
  updateRaw(value: string): void;
60
89
  }
@@ -65,3 +94,19 @@ export const useAuthState = (): Store<
65
94
  }
66
95
  return authState;
67
96
  };
97
+
98
+ export const useMsalState = (): Store<
99
+ "msal",
100
+ MsalState,
101
+ {
102
+ signIn(): () => Promise<{ email: string; token: string }>;
103
+ reset(): () => void;
104
+ }
105
+ > => {
106
+ const { USE_SSO } = $prompts();
107
+ const msalState = inject<Store<"msal", MsalState>>("msalState");
108
+ if (USE_SSO && msalState == undefined) {
109
+ throw Error("Could not inject msalState");
110
+ }
111
+ return msalState;
112
+ };