@adminforth/two-factors-auth 3.1.3 → 3.1.4

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 283,319 bytes received 229 bytes 567,096.00 bytes/sec
20
- total size is 282,425 speedup is 1.00
19
+ sent 283,900 bytes received 229 bytes 568,258.00 bytes/sec
20
+ total size is 282,994 speedup is 1.00
@@ -102,29 +102,47 @@
102
102
 
103
103
  const { alert } = useAdminforth();
104
104
 
105
- let isAwaiting2FAResult = false;
105
+ const isAwaiting2FAResult = ref(false);
106
+ let allowAddNewSessions = true;
107
+ const ALLOW_NEW_SESSIONS_PERIOD = 1000;
106
108
  const sessionsIdsToResolve = ref<string[]>([]);
109
+
110
+ watch(isAwaiting2FAResult, (awaiting) => {
111
+ if (awaiting) {
112
+ allowAddNewSessions = true;
113
+ setTimeout(() => {
114
+ if (isAwaiting2FAResult.value) {
115
+ allowAddNewSessions = false;
116
+ }
117
+ }, ALLOW_NEW_SESSIONS_PERIOD);
118
+ }
119
+ });
120
+
107
121
  watch( props, () => {
108
122
  if (props.adminUser) {
109
123
  websocket.unsubscribeByPrefix(`/user2fa/`);
110
124
  websocket.subscribe(`/user2fa/${props.adminUser.pk}`, async (data: {sessionId: string}) => {
125
+ if (!allowAddNewSessions) {
126
+ alert({message: 'Some process or user tries to add new actions to confirm. Action was blocked', variant: 'warning'});
127
+ return;
128
+ }
111
129
  sessionsIdsToResolve.value.push(data.sessionId);
112
130
  let confirmationResult;
113
- if (isAwaiting2FAResult) {
131
+ if (isAwaiting2FAResult.value) {
114
132
  return;
115
133
  }
116
134
  try {
117
- isAwaiting2FAResult = true;
135
+ isAwaiting2FAResult.value = true;
118
136
  confirmationResult = await window.adminforthTwoFaModal.get2FaConfirmationResult();
119
137
  } catch (error) {
120
138
  console.error('Error during 2FA confirmation:', error);
121
139
  }
122
- isAwaiting2FAResult = false;
140
+ isAwaiting2FAResult.value = false;
123
141
  try {
124
142
  const response = await callAdminForthApi({
125
143
  method: "POST",
126
144
  path: "/plugin/passkeys/resolveVerifyAuto",
127
- body: { confirmationResult, sessionId: data.sessionId, sessionsIds: sessionsIdsToResolve.value }
145
+ body: { confirmationResult, sessionsIds: sessionsIdsToResolve.value }
128
146
  });
129
147
  if (!response.ok && response.error === 'No session ID or confirmation result'){
130
148
  alert({message: 'Verification session finished or cancelled.', variant: 'warning'});
@@ -137,6 +155,7 @@
137
155
  } catch (error) {
138
156
  console.error('Error resolving automatic 2FA verification:', error);
139
157
  }
158
+ allowAddNewSessions = true;
140
159
  });
141
160
  websocket.subscribe(`/user2fa/${props.adminUser.pk}-resolve`, async (data: {sessionId: string}) => {
142
161
  if (sessionsIdsToResolve.value.includes(data.sessionId) && rejectFn && modelShow.value) {
@@ -102,29 +102,47 @@
102
102
 
103
103
  const { alert } = useAdminforth();
104
104
 
105
- let isAwaiting2FAResult = false;
105
+ const isAwaiting2FAResult = ref(false);
106
+ let allowAddNewSessions = true;
107
+ const ALLOW_NEW_SESSIONS_PERIOD = 1000;
106
108
  const sessionsIdsToResolve = ref<string[]>([]);
109
+
110
+ watch(isAwaiting2FAResult, (awaiting) => {
111
+ if (awaiting) {
112
+ allowAddNewSessions = true;
113
+ setTimeout(() => {
114
+ if (isAwaiting2FAResult.value) {
115
+ allowAddNewSessions = false;
116
+ }
117
+ }, ALLOW_NEW_SESSIONS_PERIOD);
118
+ }
119
+ });
120
+
107
121
  watch( props, () => {
108
122
  if (props.adminUser) {
109
123
  websocket.unsubscribeByPrefix(`/user2fa/`);
110
124
  websocket.subscribe(`/user2fa/${props.adminUser.pk}`, async (data: {sessionId: string}) => {
125
+ if (!allowAddNewSessions) {
126
+ alert({message: 'Some process or user tries to add new actions to confirm. Action was blocked', variant: 'warning'});
127
+ return;
128
+ }
111
129
  sessionsIdsToResolve.value.push(data.sessionId);
112
130
  let confirmationResult;
113
- if (isAwaiting2FAResult) {
131
+ if (isAwaiting2FAResult.value) {
114
132
  return;
115
133
  }
116
134
  try {
117
- isAwaiting2FAResult = true;
135
+ isAwaiting2FAResult.value = true;
118
136
  confirmationResult = await window.adminforthTwoFaModal.get2FaConfirmationResult();
119
137
  } catch (error) {
120
138
  console.error('Error during 2FA confirmation:', error);
121
139
  }
122
- isAwaiting2FAResult = false;
140
+ isAwaiting2FAResult.value = false;
123
141
  try {
124
142
  const response = await callAdminForthApi({
125
143
  method: "POST",
126
144
  path: "/plugin/passkeys/resolveVerifyAuto",
127
- body: { confirmationResult, sessionId: data.sessionId, sessionsIds: sessionsIdsToResolve.value }
145
+ body: { confirmationResult, sessionsIds: sessionsIdsToResolve.value }
128
146
  });
129
147
  if (!response.ok && response.error === 'No session ID or confirmation result'){
130
148
  alert({message: 'Verification session finished or cancelled.', variant: 'warning'});
@@ -137,6 +155,7 @@
137
155
  } catch (error) {
138
156
  console.error('Error resolving automatic 2FA verification:', error);
139
157
  }
158
+ allowAddNewSessions = true;
140
159
  });
141
160
  websocket.subscribe(`/user2fa/${props.adminUser.pk}-resolve`, async (data: {sessionId: string}) => {
142
161
  if (sessionsIdsToResolve.value.includes(data.sessionId) && rejectFn && modelShow.value) {
package/dist/index.js CHANGED
@@ -149,9 +149,10 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
149
149
  verifyAuto(adminUser) {
150
150
  return __awaiter(this, void 0, void 0, function* () {
151
151
  const sessionId = crypto.randomUUID();
152
- this.adminforth.websocket.publish(`/user2fa/${adminUser.pk}`, { sessionId });
153
- const result = yield this.waitForResponse(sessionId);
154
- this.adminforth.websocket.publish(`/user2fa/${adminUser.pk}-resolve`, { sessionId });
152
+ const jwt = this.adminforth.auth.issueJWT({ sessionId, adminUserPk: adminUser.pk }, 'auto2FA', '5m');
153
+ this.adminforth.websocket.publish(`/user2fa/${adminUser.pk}`, { sessionId: jwt });
154
+ const result = yield this.waitForResponse(jwt);
155
+ this.adminforth.websocket.publish(`/user2fa/${adminUser.pk}-resolve`, { sessionId: jwt });
155
156
  return result;
156
157
  });
157
158
  }
@@ -1019,15 +1020,26 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
1019
1020
  path: `/plugin/passkeys/resolveVerifyAuto`,
1020
1021
  noAuth: false,
1021
1022
  handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response, cookies, headers }) {
1022
- const sessionId = body === null || body === void 0 ? void 0 : body.sessionId;
1023
1023
  const sessionsIds = body === null || body === void 0 ? void 0 : body.sessionsIds;
1024
1024
  const confirmationResult = body === null || body === void 0 ? void 0 : body.confirmationResult;
1025
- const idsToResolve = sessionsIds && Array.isArray(sessionsIds) ? sessionsIds : [sessionId];
1026
- if (!(sessionId || sessionsIds) || !confirmationResult) {
1025
+ const idsToResolve = sessionsIds;
1026
+ const resolveAllIdsAsFailed = (message) => {
1027
1027
  for (const id of idsToResolve) {
1028
- this.resolveResponse(id, { ok: false, error: 'No session ID or confirmation result' });
1028
+ this.resolveResponse(id, { ok: false, error: message });
1029
+ }
1030
+ return { ok: false, error: message };
1031
+ };
1032
+ if (!(sessionsIds) || !confirmationResult) {
1033
+ resolveAllIdsAsFailed('Confirmation window was closed or did not return required data');
1034
+ }
1035
+ for (const id of idsToResolve) {
1036
+ const validationResult = yield this.adminforth.auth.verify(id, 'auto2FA', false);
1037
+ if (!validationResult) {
1038
+ resolveAllIdsAsFailed('Invalid session ID or confirmation result');
1039
+ }
1040
+ if (validationResult.adminUserPk !== adminUser.pk) {
1041
+ resolveAllIdsAsFailed('Session does not belong to the authenticated user');
1029
1042
  }
1030
- return { ok: false, error: 'No session ID or confirmation result' };
1031
1043
  }
1032
1044
  const verificationResult = yield this.verify(confirmationResult, {
1033
1045
  adminUser: adminUser,
@@ -1039,10 +1051,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
1039
1051
  }
1040
1052
  });
1041
1053
  if (!verificationResult || !('ok' in verificationResult)) {
1042
- for (const id of idsToResolve) {
1043
- this.resolveResponse(id, { ok: false, error: 'Verification failed' });
1044
- }
1045
- return { ok: false, error: 'Verification failed' };
1054
+ resolveAllIdsAsFailed('Verification failed');
1046
1055
  }
1047
1056
  for (const id of idsToResolve) {
1048
1057
  this.resolveResponse(id, { ok: true, passkeyConfirmed: verificationResult });
package/index.ts CHANGED
@@ -159,9 +159,10 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
159
159
 
160
160
  public async verifyAuto(adminUser: AdminUser) {
161
161
  const sessionId = crypto.randomUUID();
162
- this.adminforth.websocket.publish(`/user2fa/${adminUser.pk}`, { sessionId });
163
- const result = await this.waitForResponse(sessionId);
164
- this.adminforth.websocket.publish(`/user2fa/${adminUser.pk}-resolve`, { sessionId });
162
+ const jwt = this.adminforth.auth.issueJWT({sessionId, adminUserPk: adminUser.pk}, 'auto2FA', '5m');
163
+ this.adminforth.websocket.publish(`/user2fa/${adminUser.pk}`, { sessionId: jwt });
164
+ const result = await this.waitForResponse(jwt);
165
+ this.adminforth.websocket.publish(`/user2fa/${adminUser.pk}-resolve`, { sessionId: jwt });
165
166
  return result;
166
167
  }
167
168
 
@@ -1080,16 +1081,31 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
1080
1081
  path: `/plugin/passkeys/resolveVerifyAuto`,
1081
1082
  noAuth: false,
1082
1083
  handler: async ({ body, adminUser, response, cookies, headers }) => {
1083
- const sessionId = body?.sessionId;
1084
1084
  const sessionsIds = body?.sessionsIds;
1085
1085
  const confirmationResult = body?.confirmationResult;
1086
- const idsToResolve = sessionsIds && Array.isArray(sessionsIds) ? sessionsIds : [sessionId];
1087
- if (!(sessionId || sessionsIds) || !confirmationResult) {
1086
+ const idsToResolve = sessionsIds;
1087
+
1088
+ const resolveAllIdsAsFailed = (message) => {
1088
1089
  for (const id of idsToResolve) {
1089
- this.resolveResponse(id, { ok: false, error: 'No session ID or confirmation result' });
1090
+ this.resolveResponse(id, { ok: false, error: message });
1090
1091
  }
1091
- return { ok: false, error: 'No session ID or confirmation result' };
1092
+ return { ok: false, error: message };
1093
+ }
1094
+
1095
+ if (!(sessionsIds) || !confirmationResult) {
1096
+ resolveAllIdsAsFailed('Confirmation window was closed or did not return required data');
1092
1097
  }
1098
+
1099
+ for (const id of idsToResolve) {
1100
+ const validationResult = await this.adminforth.auth.verify(id, 'auto2FA', false);
1101
+ if (!validationResult) {
1102
+ resolveAllIdsAsFailed('Invalid session ID or confirmation result');
1103
+ }
1104
+ if (validationResult.adminUserPk !== adminUser.pk) {
1105
+ resolveAllIdsAsFailed('Session does not belong to the authenticated user');
1106
+ }
1107
+ }
1108
+
1093
1109
  const verificationResult = await this.verify(confirmationResult, {
1094
1110
  adminUser: adminUser,
1095
1111
  userPk: adminUser.pk,
@@ -1100,11 +1116,9 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
1100
1116
  } as HttpExtra
1101
1117
  });
1102
1118
  if ( !verificationResult || !('ok' in verificationResult) ) {
1103
- for (const id of idsToResolve) {
1104
- this.resolveResponse(id, { ok: false, error: 'Verification failed' });
1105
- }
1106
- return { ok: false, error: 'Verification failed' };
1119
+ resolveAllIdsAsFailed('Verification failed');
1107
1120
  }
1121
+
1108
1122
  for (const id of idsToResolve) {
1109
1123
  this.resolveResponse(id, { ok: true, passkeyConfirmed: verificationResult });
1110
1124
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/two-factors-auth",
3
- "version": "3.1.3",
3
+ "version": "3.1.4",
4
4
  "description": "AdminForth plugin for two factors authentication",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",