@adminforth/two-factors-auth 3.1.2 → 3.1.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/build.log CHANGED
@@ -16,5 +16,5 @@ custom/pnpm-lock.yaml
16
16
  custom/tsconfig.json
17
17
  custom/utils.js
18
18
 
19
- sent 282,665 bytes received 229 bytes 565,788.00 bytes/sec
20
- total size is 281,759 speedup is 1.00
19
+ sent 283,319 bytes received 229 bytes 567,096.00 bytes/sec
20
+ total size is 282,425 speedup is 1.00
@@ -4,6 +4,7 @@
4
4
  <div v-if="modalMode === 'totp'" class="af-two-factor-modal-totp flex flex-col items-center relative bg-white dark:bg-gray-700 rounded-lg shadow p-6 w-full max-w-md">
5
5
  <div id="mfaCode-label" class="mb-4 text-gray-700 dark:text-gray-100 text-center">
6
6
  <p> {{ customDialogTitle }} </p>
7
+ <p class="text-red-500 font-semibold text-xl" v-if="sessionsIdsToResolve.length > 1"> You are confirming {{ sessionsIdsToResolve.length }} actions</p>
7
8
  <p>{{ $t('Please enter your authenticator code') }}</p>
8
9
  </div>
9
10
 
@@ -51,6 +52,7 @@
51
52
  <p class="text-4xl font-semibold mb-4 text:gray-900 dark:text-gray-200 ">{{$t('Passkey')}}</p>
52
53
  <div class="mb-2 max-w-[300px] text:gray-900 dark:text-gray-200">
53
54
  <p class="mb-2">{{customDialogTitle}} </p>
55
+ <p class="text-red-500 font-semibold text-xl text-center mb-12" v-if="sessionsIdsToResolve.length > 1"> You are confirming {{ sessionsIdsToResolve.length }} actions</p>
54
56
  <p>{{$t('Authenticate yourself using the button below')}}</p>
55
57
  </div>
56
58
  <Button @click="usePasskeyButtonClick" :disabled="isFetchingPasskey" :loader="isFetchingPasskey" class="w-full mx-16">
@@ -100,23 +102,29 @@
100
102
 
101
103
  const { alert } = useAdminforth();
102
104
 
103
- let currentSessionId: string | null = null;
105
+ let isAwaiting2FAResult = false;
106
+ const sessionsIdsToResolve = ref<string[]>([]);
104
107
  watch( props, () => {
105
108
  if (props.adminUser) {
106
109
  websocket.unsubscribeByPrefix(`/user2fa/`);
107
110
  websocket.subscribe(`/user2fa/${props.adminUser.pk}`, async (data: {sessionId: string}) => {
108
- currentSessionId = data.sessionId;
111
+ sessionsIdsToResolve.value.push(data.sessionId);
109
112
  let confirmationResult;
113
+ if (isAwaiting2FAResult) {
114
+ return;
115
+ }
110
116
  try {
117
+ isAwaiting2FAResult = true;
111
118
  confirmationResult = await window.adminforthTwoFaModal.get2FaConfirmationResult();
112
119
  } catch (error) {
113
120
  console.error('Error during 2FA confirmation:', error);
114
121
  }
122
+ isAwaiting2FAResult = false;
115
123
  try {
116
124
  const response = await callAdminForthApi({
117
125
  method: "POST",
118
126
  path: "/plugin/passkeys/resolveVerifyAuto",
119
- body: { confirmationResult, sessionId: data.sessionId }
127
+ body: { confirmationResult, sessionId: data.sessionId, sessionsIds: sessionsIdsToResolve.value }
120
128
  });
121
129
  if (!response.ok && response.error === 'No session ID or confirmation result'){
122
130
  alert({message: 'Verification session finished or cancelled.', variant: 'warning'});
@@ -125,15 +133,15 @@
125
133
  } else if (response.ok) {
126
134
  alert({message: 'Verification successful', variant: 'success'});
127
135
  }
136
+ sessionsIdsToResolve.value = [];
128
137
  } catch (error) {
129
138
  console.error('Error resolving automatic 2FA verification:', error);
130
139
  }
131
- currentSessionId = null;
132
140
  });
133
141
  websocket.subscribe(`/user2fa/${props.adminUser.pk}-resolve`, async (data: {sessionId: string}) => {
134
- if (currentSessionId === data.sessionId && rejectFn && modelShow.value) {
142
+ if (sessionsIdsToResolve.value.includes(data.sessionId) && rejectFn && modelShow.value) {
135
143
  onCancel();
136
- currentSessionId = null;
144
+ sessionsIdsToResolve.value = sessionsIdsToResolve.value.filter(id => id !== data.sessionId);
137
145
  }
138
146
  });
139
147
  }
@@ -4,6 +4,7 @@
4
4
  <div v-if="modalMode === 'totp'" class="af-two-factor-modal-totp flex flex-col items-center relative bg-white dark:bg-gray-700 rounded-lg shadow p-6 w-full max-w-md">
5
5
  <div id="mfaCode-label" class="mb-4 text-gray-700 dark:text-gray-100 text-center">
6
6
  <p> {{ customDialogTitle }} </p>
7
+ <p class="text-red-500 font-semibold text-xl" v-if="sessionsIdsToResolve.length > 1"> You are confirming {{ sessionsIdsToResolve.length }} actions</p>
7
8
  <p>{{ $t('Please enter your authenticator code') }}</p>
8
9
  </div>
9
10
 
@@ -51,6 +52,7 @@
51
52
  <p class="text-4xl font-semibold mb-4 text:gray-900 dark:text-gray-200 ">{{$t('Passkey')}}</p>
52
53
  <div class="mb-2 max-w-[300px] text:gray-900 dark:text-gray-200">
53
54
  <p class="mb-2">{{customDialogTitle}} </p>
55
+ <p class="text-red-500 font-semibold text-xl text-center mb-12" v-if="sessionsIdsToResolve.length > 1"> You are confirming {{ sessionsIdsToResolve.length }} actions</p>
54
56
  <p>{{$t('Authenticate yourself using the button below')}}</p>
55
57
  </div>
56
58
  <Button @click="usePasskeyButtonClick" :disabled="isFetchingPasskey" :loader="isFetchingPasskey" class="w-full mx-16">
@@ -100,23 +102,29 @@
100
102
 
101
103
  const { alert } = useAdminforth();
102
104
 
103
- let currentSessionId: string | null = null;
105
+ let isAwaiting2FAResult = false;
106
+ const sessionsIdsToResolve = ref<string[]>([]);
104
107
  watch( props, () => {
105
108
  if (props.adminUser) {
106
109
  websocket.unsubscribeByPrefix(`/user2fa/`);
107
110
  websocket.subscribe(`/user2fa/${props.adminUser.pk}`, async (data: {sessionId: string}) => {
108
- currentSessionId = data.sessionId;
111
+ sessionsIdsToResolve.value.push(data.sessionId);
109
112
  let confirmationResult;
113
+ if (isAwaiting2FAResult) {
114
+ return;
115
+ }
110
116
  try {
117
+ isAwaiting2FAResult = true;
111
118
  confirmationResult = await window.adminforthTwoFaModal.get2FaConfirmationResult();
112
119
  } catch (error) {
113
120
  console.error('Error during 2FA confirmation:', error);
114
121
  }
122
+ isAwaiting2FAResult = false;
115
123
  try {
116
124
  const response = await callAdminForthApi({
117
125
  method: "POST",
118
126
  path: "/plugin/passkeys/resolveVerifyAuto",
119
- body: { confirmationResult, sessionId: data.sessionId }
127
+ body: { confirmationResult, sessionId: data.sessionId, sessionsIds: sessionsIdsToResolve.value }
120
128
  });
121
129
  if (!response.ok && response.error === 'No session ID or confirmation result'){
122
130
  alert({message: 'Verification session finished or cancelled.', variant: 'warning'});
@@ -125,15 +133,15 @@
125
133
  } else if (response.ok) {
126
134
  alert({message: 'Verification successful', variant: 'success'});
127
135
  }
136
+ sessionsIdsToResolve.value = [];
128
137
  } catch (error) {
129
138
  console.error('Error resolving automatic 2FA verification:', error);
130
139
  }
131
- currentSessionId = null;
132
140
  });
133
141
  websocket.subscribe(`/user2fa/${props.adminUser.pk}-resolve`, async (data: {sessionId: string}) => {
134
- if (currentSessionId === data.sessionId && rejectFn && modelShow.value) {
142
+ if (sessionsIdsToResolve.value.includes(data.sessionId) && rejectFn && modelShow.value) {
135
143
  onCancel();
136
- currentSessionId = null;
144
+ sessionsIdsToResolve.value = sessionsIdsToResolve.value.filter(id => id !== data.sessionId);
137
145
  }
138
146
  });
139
147
  }
package/dist/index.js CHANGED
@@ -1020,9 +1020,13 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
1020
1020
  noAuth: false,
1021
1021
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response, cookies, headers }) {
1022
1022
  const sessionId = body === null || body === void 0 ? void 0 : body.sessionId;
1023
+ const sessionsIds = body === null || body === void 0 ? void 0 : body.sessionsIds;
1023
1024
  const confirmationResult = body === null || body === void 0 ? void 0 : body.confirmationResult;
1024
- if (!sessionId || !confirmationResult) {
1025
- this.resolveResponse(sessionId, { ok: false, error: 'No session ID or confirmation result' });
1025
+ const idsToResolve = sessionsIds && Array.isArray(sessionsIds) ? sessionsIds : [sessionId];
1026
+ if (!(sessionId || sessionsIds) || !confirmationResult) {
1027
+ for (const id of idsToResolve) {
1028
+ this.resolveResponse(id, { ok: false, error: 'No session ID or confirmation result' });
1029
+ }
1026
1030
  return { ok: false, error: 'No session ID or confirmation result' };
1027
1031
  }
1028
1032
  const verificationResult = yield this.verify(confirmationResult, {
@@ -1035,10 +1039,14 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
1035
1039
  }
1036
1040
  });
1037
1041
  if (!verificationResult || !('ok' in verificationResult)) {
1038
- this.resolveResponse(sessionId, { ok: false, error: 'Verification failed' });
1042
+ for (const id of idsToResolve) {
1043
+ this.resolveResponse(id, { ok: false, error: 'Verification failed' });
1044
+ }
1039
1045
  return { ok: false, error: 'Verification failed' };
1040
1046
  }
1041
- this.resolveResponse(sessionId, { ok: true, passkeyConfirmed: verificationResult });
1047
+ for (const id of idsToResolve) {
1048
+ this.resolveResponse(id, { ok: true, passkeyConfirmed: verificationResult });
1049
+ }
1042
1050
  return { ok: true };
1043
1051
  })
1044
1052
  });
package/index.ts CHANGED
@@ -1081,9 +1081,13 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
1081
1081
  noAuth: false,
1082
1082
  handler: async ({ body, adminUser, response, cookies, headers }) => {
1083
1083
  const sessionId = body?.sessionId;
1084
+ const sessionsIds = body?.sessionsIds;
1084
1085
  const confirmationResult = body?.confirmationResult;
1085
- if (!sessionId || !confirmationResult) {
1086
- this.resolveResponse(sessionId, { ok: false, error: 'No session ID or confirmation result' });
1086
+ const idsToResolve = sessionsIds && Array.isArray(sessionsIds) ? sessionsIds : [sessionId];
1087
+ if (!(sessionId || sessionsIds) || !confirmationResult) {
1088
+ for (const id of idsToResolve) {
1089
+ this.resolveResponse(id, { ok: false, error: 'No session ID or confirmation result' });
1090
+ }
1087
1091
  return { ok: false, error: 'No session ID or confirmation result' };
1088
1092
  }
1089
1093
  const verificationResult = await this.verify(confirmationResult, {
@@ -1096,10 +1100,14 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
1096
1100
  } as HttpExtra
1097
1101
  });
1098
1102
  if ( !verificationResult || !('ok' in verificationResult) ) {
1099
- this.resolveResponse(sessionId, { ok: false, error: 'Verification failed' });
1103
+ for (const id of idsToResolve) {
1104
+ this.resolveResponse(id, { ok: false, error: 'Verification failed' });
1105
+ }
1100
1106
  return { ok: false, error: 'Verification failed' };
1101
1107
  }
1102
- this.resolveResponse(sessionId, { ok: true, passkeyConfirmed: verificationResult });
1108
+ for (const id of idsToResolve) {
1109
+ this.resolveResponse(id, { ok: true, passkeyConfirmed: verificationResult });
1110
+ }
1103
1111
  return { ok: true };
1104
1112
  }
1105
1113
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/two-factors-auth",
3
- "version": "3.1.2",
3
+ "version": "3.1.3",
4
4
  "description": "AdminForth plugin for two factors authentication",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",