@lvce-editor/auth-worker 1.0.0 → 1.2.0
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/dist/authWorkerMain.js +290 -1
- package/package.json +1 -1
package/dist/authWorkerMain.js
CHANGED
|
@@ -956,8 +956,15 @@ const create = rpcId => {
|
|
|
956
956
|
};
|
|
957
957
|
};
|
|
958
958
|
|
|
959
|
+
const OpenerWorker = 4561;
|
|
959
960
|
const RendererWorker = 1;
|
|
960
961
|
|
|
962
|
+
const {
|
|
963
|
+
invoke} = create(OpenerWorker);
|
|
964
|
+
const openUrl = async (url, platform) => {
|
|
965
|
+
return invoke('Open.openUrl', url, platform);
|
|
966
|
+
};
|
|
967
|
+
|
|
961
968
|
const {
|
|
962
969
|
set
|
|
963
970
|
} = create(RendererWorker);
|
|
@@ -973,8 +980,290 @@ const initialize = async platform => {
|
|
|
973
980
|
// TODO
|
|
974
981
|
};
|
|
975
982
|
|
|
983
|
+
const trailingSlashesRegex = /\/+$/;
|
|
984
|
+
const trimTrailingSlashes = value => {
|
|
985
|
+
return value.replace(trailingSlashesRegex, '');
|
|
986
|
+
};
|
|
987
|
+
|
|
988
|
+
const getBackendAuthUrl = (backendUrl, path) => {
|
|
989
|
+
return `${trimTrailingSlashes(backendUrl)}${path}`;
|
|
990
|
+
};
|
|
991
|
+
|
|
992
|
+
const getBackendLoginUrl = backendUrl => {
|
|
993
|
+
return getBackendAuthUrl(backendUrl, '/auth/login');
|
|
994
|
+
};
|
|
995
|
+
|
|
996
|
+
const getLoggedOutBackendAuthState = (authErrorMessage = '') => {
|
|
997
|
+
return {
|
|
998
|
+
authAccessToken: '',
|
|
999
|
+
authErrorMessage,
|
|
1000
|
+
userName: '',
|
|
1001
|
+
userState: 'loggedOut',
|
|
1002
|
+
userSubscriptionPlan: '',
|
|
1003
|
+
userUsedTokens: 0
|
|
1004
|
+
};
|
|
1005
|
+
};
|
|
1006
|
+
|
|
1007
|
+
const getBackendLogoutUrl = backendUrl => {
|
|
1008
|
+
return getBackendAuthUrl(backendUrl, '/auth/logout');
|
|
1009
|
+
};
|
|
1010
|
+
|
|
1011
|
+
const logoutFromBackend = async backendUrl => {
|
|
1012
|
+
if (!backendUrl) {
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
try {
|
|
1016
|
+
await fetch(getBackendLogoutUrl(backendUrl), {
|
|
1017
|
+
credentials: 'include',
|
|
1018
|
+
headers: {
|
|
1019
|
+
Accept: 'application/json'
|
|
1020
|
+
},
|
|
1021
|
+
method: 'POST'
|
|
1022
|
+
});
|
|
1023
|
+
} catch {
|
|
1024
|
+
// Ignore logout failures and still clear local auth state.
|
|
1025
|
+
}
|
|
1026
|
+
};
|
|
1027
|
+
|
|
1028
|
+
const getBackendRefreshUrl = backendUrl => {
|
|
1029
|
+
return getBackendAuthUrl(backendUrl, '/auth/refresh');
|
|
1030
|
+
};
|
|
1031
|
+
|
|
1032
|
+
const delay = async ms => {
|
|
1033
|
+
await new Promise(resolve => setTimeout(resolve, ms));
|
|
1034
|
+
};
|
|
1035
|
+
|
|
1036
|
+
let nextLoginResponse;
|
|
1037
|
+
let nextRefreshResponse;
|
|
1038
|
+
const setNextLoginResponse = response => {
|
|
1039
|
+
nextLoginResponse = response;
|
|
1040
|
+
};
|
|
1041
|
+
const setNextRefreshResponse = response => {
|
|
1042
|
+
nextRefreshResponse = response;
|
|
1043
|
+
};
|
|
1044
|
+
const clear = () => {
|
|
1045
|
+
nextLoginResponse = undefined;
|
|
1046
|
+
nextRefreshResponse = undefined;
|
|
1047
|
+
};
|
|
1048
|
+
const hasPendingMockLoginResponse = () => {
|
|
1049
|
+
return !!nextLoginResponse;
|
|
1050
|
+
};
|
|
1051
|
+
const hasPendingMockRefreshResponse = () => {
|
|
1052
|
+
return !!nextRefreshResponse;
|
|
1053
|
+
};
|
|
1054
|
+
const consumeNextLoginResponse = async () => {
|
|
1055
|
+
if (!nextLoginResponse) {
|
|
1056
|
+
return undefined;
|
|
1057
|
+
}
|
|
1058
|
+
const response = nextLoginResponse;
|
|
1059
|
+
nextLoginResponse = undefined;
|
|
1060
|
+
if (response.delay > 0) {
|
|
1061
|
+
await delay(response.delay);
|
|
1062
|
+
}
|
|
1063
|
+
if (response.type === 'error') {
|
|
1064
|
+
throw new Error(response.message);
|
|
1065
|
+
}
|
|
1066
|
+
return response.response;
|
|
1067
|
+
};
|
|
1068
|
+
const consumeNextRefreshResponse = async () => {
|
|
1069
|
+
if (!nextRefreshResponse) {
|
|
1070
|
+
return undefined;
|
|
1071
|
+
}
|
|
1072
|
+
const response = nextRefreshResponse;
|
|
1073
|
+
nextRefreshResponse = undefined;
|
|
1074
|
+
if (response.delay > 0) {
|
|
1075
|
+
await new Promise(resolve => setTimeout(resolve, response.delay));
|
|
1076
|
+
}
|
|
1077
|
+
if (response.type === 'error') {
|
|
1078
|
+
throw new Error(response.message);
|
|
1079
|
+
}
|
|
1080
|
+
return response.response;
|
|
1081
|
+
};
|
|
1082
|
+
|
|
1083
|
+
const isObject = value => {
|
|
1084
|
+
return !!value && typeof value === 'object';
|
|
1085
|
+
};
|
|
1086
|
+
|
|
1087
|
+
const isBackendAuthResponse = value => {
|
|
1088
|
+
return isObject(value);
|
|
1089
|
+
};
|
|
1090
|
+
|
|
1091
|
+
const getNumber = (value, fallback = 0) => {
|
|
1092
|
+
return typeof value === 'number' ? value : fallback;
|
|
1093
|
+
};
|
|
1094
|
+
|
|
1095
|
+
const getString = (value, fallback = '') => {
|
|
1096
|
+
return typeof value === 'string' ? value : fallback;
|
|
1097
|
+
};
|
|
1098
|
+
|
|
1099
|
+
const toBackendAuthState = value => {
|
|
1100
|
+
return {
|
|
1101
|
+
authAccessToken: getString(value.accessToken),
|
|
1102
|
+
authErrorMessage: getString(value.error),
|
|
1103
|
+
userName: getString(value.userName),
|
|
1104
|
+
userState: value.accessToken ? 'loggedIn' : 'loggedOut',
|
|
1105
|
+
userSubscriptionPlan: getString(value.subscriptionPlan),
|
|
1106
|
+
userUsedTokens: getNumber(value.usedTokens)
|
|
1107
|
+
};
|
|
1108
|
+
};
|
|
1109
|
+
|
|
1110
|
+
const parseBackendAuthResponse = value => {
|
|
1111
|
+
if (!isBackendAuthResponse(value)) {
|
|
1112
|
+
return getLoggedOutBackendAuthState('Backend returned an invalid authentication response.');
|
|
1113
|
+
}
|
|
1114
|
+
return toBackendAuthState(value);
|
|
1115
|
+
};
|
|
1116
|
+
|
|
1117
|
+
const syncBackendAuth = async backendUrl => {
|
|
1118
|
+
if (!backendUrl) {
|
|
1119
|
+
return getLoggedOutBackendAuthState('Backend URL is missing.');
|
|
1120
|
+
}
|
|
1121
|
+
try {
|
|
1122
|
+
if (hasPendingMockRefreshResponse()) {
|
|
1123
|
+
const mockResponse = await consumeNextRefreshResponse();
|
|
1124
|
+
return parseBackendAuthResponse(mockResponse);
|
|
1125
|
+
}
|
|
1126
|
+
const response = await fetch(getBackendRefreshUrl(backendUrl), {
|
|
1127
|
+
credentials: 'include',
|
|
1128
|
+
headers: {
|
|
1129
|
+
Accept: 'application/json'
|
|
1130
|
+
},
|
|
1131
|
+
method: 'POST'
|
|
1132
|
+
});
|
|
1133
|
+
if (response.status === 401 || response.status === 403) {
|
|
1134
|
+
return getLoggedOutBackendAuthState();
|
|
1135
|
+
}
|
|
1136
|
+
let payload = undefined;
|
|
1137
|
+
try {
|
|
1138
|
+
payload = await response.json();
|
|
1139
|
+
} catch {
|
|
1140
|
+
payload = undefined;
|
|
1141
|
+
}
|
|
1142
|
+
if (!response.ok) {
|
|
1143
|
+
const parsed = parseBackendAuthResponse(payload);
|
|
1144
|
+
return getLoggedOutBackendAuthState(parsed.authErrorMessage || 'Backend authentication failed.');
|
|
1145
|
+
}
|
|
1146
|
+
const parsed = parseBackendAuthResponse(payload);
|
|
1147
|
+
if (parsed.authErrorMessage) {
|
|
1148
|
+
return getLoggedOutBackendAuthState(parsed.authErrorMessage);
|
|
1149
|
+
}
|
|
1150
|
+
if (!parsed.authAccessToken) {
|
|
1151
|
+
return getLoggedOutBackendAuthState();
|
|
1152
|
+
}
|
|
1153
|
+
return parsed;
|
|
1154
|
+
} catch (error) {
|
|
1155
|
+
const authErrorMessage = error instanceof Error && error.message ? error.message : 'Backend authentication failed.';
|
|
1156
|
+
return getLoggedOutBackendAuthState(authErrorMessage);
|
|
1157
|
+
}
|
|
1158
|
+
};
|
|
1159
|
+
|
|
1160
|
+
const waitForBackendLogin = async (backendUrl, timeoutMs = 30_000, pollIntervalMs = 1000) => {
|
|
1161
|
+
const deadline = Date.now() + timeoutMs;
|
|
1162
|
+
let lastErrorMessage = '';
|
|
1163
|
+
while (Date.now() < deadline) {
|
|
1164
|
+
const authState = await syncBackendAuth(backendUrl);
|
|
1165
|
+
if (authState.userState === 'loggedIn') {
|
|
1166
|
+
return authState;
|
|
1167
|
+
}
|
|
1168
|
+
if (authState.authErrorMessage) {
|
|
1169
|
+
lastErrorMessage = authState.authErrorMessage;
|
|
1170
|
+
}
|
|
1171
|
+
await delay(pollIntervalMs);
|
|
1172
|
+
}
|
|
1173
|
+
return getLoggedOutBackendAuthState(lastErrorMessage);
|
|
1174
|
+
};
|
|
1175
|
+
|
|
1176
|
+
const getLoggedInState = (state, response) => {
|
|
1177
|
+
const accessToken = typeof response.accessToken === 'string' ? response.accessToken : '';
|
|
1178
|
+
return {
|
|
1179
|
+
...state,
|
|
1180
|
+
authAccessToken: accessToken,
|
|
1181
|
+
authErrorMessage: '',
|
|
1182
|
+
userName: typeof response.userName === 'string' ? response.userName : state.userName,
|
|
1183
|
+
userState: accessToken ? 'loggedIn' : 'loggedOut',
|
|
1184
|
+
userSubscriptionPlan: typeof response.subscriptionPlan === 'string' ? response.subscriptionPlan : state.userSubscriptionPlan,
|
|
1185
|
+
userUsedTokens: typeof response.usedTokens === 'number' ? response.usedTokens : state.userUsedTokens
|
|
1186
|
+
};
|
|
1187
|
+
};
|
|
1188
|
+
|
|
1189
|
+
const isLoginResponse = value => {
|
|
1190
|
+
if (!value || typeof value !== 'object') {
|
|
1191
|
+
return false;
|
|
1192
|
+
}
|
|
1193
|
+
return true;
|
|
1194
|
+
};
|
|
1195
|
+
|
|
1196
|
+
const handleClickLogin = async state => {
|
|
1197
|
+
if (!state.backendUrl) {
|
|
1198
|
+
return {
|
|
1199
|
+
...state,
|
|
1200
|
+
authErrorMessage: 'Backend URL is missing.',
|
|
1201
|
+
userState: 'loggedOut'
|
|
1202
|
+
};
|
|
1203
|
+
}
|
|
1204
|
+
const signingInState = {
|
|
1205
|
+
...state,
|
|
1206
|
+
authErrorMessage: '',
|
|
1207
|
+
userState: 'loggingIn'
|
|
1208
|
+
};
|
|
1209
|
+
try {
|
|
1210
|
+
if (hasPendingMockLoginResponse()) {
|
|
1211
|
+
const response = await consumeNextLoginResponse();
|
|
1212
|
+
if (!isLoginResponse(response)) {
|
|
1213
|
+
return {
|
|
1214
|
+
...signingInState,
|
|
1215
|
+
authErrorMessage: 'Backend returned an invalid login response.',
|
|
1216
|
+
userState: 'loggedOut'
|
|
1217
|
+
};
|
|
1218
|
+
}
|
|
1219
|
+
if (typeof response.error === 'string' && response.error) {
|
|
1220
|
+
return {
|
|
1221
|
+
...signingInState,
|
|
1222
|
+
authErrorMessage: response.error,
|
|
1223
|
+
userState: 'loggedOut'
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
return getLoggedInState(signingInState, response);
|
|
1227
|
+
}
|
|
1228
|
+
await openUrl(getBackendLoginUrl(state.backendUrl), state.platform);
|
|
1229
|
+
const authState = await waitForBackendLogin(state.backendUrl);
|
|
1230
|
+
return {
|
|
1231
|
+
...signingInState,
|
|
1232
|
+
...authState
|
|
1233
|
+
};
|
|
1234
|
+
} catch (error) {
|
|
1235
|
+
const errorMessage = error instanceof Error && error.message ? error.message : 'Backend authentication failed.';
|
|
1236
|
+
return {
|
|
1237
|
+
...signingInState,
|
|
1238
|
+
...getLoggedOutBackendAuthState(errorMessage)
|
|
1239
|
+
};
|
|
1240
|
+
}
|
|
1241
|
+
};
|
|
1242
|
+
|
|
1243
|
+
const logout = async state => {
|
|
1244
|
+
const loggingOutState = {
|
|
1245
|
+
authErrorMessage: '',
|
|
1246
|
+
userState: 'loggingOut'
|
|
1247
|
+
};
|
|
1248
|
+
await logoutFromBackend(state.backendUrl);
|
|
1249
|
+
return {
|
|
1250
|
+
...loggingOutState,
|
|
1251
|
+
...getLoggedOutBackendAuthState()
|
|
1252
|
+
};
|
|
1253
|
+
};
|
|
1254
|
+
|
|
976
1255
|
const commandMap = {
|
|
977
|
-
'
|
|
1256
|
+
'Auth.clearMocks': clear,
|
|
1257
|
+
'Auth.consumeNextLoginResponse': consumeNextLoginResponse,
|
|
1258
|
+
'Auth.consumeNextRefreshResponse': consumeNextRefreshResponse,
|
|
1259
|
+
'Auth.hasPendingMockLoginResponse': hasPendingMockLoginResponse,
|
|
1260
|
+
'Auth.hasPendingMockRefreshResponse': hasPendingMockRefreshResponse,
|
|
1261
|
+
'Auth.initialize': initialize,
|
|
1262
|
+
'Auth.login': handleClickLogin,
|
|
1263
|
+
'Auth.logout': logout,
|
|
1264
|
+
'Auth.setNextLoginResponse': setNextLoginResponse,
|
|
1265
|
+
'Auth.setNextRefreshResponse': setNextRefreshResponse,
|
|
1266
|
+
'Auth.syncBackendAuth': syncBackendAuth,
|
|
978
1267
|
'HandleMessagePort.handleMessagePort': handleMessagePort
|
|
979
1268
|
};
|
|
980
1269
|
|