dexie-cloud-addon 4.2.5 → 4.3.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/TODO-SOCIALAUTH.md +545 -0
- package/dist/modern/DexieCloudAPI.d.ts +4 -0
- package/dist/modern/DexieCloudOptions.d.ts +20 -0
- package/dist/modern/authentication/exchangeOAuthCode.d.ts +23 -0
- package/dist/modern/authentication/fetchAuthProviders.d.ts +14 -0
- package/dist/modern/authentication/handleOAuthCallback.d.ts +57 -0
- package/dist/modern/authentication/interactWithUser.d.ts +19 -0
- package/dist/modern/authentication/oauthLogin.d.ts +37 -0
- package/dist/modern/default-ui/AuthProviderButton.d.ts +21 -0
- package/dist/modern/default-ui/LoginDialog.d.ts +5 -2
- package/dist/modern/default-ui/ProviderSelectionDialog.d.ts +7 -0
- package/dist/modern/dexie-cloud-addon.js +577 -5
- package/dist/modern/dexie-cloud-addon.js.map +1 -1
- package/dist/modern/dexie-cloud-addon.min.js +1 -1
- package/dist/modern/dexie-cloud-addon.min.js.gz +0 -0
- package/dist/modern/dexie-cloud-addon.min.js.map +1 -1
- package/dist/modern/errors/OAuthError.d.ts +10 -0
- package/dist/modern/service-worker.js +577 -5
- package/dist/modern/service-worker.js.map +1 -1
- package/dist/modern/service-worker.min.js +1 -1
- package/dist/modern/service-worker.min.js.map +1 -1
- package/dist/modern/types/DXCUserInteraction.d.ts +24 -1
- package/dist/umd/DISABLE_SERVICEWORKER_STRATEGY.d.ts +1 -0
- package/dist/umd/DXCWebSocketStatus.d.ts +1 -0
- package/dist/umd/DexieCloudAPI.d.ts +75 -0
- package/dist/umd/DexieCloudOptions.d.ts +27 -0
- package/dist/umd/DexieCloudSyncOptions.d.ts +4 -0
- package/dist/umd/DexieCloudTable.d.ts +18 -0
- package/dist/umd/InvalidLicenseError.d.ts +5 -0
- package/dist/umd/Invite.d.ts +8 -0
- package/dist/umd/PermissionChecker.d.ts +15 -0
- package/dist/umd/TSON.d.ts +17 -0
- package/dist/umd/WSObservable.d.ts +72 -0
- package/dist/umd/associate.d.ts +1 -0
- package/dist/umd/authentication/AuthPersistedContext.d.ts +9 -0
- package/dist/umd/authentication/TokenErrorResponseError.d.ts +10 -0
- package/dist/umd/authentication/TokenExpiredError.d.ts +3 -0
- package/dist/umd/authentication/UNAUTHORIZED_USER.d.ts +2 -0
- package/dist/umd/authentication/authenticate.d.ts +13 -0
- package/dist/umd/authentication/currentUserObservable.d.ts +1 -0
- package/dist/umd/authentication/interactWithUser.d.ts +21 -0
- package/dist/umd/authentication/login.d.ts +3 -0
- package/dist/umd/authentication/logout.d.ts +5 -0
- package/dist/umd/authentication/otpFetchTokenCallback.d.ts +3 -0
- package/dist/umd/authentication/setCurrentUser.d.ts +14 -0
- package/dist/umd/authentication/waitUntil.d.ts +3 -0
- package/dist/umd/computeSyncState.d.ts +4 -0
- package/dist/umd/createSharedValueObservable.d.ts +3 -0
- package/dist/umd/currentUserEmitter.d.ts +3 -0
- package/dist/umd/db/DexieCloudDB.d.ts +61 -0
- package/dist/umd/db/entities/BaseRevisionMapEntry.d.ts +5 -0
- package/dist/umd/db/entities/EntityCommon.d.ts +5 -0
- package/dist/umd/db/entities/GuardedJob.d.ts +5 -0
- package/dist/umd/db/entities/Member.d.ts +19 -0
- package/dist/umd/db/entities/PersistedSyncState.d.ts +22 -0
- package/dist/umd/db/entities/Realm.d.ts +14 -0
- package/dist/umd/db/entities/Role.d.ts +11 -0
- package/dist/umd/db/entities/UserLogin.d.ts +23 -0
- package/dist/umd/default-ui/Dialog.d.ts +5 -0
- package/dist/umd/default-ui/LoginDialog.d.ts +3 -0
- package/dist/umd/default-ui/Styles.d.ts +3 -0
- package/dist/umd/default-ui/index.d.ts +24 -0
- package/dist/umd/define-ydoc-trigger.d.ts +3 -0
- package/dist/umd/dexie-cloud-addon.d.ts +3 -0
- package/dist/umd/dexie-cloud-addon.js +578 -6
- package/dist/umd/dexie-cloud-addon.js.gz +0 -0
- package/dist/umd/dexie-cloud-addon.js.map +1 -1
- package/dist/umd/dexie-cloud-addon.min.js +1 -1
- package/dist/umd/dexie-cloud-addon.min.js.gz +0 -0
- package/dist/umd/dexie-cloud-addon.min.js.map +1 -1
- package/dist/umd/dexie-cloud-client.d.ts +23 -0
- package/dist/umd/errors/HttpError.d.ts +5 -0
- package/dist/umd/extend-dexie-interface.d.ts +23 -0
- package/dist/umd/getGlobalRolesObservable.d.ts +5 -0
- package/dist/umd/getInternalAccessControlObservable.d.ts +12 -0
- package/dist/umd/getInvitesObservable.d.ts +23 -0
- package/dist/umd/getPermissionsLookupObservable.d.ts +16 -0
- package/dist/umd/getTiedRealmId.d.ts +2 -0
- package/dist/umd/helpers/BroadcastedAndLocalEvent.d.ts +8 -0
- package/dist/umd/helpers/CancelToken.d.ts +4 -0
- package/dist/umd/helpers/IS_SERVICE_WORKER.d.ts +1 -0
- package/dist/umd/helpers/SWBroadcastChannel.d.ts +12 -0
- package/dist/umd/helpers/allSettled.d.ts +1 -0
- package/dist/umd/helpers/bulkUpdate.d.ts +4 -0
- package/dist/umd/helpers/computeRealmSetHash.d.ts +2 -0
- package/dist/umd/helpers/date-constants.d.ts +5 -0
- package/dist/umd/helpers/flatten.d.ts +1 -0
- package/dist/umd/helpers/getMutationTable.d.ts +1 -0
- package/dist/umd/helpers/getSyncableTables.d.ts +4 -0
- package/dist/umd/helpers/getTableFromMutationTable.d.ts +1 -0
- package/dist/umd/helpers/makeArray.d.ts +1 -0
- package/dist/umd/helpers/randomString.d.ts +1 -0
- package/dist/umd/helpers/resolveText.d.ts +16 -0
- package/dist/umd/helpers/throwVersionIncrementNeeded.d.ts +1 -0
- package/dist/umd/helpers/visibilityState.d.ts +1 -0
- package/dist/umd/isEagerSyncDisabled.d.ts +2 -0
- package/dist/umd/isFirefox.d.ts +1 -0
- package/dist/umd/isSafari.d.ts +2 -0
- package/dist/umd/mapValueObservable.d.ts +5 -0
- package/dist/umd/mergePermissions.d.ts +2 -0
- package/dist/umd/middleware-helpers/guardedTable.d.ts +11 -0
- package/dist/umd/middleware-helpers/idGenerationHelpers.d.ts +18 -0
- package/dist/umd/middlewares/createIdGenerationMiddleware.d.ts +3 -0
- package/dist/umd/middlewares/createImplicitPropSetterMiddleware.d.ts +3 -0
- package/dist/umd/middlewares/createMutationTrackingMiddleware.d.ts +17 -0
- package/dist/umd/middlewares/outstandingTransaction.d.ts +4 -0
- package/dist/umd/overrideParseStoresSpec.d.ts +4 -0
- package/dist/umd/performInitialSync.d.ts +4 -0
- package/dist/umd/permissions.d.ts +9 -0
- package/dist/umd/prodLog.d.ts +9 -0
- package/dist/umd/service-worker.d.ts +1 -0
- package/dist/umd/service-worker.js +578 -6
- package/dist/umd/service-worker.js.map +1 -1
- package/dist/umd/service-worker.min.js +1 -1
- package/dist/umd/service-worker.min.js.map +1 -1
- package/dist/umd/sync/DEXIE_CLOUD_SYNCER_ID.d.ts +1 -0
- package/dist/umd/sync/LocalSyncWorker.d.ts +7 -0
- package/dist/umd/sync/SyncRequiredError.d.ts +3 -0
- package/dist/umd/sync/applyServerChanges.d.ts +3 -0
- package/dist/umd/sync/connectWebSocket.d.ts +2 -0
- package/dist/umd/sync/encodeIdsForServer.d.ts +4 -0
- package/dist/umd/sync/extractRealm.d.ts +2 -0
- package/dist/umd/sync/getLatestRevisionsPerTable.d.ts +6 -0
- package/dist/umd/sync/getTablesToSyncify.d.ts +3 -0
- package/dist/umd/sync/isOnline.d.ts +1 -0
- package/dist/umd/sync/isSyncNeeded.d.ts +2 -0
- package/dist/umd/sync/listClientChanges.d.ts +9 -0
- package/dist/umd/sync/listSyncifiedChanges.d.ts +5 -0
- package/dist/umd/sync/messageConsumerIsReady.d.ts +2 -0
- package/dist/umd/sync/messagesFromServerQueue.d.ts +8 -0
- package/dist/umd/sync/modifyLocalObjectsWithNewUserId.d.ts +4 -0
- package/dist/umd/sync/myId.d.ts +1 -0
- package/dist/umd/sync/numUnsyncedMutations.d.ts +2 -0
- package/dist/umd/sync/old_startSyncingClientChanges.d.ts +39 -0
- package/dist/umd/sync/performGuardedJob.d.ts +2 -0
- package/dist/umd/sync/ratelimit.d.ts +3 -0
- package/dist/umd/sync/registerSyncEvent.d.ts +3 -0
- package/dist/umd/sync/sync.d.ts +15 -0
- package/dist/umd/sync/syncIfPossible.d.ts +5 -0
- package/dist/umd/sync/syncWithServer.d.ts +6 -0
- package/dist/umd/sync/triggerSync.d.ts +2 -0
- package/dist/umd/sync/updateBaseRevs.d.ts +5 -0
- package/dist/umd/types/DXCAlert.d.ts +25 -0
- package/dist/umd/types/DXCInputField.d.ts +11 -0
- package/dist/umd/types/DXCUserInteraction.d.ts +93 -0
- package/dist/umd/types/NewIdOptions.d.ts +3 -0
- package/dist/umd/types/SWMessageEvent.d.ts +3 -0
- package/dist/umd/types/SWSyncEvent.d.ts +4 -0
- package/dist/umd/types/SyncState.d.ts +9 -0
- package/dist/umd/types/TXExpandos.d.ts +11 -0
- package/dist/umd/updateSchemaFromOptions.d.ts +3 -0
- package/dist/umd/userIsActive.d.ts +7 -0
- package/dist/umd/verifyConfig.d.ts +2 -0
- package/dist/umd/verifySchema.d.ts +2 -0
- package/dist/umd/yjs/YDexieCloudSyncState.d.ts +3 -0
- package/dist/umd/yjs/YTable.d.ts +3 -0
- package/dist/umd/yjs/applyYMessages.d.ts +9 -0
- package/dist/umd/yjs/awareness.d.ts +3 -0
- package/dist/umd/yjs/createYClientUpdateObservable.d.ts +4 -0
- package/dist/umd/yjs/createYHandler.d.ts +2 -0
- package/dist/umd/yjs/downloadYDocsFromServer.d.ts +3 -0
- package/dist/umd/yjs/getUpdatesTable.d.ts +3 -0
- package/dist/umd/yjs/listUpdatesSince.d.ts +3 -0
- package/dist/umd/yjs/listYClientMessagesAndStateVector.d.ts +26 -0
- package/dist/umd/yjs/reopenDocSignal.d.ts +10 -0
- package/dist/umd/yjs/updateYSyncStates.d.ts +6 -0
- package/oauth_flow.md +299 -0
- package/package.json +3 -3
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* ==========================================================================
|
|
10
10
|
*
|
|
11
|
-
* Version 4.
|
|
11
|
+
* Version 4.3.0, Tue Jan 20 2026
|
|
12
12
|
*
|
|
13
13
|
* https://dexie.org
|
|
14
14
|
*
|
|
@@ -106,6 +106,15 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
106
106
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
107
107
|
};
|
|
108
108
|
|
|
109
|
+
/** Type guard to check if a message is an OAuthResultMessage */
|
|
110
|
+
function isOAuthResultMessage(msg) {
|
|
111
|
+
return (typeof msg === 'object' &&
|
|
112
|
+
msg !== null &&
|
|
113
|
+
msg.type === 'dexie:oauthResult' &&
|
|
114
|
+
typeof msg.provider === 'string' &&
|
|
115
|
+
typeof msg.state === 'string');
|
|
116
|
+
}
|
|
117
|
+
|
|
109
118
|
function assert(b) {
|
|
110
119
|
if (!b)
|
|
111
120
|
throw new Error('Assertion Failed');
|
|
@@ -895,6 +904,43 @@ function confirmLogout(userInteraction, currentUserId, numUnsyncedChanges) {
|
|
|
895
904
|
.catch(() => false);
|
|
896
905
|
});
|
|
897
906
|
}
|
|
907
|
+
/**
|
|
908
|
+
* Prompts the user to select an authentication method (OAuth provider or OTP).
|
|
909
|
+
*
|
|
910
|
+
* @param userInteraction - The user interaction BehaviorSubject
|
|
911
|
+
* @param providers - Available OAuth providers
|
|
912
|
+
* @param otpEnabled - Whether OTP is available
|
|
913
|
+
* @param title - Dialog title
|
|
914
|
+
* @param alerts - Optional alerts to display
|
|
915
|
+
* @returns Promise resolving to the user's selection
|
|
916
|
+
*/
|
|
917
|
+
function promptForProvider(userInteraction, providers, otpEnabled, title = 'Choose login method', alerts = []) {
|
|
918
|
+
return new Promise((resolve, reject) => {
|
|
919
|
+
const interactionProps = {
|
|
920
|
+
type: 'provider-selection',
|
|
921
|
+
title,
|
|
922
|
+
alerts,
|
|
923
|
+
providers,
|
|
924
|
+
otpEnabled,
|
|
925
|
+
fields: {},
|
|
926
|
+
submitLabel: undefined,
|
|
927
|
+
cancelLabel: 'Cancel',
|
|
928
|
+
onSelectProvider: (providerName) => {
|
|
929
|
+
userInteraction.next(undefined);
|
|
930
|
+
resolve({ type: 'provider', provider: providerName });
|
|
931
|
+
},
|
|
932
|
+
onSelectOtp: () => {
|
|
933
|
+
userInteraction.next(undefined);
|
|
934
|
+
resolve({ type: 'otp' });
|
|
935
|
+
},
|
|
936
|
+
onCancel: () => {
|
|
937
|
+
userInteraction.next(undefined);
|
|
938
|
+
reject(new Dexie.AbortError('User cancelled'));
|
|
939
|
+
},
|
|
940
|
+
};
|
|
941
|
+
userInteraction.next(interactionProps);
|
|
942
|
+
});
|
|
943
|
+
}
|
|
898
944
|
|
|
899
945
|
function loadAccessToken(db) {
|
|
900
946
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -1218,15 +1264,313 @@ class HttpError extends Error {
|
|
|
1218
1264
|
}
|
|
1219
1265
|
}
|
|
1220
1266
|
|
|
1267
|
+
/** User-friendly messages for OAuth error codes */
|
|
1268
|
+
const ERROR_MESSAGES = {
|
|
1269
|
+
popup_blocked: 'The login popup was blocked by your browser. Please allow popups for this site and try again.',
|
|
1270
|
+
popup_closed: 'The login popup was closed before completing authentication.',
|
|
1271
|
+
access_denied: 'Access was denied by the authentication provider.',
|
|
1272
|
+
invalid_state: 'The authentication response could not be verified. Please try again.',
|
|
1273
|
+
email_not_verified: 'Your email address must be verified before you can log in.',
|
|
1274
|
+
expired_code: 'The authentication code has expired. Please try again.',
|
|
1275
|
+
provider_error: 'An error occurred with the authentication provider.',
|
|
1276
|
+
network_error: 'A network error occurred during authentication. Please check your connection and try again.',
|
|
1277
|
+
};
|
|
1278
|
+
/** Error class for OAuth-specific errors */
|
|
1279
|
+
class OAuthError extends Error {
|
|
1280
|
+
constructor(code, provider, customMessage) {
|
|
1281
|
+
super(customMessage || ERROR_MESSAGES[code]);
|
|
1282
|
+
this.name = 'OAuthError';
|
|
1283
|
+
this.code = code;
|
|
1284
|
+
this.provider = provider;
|
|
1285
|
+
}
|
|
1286
|
+
/** Get user-friendly message for this error */
|
|
1287
|
+
get userMessage() {
|
|
1288
|
+
return ERROR_MESSAGES[this.code] || this.message;
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
/**
|
|
1293
|
+
* Exchanges a Dexie Cloud authorization code for access and refresh tokens.
|
|
1294
|
+
*
|
|
1295
|
+
* This is called after the OAuth callback delivers the authorization code
|
|
1296
|
+
* via postMessage (popup flow) or redirect.
|
|
1297
|
+
*
|
|
1298
|
+
* @param options - Exchange options
|
|
1299
|
+
* @returns Promise resolving to TokenFinalResponse
|
|
1300
|
+
* @throws OAuthError or TokenErrorResponseError on failure
|
|
1301
|
+
*/
|
|
1302
|
+
function exchangeOAuthCode(options) {
|
|
1303
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1304
|
+
const { databaseUrl, code, publicKey, scopes = ['ACCESS_DB'] } = options;
|
|
1305
|
+
const tokenRequest = {
|
|
1306
|
+
grant_type: 'authorization_code',
|
|
1307
|
+
code,
|
|
1308
|
+
public_key: publicKey,
|
|
1309
|
+
scopes,
|
|
1310
|
+
};
|
|
1311
|
+
try {
|
|
1312
|
+
const res = yield fetch(`${databaseUrl}/token`, {
|
|
1313
|
+
method: 'POST',
|
|
1314
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1315
|
+
body: JSON.stringify(tokenRequest),
|
|
1316
|
+
mode: 'cors',
|
|
1317
|
+
});
|
|
1318
|
+
if (!res.ok) {
|
|
1319
|
+
if (res.status === 400 || res.status === 401) {
|
|
1320
|
+
// Try to parse error response
|
|
1321
|
+
try {
|
|
1322
|
+
const errorResponse = yield res.json();
|
|
1323
|
+
if (errorResponse.type === 'error') {
|
|
1324
|
+
// Check for specific error codes
|
|
1325
|
+
if (errorResponse.messageCode === 'INVALID_OTP') {
|
|
1326
|
+
// In the context of OAuth, this likely means expired code
|
|
1327
|
+
throw new OAuthError('expired_code', undefined, errorResponse.message);
|
|
1328
|
+
}
|
|
1329
|
+
throw new TokenErrorResponseError(errorResponse);
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
catch (e) {
|
|
1333
|
+
if (e instanceof OAuthError || e instanceof TokenErrorResponseError) {
|
|
1334
|
+
throw e;
|
|
1335
|
+
}
|
|
1336
|
+
// Fall through to generic error
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
const errorText = yield res.text().catch(() => res.statusText);
|
|
1340
|
+
throw new OAuthError('provider_error', undefined, `Token exchange failed: ${res.status} ${errorText}`);
|
|
1341
|
+
}
|
|
1342
|
+
const response = yield res.json();
|
|
1343
|
+
if (response.type === 'error') {
|
|
1344
|
+
throw new TokenErrorResponseError(response);
|
|
1345
|
+
}
|
|
1346
|
+
if (response.type !== 'tokens') {
|
|
1347
|
+
throw new OAuthError('provider_error', undefined, `Unexpected response type: ${response.type}`);
|
|
1348
|
+
}
|
|
1349
|
+
return response;
|
|
1350
|
+
}
|
|
1351
|
+
catch (error) {
|
|
1352
|
+
if (error instanceof OAuthError || error instanceof TokenErrorResponseError) {
|
|
1353
|
+
throw error;
|
|
1354
|
+
}
|
|
1355
|
+
if (error instanceof TypeError) {
|
|
1356
|
+
// Network error
|
|
1357
|
+
throw new OAuthError('network_error');
|
|
1358
|
+
}
|
|
1359
|
+
throw error;
|
|
1360
|
+
}
|
|
1361
|
+
});
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
/** Default response when OAuth is disabled or unavailable */
|
|
1365
|
+
const OTP_ONLY_RESPONSE = {
|
|
1366
|
+
providers: [],
|
|
1367
|
+
otpEnabled: true,
|
|
1368
|
+
};
|
|
1369
|
+
/**
|
|
1370
|
+
* Fetches available authentication providers from the Dexie Cloud server.
|
|
1371
|
+
*
|
|
1372
|
+
* @param databaseUrl - The Dexie Cloud database URL
|
|
1373
|
+
* @param socialAuthEnabled - Whether social auth is enabled in client config (default: true)
|
|
1374
|
+
* @returns Promise resolving to AuthProvidersResponse
|
|
1375
|
+
*
|
|
1376
|
+
* Handles failures gracefully:
|
|
1377
|
+
* - 404 → Returns OTP-only (old server version)
|
|
1378
|
+
* - Network error → Returns OTP-only
|
|
1379
|
+
* - socialAuthEnabled: false → Returns OTP-only without fetching
|
|
1380
|
+
*/
|
|
1381
|
+
function fetchAuthProviders(databaseUrl_1) {
|
|
1382
|
+
return __awaiter(this, arguments, void 0, function* (databaseUrl, socialAuthEnabled = true) {
|
|
1383
|
+
// If social auth is disabled, return OTP-only without fetching
|
|
1384
|
+
if (!socialAuthEnabled) {
|
|
1385
|
+
return OTP_ONLY_RESPONSE;
|
|
1386
|
+
}
|
|
1387
|
+
try {
|
|
1388
|
+
const res = yield fetch(`${databaseUrl}/auth-providers`, {
|
|
1389
|
+
method: 'GET',
|
|
1390
|
+
headers: { 'Accept': 'application/json' },
|
|
1391
|
+
mode: 'cors',
|
|
1392
|
+
});
|
|
1393
|
+
if (res.status === 404) {
|
|
1394
|
+
// Old server version without OAuth support
|
|
1395
|
+
console.debug('[dexie-cloud] Server does not support /auth-providers endpoint. Using OTP-only authentication.');
|
|
1396
|
+
return OTP_ONLY_RESPONSE;
|
|
1397
|
+
}
|
|
1398
|
+
if (!res.ok) {
|
|
1399
|
+
console.warn(`[dexie-cloud] Failed to fetch auth providers: ${res.status} ${res.statusText}`);
|
|
1400
|
+
return OTP_ONLY_RESPONSE;
|
|
1401
|
+
}
|
|
1402
|
+
return yield res.json();
|
|
1403
|
+
}
|
|
1404
|
+
catch (error) {
|
|
1405
|
+
// Network error or other failure - fall back to OTP
|
|
1406
|
+
console.debug('[dexie-cloud] Could not fetch auth providers, falling back to OTP:', error);
|
|
1407
|
+
return OTP_ONLY_RESPONSE;
|
|
1408
|
+
}
|
|
1409
|
+
});
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
/** Generate a random state string for CSRF protection */
|
|
1413
|
+
function generateState() {
|
|
1414
|
+
const array = new Uint8Array(32);
|
|
1415
|
+
crypto.getRandomValues(array);
|
|
1416
|
+
return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
|
|
1417
|
+
}
|
|
1418
|
+
/** Build the OAuth login URL */
|
|
1419
|
+
function buildOAuthLoginUrl(options, state) {
|
|
1420
|
+
const url = new URL(`${options.databaseUrl}/oauth/login/${options.provider}`);
|
|
1421
|
+
url.searchParams.set('state', state);
|
|
1422
|
+
// Set the redirect URI for postMessage or custom scheme
|
|
1423
|
+
const redirectUri = options.redirectUri ||
|
|
1424
|
+
(typeof window !== 'undefined' ? window.location.origin : '');
|
|
1425
|
+
if (redirectUri) {
|
|
1426
|
+
url.searchParams.set('redirect_uri', redirectUri);
|
|
1427
|
+
}
|
|
1428
|
+
return url.toString();
|
|
1429
|
+
}
|
|
1430
|
+
/** Calculate centered popup position */
|
|
1431
|
+
function getPopupPosition(width, height) {
|
|
1432
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1433
|
+
const screenLeft = (_a = window.screenLeft) !== null && _a !== void 0 ? _a : window.screenX;
|
|
1434
|
+
const screenTop = (_b = window.screenTop) !== null && _b !== void 0 ? _b : window.screenY;
|
|
1435
|
+
const screenWidth = (_d = (_c = window.innerWidth) !== null && _c !== void 0 ? _c : document.documentElement.clientWidth) !== null && _d !== void 0 ? _d : screen.width;
|
|
1436
|
+
const screenHeight = (_f = (_e = window.innerHeight) !== null && _e !== void 0 ? _e : document.documentElement.clientHeight) !== null && _f !== void 0 ? _f : screen.height;
|
|
1437
|
+
const left = screenLeft + (screenWidth - width) / 2;
|
|
1438
|
+
const top = screenTop + (screenHeight - height) / 2;
|
|
1439
|
+
return { left: Math.max(0, left), top: Math.max(0, top) };
|
|
1440
|
+
}
|
|
1441
|
+
/**
|
|
1442
|
+
* Initiates OAuth login flow using a popup window.
|
|
1443
|
+
*
|
|
1444
|
+
* Opens a popup to the OAuth provider, listens for postMessage with the result,
|
|
1445
|
+
* and returns the Dexie Cloud authorization code.
|
|
1446
|
+
*
|
|
1447
|
+
* @param options - OAuth login options
|
|
1448
|
+
* @returns Promise resolving to OAuthLoginResult
|
|
1449
|
+
* @throws OAuthError on failure
|
|
1450
|
+
*/
|
|
1451
|
+
function oauthLogin(options) {
|
|
1452
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1453
|
+
const { databaseUrl, provider, usePopup = true } = options;
|
|
1454
|
+
if (!usePopup) {
|
|
1455
|
+
// For redirect flows, we can't return a promise - the page will navigate away
|
|
1456
|
+
throw new Error('Non-popup OAuth flow requires handleOAuthCallback after redirect');
|
|
1457
|
+
}
|
|
1458
|
+
const state = generateState();
|
|
1459
|
+
const loginUrl = buildOAuthLoginUrl(options, state);
|
|
1460
|
+
// Calculate popup dimensions and position
|
|
1461
|
+
const width = 500;
|
|
1462
|
+
const height = 600;
|
|
1463
|
+
const { left, top } = getPopupPosition(width, height);
|
|
1464
|
+
// Open popup window
|
|
1465
|
+
const popup = window.open(loginUrl, 'dexie-cloud-oauth', `width=${width},height=${height},left=${left},top=${top},menubar=no,toolbar=no,location=yes,status=no`);
|
|
1466
|
+
if (!popup) {
|
|
1467
|
+
throw new OAuthError('popup_blocked', provider);
|
|
1468
|
+
}
|
|
1469
|
+
return new Promise((resolve, reject) => {
|
|
1470
|
+
let resolved = false;
|
|
1471
|
+
// Listen for postMessage from the popup
|
|
1472
|
+
const handleMessage = (event) => {
|
|
1473
|
+
// Validate origin - must be from the Dexie Cloud server
|
|
1474
|
+
const expectedOrigin = new URL(databaseUrl).origin;
|
|
1475
|
+
if (event.origin !== expectedOrigin) {
|
|
1476
|
+
return; // Ignore messages from other origins
|
|
1477
|
+
}
|
|
1478
|
+
// Check if this is our OAuth result message
|
|
1479
|
+
if (!isOAuthResultMessage(event.data)) {
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1482
|
+
const message = event.data;
|
|
1483
|
+
// Validate state to prevent CSRF
|
|
1484
|
+
if (message.state !== state) {
|
|
1485
|
+
console.warn('[dexie-cloud] OAuth state mismatch, ignoring message');
|
|
1486
|
+
return;
|
|
1487
|
+
}
|
|
1488
|
+
// Clean up
|
|
1489
|
+
cleanup();
|
|
1490
|
+
resolved = true;
|
|
1491
|
+
// Handle error from OAuth flow
|
|
1492
|
+
if (message.error) {
|
|
1493
|
+
const errorCode = mapOAuthError(message.error);
|
|
1494
|
+
reject(new OAuthError(errorCode, provider, message.error));
|
|
1495
|
+
return;
|
|
1496
|
+
}
|
|
1497
|
+
// Success - return the authorization code
|
|
1498
|
+
if (message.code) {
|
|
1499
|
+
resolve({
|
|
1500
|
+
code: message.code,
|
|
1501
|
+
provider: message.provider,
|
|
1502
|
+
state: message.state,
|
|
1503
|
+
});
|
|
1504
|
+
}
|
|
1505
|
+
else {
|
|
1506
|
+
reject(new OAuthError('provider_error', provider, 'No authorization code received'));
|
|
1507
|
+
}
|
|
1508
|
+
};
|
|
1509
|
+
// Check if popup was closed without completing
|
|
1510
|
+
const checkPopupClosed = setInterval(() => {
|
|
1511
|
+
if (popup.closed && !resolved) {
|
|
1512
|
+
cleanup();
|
|
1513
|
+
reject(new OAuthError('popup_closed', provider));
|
|
1514
|
+
}
|
|
1515
|
+
}, 500);
|
|
1516
|
+
// Cleanup function
|
|
1517
|
+
const cleanup = () => {
|
|
1518
|
+
window.removeEventListener('message', handleMessage);
|
|
1519
|
+
clearInterval(checkPopupClosed);
|
|
1520
|
+
try {
|
|
1521
|
+
if (!popup.closed) {
|
|
1522
|
+
popup.close();
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
catch (_a) {
|
|
1526
|
+
// Ignore errors when closing popup
|
|
1527
|
+
}
|
|
1528
|
+
};
|
|
1529
|
+
// Start listening for messages
|
|
1530
|
+
window.addEventListener('message', handleMessage);
|
|
1531
|
+
});
|
|
1532
|
+
});
|
|
1533
|
+
}
|
|
1534
|
+
/** Map OAuth error strings to error codes */
|
|
1535
|
+
function mapOAuthError(error) {
|
|
1536
|
+
const lowerError = error.toLowerCase();
|
|
1537
|
+
if (lowerError.includes('access_denied') || lowerError.includes('access denied')) {
|
|
1538
|
+
return 'access_denied';
|
|
1539
|
+
}
|
|
1540
|
+
if (lowerError.includes('email') && lowerError.includes('verif')) {
|
|
1541
|
+
return 'email_not_verified';
|
|
1542
|
+
}
|
|
1543
|
+
if (lowerError.includes('expired')) {
|
|
1544
|
+
return 'expired_code';
|
|
1545
|
+
}
|
|
1546
|
+
if (lowerError.includes('state')) {
|
|
1547
|
+
return 'invalid_state';
|
|
1548
|
+
}
|
|
1549
|
+
return 'provider_error';
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1221
1552
|
function otpFetchTokenCallback(db) {
|
|
1222
1553
|
const { userInteraction } = db.cloud;
|
|
1223
1554
|
return function otpAuthenticate(_a) {
|
|
1224
1555
|
return __awaiter(this, arguments, void 0, function* ({ public_key, hints }) {
|
|
1225
|
-
var _b;
|
|
1556
|
+
var _b, _c;
|
|
1226
1557
|
let tokenRequest;
|
|
1227
1558
|
const url = (_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.databaseUrl;
|
|
1228
1559
|
if (!url)
|
|
1229
1560
|
throw new Error(`No database URL given.`);
|
|
1561
|
+
// Handle OAuth code exchange (from redirect/deep link flows)
|
|
1562
|
+
if ((hints === null || hints === void 0 ? void 0 : hints.oauthCode) && hints.provider) {
|
|
1563
|
+
return yield exchangeOAuthCode({
|
|
1564
|
+
databaseUrl: url,
|
|
1565
|
+
code: hints.oauthCode,
|
|
1566
|
+
publicKey: public_key,
|
|
1567
|
+
scopes: ['ACCESS_DB'],
|
|
1568
|
+
});
|
|
1569
|
+
}
|
|
1570
|
+
// Handle OAuth provider login (popup flow)
|
|
1571
|
+
if (hints === null || hints === void 0 ? void 0 : hints.provider) {
|
|
1572
|
+
return yield handleOAuthFlow(db, public_key, hints.provider);
|
|
1573
|
+
}
|
|
1230
1574
|
if ((hints === null || hints === void 0 ? void 0 : hints.grant_type) === 'demo') {
|
|
1231
1575
|
const demo_user = yield promptForEmail(userInteraction, 'Enter a demo user email', (hints === null || hints === void 0 ? void 0 : hints.email) || (hints === null || hints === void 0 ? void 0 : hints.userId));
|
|
1232
1576
|
tokenRequest = {
|
|
@@ -1250,6 +1594,18 @@ function otpFetchTokenCallback(db) {
|
|
|
1250
1594
|
};
|
|
1251
1595
|
}
|
|
1252
1596
|
else {
|
|
1597
|
+
// Check for available auth providers (OAuth + OTP)
|
|
1598
|
+
const socialAuthEnabled = ((_c = db.cloud.options) === null || _c === void 0 ? void 0 : _c.socialAuth) !== false;
|
|
1599
|
+
const authProviders = yield fetchAuthProviders(url, socialAuthEnabled);
|
|
1600
|
+
// If we have OAuth providers available, prompt for selection
|
|
1601
|
+
if (authProviders.providers.length > 0) {
|
|
1602
|
+
const selection = yield promptForProvider(userInteraction, authProviders.providers, authProviders.otpEnabled, 'Sign in');
|
|
1603
|
+
if (selection.type === 'provider') {
|
|
1604
|
+
// User selected an OAuth provider
|
|
1605
|
+
return yield handleOAuthFlow(db, public_key, selection.provider);
|
|
1606
|
+
}
|
|
1607
|
+
// User chose OTP - continue with email prompt below
|
|
1608
|
+
}
|
|
1253
1609
|
const email = yield promptForEmail(userInteraction, 'Enter email address', hints === null || hints === void 0 ? void 0 : hints.email);
|
|
1254
1610
|
if (/@demo.local$/.test(email)) {
|
|
1255
1611
|
tokenRequest = {
|
|
@@ -1327,6 +1683,49 @@ function otpFetchTokenCallback(db) {
|
|
|
1327
1683
|
});
|
|
1328
1684
|
};
|
|
1329
1685
|
}
|
|
1686
|
+
/**
|
|
1687
|
+
* Handles the OAuth popup flow and token exchange.
|
|
1688
|
+
*/
|
|
1689
|
+
function handleOAuthFlow(db, publicKey, provider) {
|
|
1690
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1691
|
+
var _a, _b, _c;
|
|
1692
|
+
const url = (_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.databaseUrl;
|
|
1693
|
+
if (!url)
|
|
1694
|
+
throw new Error(`No database URL given.`);
|
|
1695
|
+
const { userInteraction } = db.cloud;
|
|
1696
|
+
const usePopup = ((_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.oauthPopup) !== false;
|
|
1697
|
+
const redirectUri = ((_c = db.cloud.options) === null || _c === void 0 ? void 0 : _c.oauthRedirectUri) ||
|
|
1698
|
+
(typeof window !== 'undefined' ? window.location.origin : undefined);
|
|
1699
|
+
try {
|
|
1700
|
+
// Start OAuth popup flow
|
|
1701
|
+
const result = yield oauthLogin({
|
|
1702
|
+
databaseUrl: url,
|
|
1703
|
+
provider,
|
|
1704
|
+
redirectUri,
|
|
1705
|
+
usePopup,
|
|
1706
|
+
});
|
|
1707
|
+
// Exchange the auth code for tokens
|
|
1708
|
+
return yield exchangeOAuthCode({
|
|
1709
|
+
databaseUrl: url,
|
|
1710
|
+
code: result.code,
|
|
1711
|
+
publicKey,
|
|
1712
|
+
scopes: ['ACCESS_DB'],
|
|
1713
|
+
});
|
|
1714
|
+
}
|
|
1715
|
+
catch (error) {
|
|
1716
|
+
if (error instanceof OAuthError) {
|
|
1717
|
+
// Show user-friendly error message
|
|
1718
|
+
yield alertUser(userInteraction, 'Authentication Failed', {
|
|
1719
|
+
type: 'error',
|
|
1720
|
+
messageCode: 'GENERIC_ERROR',
|
|
1721
|
+
message: error.userMessage,
|
|
1722
|
+
messageParams: {},
|
|
1723
|
+
}).catch(() => { });
|
|
1724
|
+
}
|
|
1725
|
+
throw error;
|
|
1726
|
+
}
|
|
1727
|
+
});
|
|
1728
|
+
}
|
|
1330
1729
|
|
|
1331
1730
|
/** A way to log to console in production without terser stripping out
|
|
1332
1731
|
* it from the release bundle.
|
|
@@ -5388,6 +5787,99 @@ const Styles = {
|
|
|
5388
5787
|
color: "#333",
|
|
5389
5788
|
borderBottom: "1px solid #eee",
|
|
5390
5789
|
paddingBottom: "10px"
|
|
5790
|
+
},
|
|
5791
|
+
// OAuth Provider Button Styles
|
|
5792
|
+
ProviderButton: {
|
|
5793
|
+
display: "flex",
|
|
5794
|
+
alignItems: "center",
|
|
5795
|
+
justifyContent: "center",
|
|
5796
|
+
width: "100%",
|
|
5797
|
+
padding: "12px 16px",
|
|
5798
|
+
marginBottom: "10px",
|
|
5799
|
+
border: "1px solid #d1d5db",
|
|
5800
|
+
borderRadius: "6px",
|
|
5801
|
+
backgroundColor: "#ffffff",
|
|
5802
|
+
cursor: "pointer",
|
|
5803
|
+
fontSize: "14px",
|
|
5804
|
+
fontWeight: "500",
|
|
5805
|
+
color: "#374151",
|
|
5806
|
+
transition: "all 0.2s ease",
|
|
5807
|
+
gap: "12px"
|
|
5808
|
+
},
|
|
5809
|
+
ProviderButtonIcon: {
|
|
5810
|
+
width: "20px",
|
|
5811
|
+
height: "20px",
|
|
5812
|
+
flexShrink: 0
|
|
5813
|
+
},
|
|
5814
|
+
ProviderButtonText: {
|
|
5815
|
+
flex: 1,
|
|
5816
|
+
textAlign: "left"
|
|
5817
|
+
},
|
|
5818
|
+
// Provider-specific colors
|
|
5819
|
+
ProviderGoogle: {
|
|
5820
|
+
backgroundColor: "#ffffff",
|
|
5821
|
+
border: "1px solid #dadce0",
|
|
5822
|
+
color: "#3c4043"
|
|
5823
|
+
},
|
|
5824
|
+
ProviderGitHub: {
|
|
5825
|
+
backgroundColor: "#24292e",
|
|
5826
|
+
border: "1px solid #24292e",
|
|
5827
|
+
color: "#ffffff"
|
|
5828
|
+
},
|
|
5829
|
+
ProviderMicrosoft: {
|
|
5830
|
+
backgroundColor: "#ffffff",
|
|
5831
|
+
border: "1px solid #8c8c8c",
|
|
5832
|
+
color: "#5e5e5e"
|
|
5833
|
+
},
|
|
5834
|
+
ProviderApple: {
|
|
5835
|
+
backgroundColor: "#000000",
|
|
5836
|
+
border: "1px solid #000000",
|
|
5837
|
+
color: "#ffffff"
|
|
5838
|
+
},
|
|
5839
|
+
ProviderCustom: {
|
|
5840
|
+
backgroundColor: "#4f46e5",
|
|
5841
|
+
border: "1px solid #4f46e5",
|
|
5842
|
+
color: "#ffffff"
|
|
5843
|
+
},
|
|
5844
|
+
// Divider styles
|
|
5845
|
+
Divider: {
|
|
5846
|
+
display: "flex",
|
|
5847
|
+
alignItems: "center",
|
|
5848
|
+
margin: "20px 0",
|
|
5849
|
+
color: "#6b7280",
|
|
5850
|
+
fontSize: "13px"
|
|
5851
|
+
},
|
|
5852
|
+
DividerLine: {
|
|
5853
|
+
flex: 1,
|
|
5854
|
+
height: "1px",
|
|
5855
|
+
backgroundColor: "#e5e7eb"
|
|
5856
|
+
},
|
|
5857
|
+
DividerText: {
|
|
5858
|
+
padding: "0 12px",
|
|
5859
|
+
color: "#9ca3af"
|
|
5860
|
+
},
|
|
5861
|
+
// OTP Button (Continue with email)
|
|
5862
|
+
OtpButton: {
|
|
5863
|
+
display: "flex",
|
|
5864
|
+
alignItems: "center",
|
|
5865
|
+
justifyContent: "center",
|
|
5866
|
+
width: "100%",
|
|
5867
|
+
padding: "12px 16px",
|
|
5868
|
+
border: "1px solid #d1d5db",
|
|
5869
|
+
borderRadius: "6px",
|
|
5870
|
+
backgroundColor: "#f9fafb",
|
|
5871
|
+
cursor: "pointer",
|
|
5872
|
+
fontSize: "14px",
|
|
5873
|
+
fontWeight: "500",
|
|
5874
|
+
color: "#374151",
|
|
5875
|
+
transition: "all 0.2s ease",
|
|
5876
|
+
gap: "12px"
|
|
5877
|
+
},
|
|
5878
|
+
// Cancel button for provider selection
|
|
5879
|
+
CancelButtonRow: {
|
|
5880
|
+
display: "flex",
|
|
5881
|
+
justifyContent: "center",
|
|
5882
|
+
marginTop: "16px"
|
|
5391
5883
|
}
|
|
5392
5884
|
};
|
|
5393
5885
|
|
|
@@ -5458,6 +5950,82 @@ function valueTransformer(type, value) {
|
|
|
5458
5950
|
}
|
|
5459
5951
|
}
|
|
5460
5952
|
|
|
5953
|
+
/** Default SVG icons for built-in providers */
|
|
5954
|
+
const ProviderIcons = {
|
|
5955
|
+
google: `<svg viewBox="0 0 24 24" width="20" height="20"><path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/><path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/><path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/><path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/></svg>`,
|
|
5956
|
+
github: `<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z"/></svg>`,
|
|
5957
|
+
microsoft: `<svg viewBox="0 0 24 24" width="20" height="20"><rect fill="#F25022" x="1" y="1" width="10" height="10"/><rect fill="#00A4EF" x="1" y="13" width="10" height="10"/><rect fill="#7FBA00" x="13" y="1" width="10" height="10"/><rect fill="#FFB900" x="13" y="13" width="10" height="10"/></svg>`,
|
|
5958
|
+
apple: `<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.81-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/></svg>`,
|
|
5959
|
+
};
|
|
5960
|
+
/** Get provider-specific button styles */
|
|
5961
|
+
function getProviderStyle(providerType) {
|
|
5962
|
+
const baseStyle = Object.assign({}, Styles.ProviderButton);
|
|
5963
|
+
switch (providerType) {
|
|
5964
|
+
case 'google':
|
|
5965
|
+
return Object.assign(Object.assign({}, baseStyle), Styles.ProviderGoogle);
|
|
5966
|
+
case 'github':
|
|
5967
|
+
return Object.assign(Object.assign({}, baseStyle), Styles.ProviderGitHub);
|
|
5968
|
+
case 'microsoft':
|
|
5969
|
+
return Object.assign(Object.assign({}, baseStyle), Styles.ProviderMicrosoft);
|
|
5970
|
+
case 'apple':
|
|
5971
|
+
return Object.assign(Object.assign({}, baseStyle), Styles.ProviderApple);
|
|
5972
|
+
default:
|
|
5973
|
+
return Object.assign(Object.assign({}, baseStyle), Styles.ProviderCustom);
|
|
5974
|
+
}
|
|
5975
|
+
}
|
|
5976
|
+
/**
|
|
5977
|
+
* Button component for OAuth provider login.
|
|
5978
|
+
* Displays the provider's icon and name following provider branding guidelines.
|
|
5979
|
+
*/
|
|
5980
|
+
function AuthProviderButton({ provider, onClick }) {
|
|
5981
|
+
const { type, name, displayName, iconUrl } = provider;
|
|
5982
|
+
const style = getProviderStyle(type);
|
|
5983
|
+
// Determine button text
|
|
5984
|
+
const buttonText = `Continue with ${displayName}`;
|
|
5985
|
+
// Get icon - use custom iconUrl if provided, otherwise use built-in SVG
|
|
5986
|
+
const iconSvg = ProviderIcons[type] || '';
|
|
5987
|
+
return (_$1("button", { type: "button", style: style, onClick: onClick, class: `dxc-provider-btn dxc-provider-${type}`, "aria-label": buttonText },
|
|
5988
|
+
iconUrl ? (_$1("img", { src: iconUrl, alt: "", style: Styles.ProviderButtonIcon, "aria-hidden": "true" })) : iconSvg ? (_$1("span", { style: Styles.ProviderButtonIcon, "aria-hidden": "true", dangerouslySetInnerHTML: { __html: iconSvg } })) : null,
|
|
5989
|
+
_$1("span", { style: Styles.ProviderButtonText }, buttonText)));
|
|
5990
|
+
}
|
|
5991
|
+
/** Email/envelope icon for OTP button */
|
|
5992
|
+
const EmailIcon = `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M22 6L12 13 2 6"/></svg>`;
|
|
5993
|
+
/**
|
|
5994
|
+
* Button for email/OTP authentication option.
|
|
5995
|
+
*/
|
|
5996
|
+
function OtpButton({ onClick }) {
|
|
5997
|
+
return (_$1("button", { type: "button", style: Styles.OtpButton, onClick: onClick, class: "dxc-otp-btn", "aria-label": "Continue with email" },
|
|
5998
|
+
_$1("span", { style: Styles.ProviderButtonIcon, "aria-hidden": "true", dangerouslySetInnerHTML: { __html: EmailIcon } }),
|
|
5999
|
+
_$1("span", { style: Styles.ProviderButtonText }, "Continue with email")));
|
|
6000
|
+
}
|
|
6001
|
+
/**
|
|
6002
|
+
* Visual divider with "or" text.
|
|
6003
|
+
*/
|
|
6004
|
+
function Divider() {
|
|
6005
|
+
return (_$1("div", { style: Styles.Divider },
|
|
6006
|
+
_$1("div", { style: Styles.DividerLine }),
|
|
6007
|
+
_$1("span", { style: Styles.DividerText }, "or"),
|
|
6008
|
+
_$1("div", { style: Styles.DividerLine })));
|
|
6009
|
+
}
|
|
6010
|
+
|
|
6011
|
+
/**
|
|
6012
|
+
* Dialog component for OAuth provider selection.
|
|
6013
|
+
* Displays available OAuth providers as buttons and optionally an email/OTP option.
|
|
6014
|
+
*/
|
|
6015
|
+
function ProviderSelectionDialog({ title, alerts, providers, otpEnabled, cancelLabel, onSelectProvider, onSelectOtp, onCancel, }) {
|
|
6016
|
+
return (_$1(Dialog, { className: "dxc-provider-selection-dlg" },
|
|
6017
|
+
_$1(k$1, null,
|
|
6018
|
+
_$1("h3", { style: Styles.WindowHeader }, title),
|
|
6019
|
+
alerts.map((alert, idx) => (_$1("p", { key: idx, style: Styles.Alert[alert.type] }, resolveText(alert)))),
|
|
6020
|
+
_$1("div", { class: "dxc-providers" }, providers.map((provider) => (_$1(AuthProviderButton, { key: provider.name, provider: provider, onClick: () => onSelectProvider(provider.name) })))),
|
|
6021
|
+
otpEnabled && providers.length > 0 && (_$1(k$1, null,
|
|
6022
|
+
_$1(Divider, null),
|
|
6023
|
+
_$1(OtpButton, { onClick: onSelectOtp }))),
|
|
6024
|
+
otpEnabled && providers.length === 0 && (_$1(OtpButton, { onClick: onSelectOtp })),
|
|
6025
|
+
cancelLabel && (_$1("div", { style: Styles.CancelButtonRow },
|
|
6026
|
+
_$1("button", { type: "button", style: Styles.Button, onClick: onCancel }, cancelLabel))))));
|
|
6027
|
+
}
|
|
6028
|
+
|
|
5461
6029
|
class LoginGui extends x {
|
|
5462
6030
|
constructor(props) {
|
|
5463
6031
|
super(props);
|
|
@@ -5476,7 +6044,11 @@ class LoginGui extends x {
|
|
|
5476
6044
|
render(props, { userInteraction }) {
|
|
5477
6045
|
if (!userInteraction)
|
|
5478
6046
|
return null;
|
|
5479
|
-
//
|
|
6047
|
+
// Render appropriate dialog based on interaction type
|
|
6048
|
+
if (userInteraction.type === 'provider-selection') {
|
|
6049
|
+
return _$1(ProviderSelectionDialog, Object.assign({}, userInteraction));
|
|
6050
|
+
}
|
|
6051
|
+
// Default to LoginDialog for other interaction types
|
|
5480
6052
|
return _$1(LoginDialog, Object.assign({}, userInteraction));
|
|
5481
6053
|
}
|
|
5482
6054
|
}
|
|
@@ -6246,7 +6818,7 @@ function dexieCloud(dexie) {
|
|
|
6246
6818
|
const syncComplete = new Subject();
|
|
6247
6819
|
dexie.cloud = {
|
|
6248
6820
|
// @ts-ignore
|
|
6249
|
-
version: "4.
|
|
6821
|
+
version: "4.3.0",
|
|
6250
6822
|
options: Object.assign({}, DEFAULT_OPTIONS),
|
|
6251
6823
|
schema: null,
|
|
6252
6824
|
get currentUserId() {
|
|
@@ -6563,7 +7135,7 @@ function dexieCloud(dexie) {
|
|
|
6563
7135
|
}
|
|
6564
7136
|
}
|
|
6565
7137
|
// @ts-ignore
|
|
6566
|
-
dexieCloud.version = "4.
|
|
7138
|
+
dexieCloud.version = "4.3.0";
|
|
6567
7139
|
Dexie.Cloud = dexieCloud;
|
|
6568
7140
|
|
|
6569
7141
|
export { dexieCloud as default, defineYDocTrigger, dexieCloud, getTiedObjectId, getTiedRealmId, resolveText };
|