@oxyhq/core 12.10.4 → 12.10.6

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.
@@ -1552,6 +1552,7 @@
1552
1552
  "fileEmpty": "File \"{{name}}\" is empty",
1553
1553
  "fileTooLarge": "\"{{name}}\" is too large. Maximum file size is {{maxSize}}",
1554
1554
  "noValidFiles": "No valid files to upload",
1555
+ "imagesOnlyRequired": "Only image files can be selected",
1555
1556
  "waitForSelection": "Please wait for the current file selection to complete",
1556
1557
  "noFilesSelected": "No files were selected",
1557
1558
  "loadSomeFailed": "Failed to load some files:\n{{errors}}",
@@ -1552,6 +1552,7 @@
1552
1552
  "fileEmpty": "El archivo \"{{name}}\" está vacío",
1553
1553
  "fileTooLarge": "\"{{name}}\" es demasiado grande. El tamaño máximo es {{maxSize}}",
1554
1554
  "noValidFiles": "No hay archivos válidos para subir",
1555
+ "imagesOnlyRequired": "Solo se pueden seleccionar archivos de imagen",
1555
1556
  "waitForSelection": "Espera a que se complete la selección actual",
1556
1557
  "noFilesSelected": "No se seleccionaron archivos",
1557
1558
  "loadSomeFailed": "Error al cargar algunos archivos:\n{{errors}}",
@@ -1552,6 +1552,7 @@
1552
1552
  "fileEmpty": "File \"{{name}}\" is empty",
1553
1553
  "fileTooLarge": "\"{{name}}\" is too large. Maximum file size is {{maxSize}}",
1554
1554
  "noValidFiles": "No valid files to upload",
1555
+ "imagesOnlyRequired": "Only image files can be selected",
1555
1556
  "waitForSelection": "Please wait for the current file selection to complete",
1556
1557
  "noFilesSelected": "No files were selected",
1557
1558
  "loadSomeFailed": "Failed to load some files:\n{{errors}}",
@@ -1552,6 +1552,7 @@
1552
1552
  "fileEmpty": "El archivo \"{{name}}\" está vacío",
1553
1553
  "fileTooLarge": "\"{{name}}\" es demasiado grande. El tamaño máximo es {{maxSize}}",
1554
1554
  "noValidFiles": "No hay archivos válidos para subir",
1555
+ "imagesOnlyRequired": "Solo se pueden seleccionar archivos de imagen",
1555
1556
  "waitForSelection": "Espera a que se complete la selección actual",
1556
1557
  "noFilesSelected": "No se seleccionaron archivos",
1557
1558
  "loadSomeFailed": "Error al cargar algunos archivos:\n{{errors}}",
package/dist/cjs/index.js CHANGED
@@ -21,7 +21,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
21
21
  exports.FALLBACK_LOCALE = exports.SUPPORTED_LANGUAGES = exports.TopicSource = exports.TopicType = exports.SECURITY_EVENT_SEVERITY_MAP = exports.DeviceManager = exports.deriveSharedSecret = exports.AEAD_NONCE_LENGTH = exports.AEAD_KEY_LENGTH = exports.decryptAead = exports.encryptAead = exports.hkdfSha256 = exports.RecoveryPhraseService = exports.SignatureService = exports.updateIdentityMarker = exports.readIdentityMarker = exports.IdentityUnavailableError = exports.IdentityPersistError = exports.IdentityAlreadyExistsError = exports.KeyManager = exports.sessionsArraysEqual = exports.normalizeAndSortSessions = exports.mergeSessions = exports.authenticatedApiCall = exports.withAuthErrorHandling = exports.isAuthenticationError = exports.ensureValidToken = exports.AuthenticationFailedError = exports.SessionSyncRequiredError = exports.verifyPublicCardAttestation = exports.parseAttestPayload = exports.parseIdPayload = exports.buildUserDid = exports.ORGANIZATION_CATEGORIES = exports.normalizeProfileLinks = exports.getNormalizedUserHandle = exports.getCanonicalUserHandle = exports.normalizeUserIdentityOrNull = exports.normalizeUserIdentity = exports.getNormalizedUserId = exports.OxyAppDataIdentifierError = exports.parseCommonsApprovalExpiresAt = exports.getCommonsApprovalBlockingReason = exports.ServiceCredentialMismatchError = exports.oxyClient = exports.OXY_CLOUD_URL = exports.OxyAuthenticationTimeoutError = exports.OxyAuthenticationError = exports.AssetUrlResolutionError = exports.OxyServices = void 0;
22
22
  exports.buildUrl = exports.buildSearchParams = exports.translate = exports.withRetry = exports.delay = exports.shouldAllowRequest = exports.recordSuccess = exports.recordFailure = exports.calculateBackoffInterval = exports.createCircuitBreakerState = exports.DEFAULT_CIRCUIT_BREAKER_CONFIG = exports.isRetryableError = exports.isNetworkError = exports.isServerError = exports.isRateLimitError = exports.isNotFoundError = exports.isForbiddenError = exports.isUnauthorizedError = exports.isAlreadyRegisteredError = exports.getErrorMessage = exports.getErrorStatus = exports.HttpStatus = exports.getSystemColorScheme = exports.systemPrefersDarkMode = exports.getOppositeTheme = exports.normalizeColorScheme = exports.normalizeTheme = exports.getContrastTextColor = exports.isLightColor = exports.withOpacity = exports.rgbToHex = exports.hexToRgb = exports.lightenColor = exports.darkenColor = exports.isWebBrowser = exports.isAndroid = exports.isIOS = exports.isNative = exports.isWeb = exports.setPlatformOS = exports.getPlatformOS = exports.getPrimaryLanguage = exports.getUserLanguages = exports.isRTLLocale = exports.getNativeLanguageName = exports.getLanguageName = exports.getLanguageMetadata = exports.isSupportedLocale = exports.normalizeLocale = exports.getBaseLanguage = void 0;
23
23
  exports.isOxyRpOrigin = exports.CENTRAL_IDP_APEX = exports.registrableApex = exports.getAccountColor = exports.formatPublicKeyHandle = exports.getAccountFallbackHandle = exports.getAccountDisplayName = exports.createQuickAccount = exports.buildAccountsArray = exports.updateAvatarVisibility = exports.isDev = exports.consoleSink = exports.resetLoggerConfig = exports.getLoggerConfig = exports.configureLogger = exports.createLogger = exports.logger = exports.normalizeMultilineText = exports.normalizeInlineText = exports.validateAndSanitizeUserInput = exports.isValidObjectId = exports.sanitizeHTML = exports.sanitizeString = exports.isValidFileType = exports.isValidFileSize = exports.isValidDate = exports.isValidURL = exports.isValidUUID = exports.isValidObject = exports.isValidArray = exports.isRequiredBoolean = exports.isRequiredNumber = exports.isRequiredString = exports.DISPLAY_NAME_ORPHANED_MARK_SOURCE = exports.DISPLAY_NAME_DISALLOWED_SOURCE = exports.DISPLAY_NAME_ALLOWED_SCRIPTS = exports.isValidDisplayName = exports.isValidPassword = exports.isValidUsername = exports.isValidEmail = exports.PASSWORD_REGEX = exports.USERNAME_REGEX = exports.EMAIL_REGEX = exports.retryAsync = exports.validateRequiredFields = exports.handleHttpError = exports.createApiError = exports.ErrorCodes = exports.safeJsonParse = exports.buildPaginationParams = void 0;
24
- exports.packageInfo = exports.runSessionColdBoot = exports.TOKEN_REFRESH_LEAD_MS = exports.startTokenRefreshScheduler = exports.installAuthRefreshHandler = exports.createAuthRefreshHandler = exports.refreshDeviceSecretArm = exports.refreshPersistedSession = exports.AUTH_STATE_STORAGE_KEY = exports.createMemoryAuthStateStore = exports.createNativeAuthStateStore = exports.createWebAuthStateStore = exports.createAccountDialogController = exports.AccountDialogController = exports.switchableAccountIds = exports.projectSwitchableAccounts = exports.accountIdsOf = exports.activeUserOf = exports.activeSessionIdOf = exports.deviceStateToClientSessions = exports.createSessionClient = exports.createSessionClientHost = exports.SessionClient = exports.redeemHubTicketOnHub = exports.syncHubAfterSignIn = exports.parseHubSyncReturnUrl = exports.normalizeOfficialReturnOrigin = exports.isAllowedDeviceJoinOrigin = exports.isOfficialWebOrigin = exports.isLoopbackOrigin = exports.isIdpHubOrigin = exports.buildHubSyncUrl = exports.buildIdpHubOrigin = exports.clearOAuthHandshake = exports.readOAuthHandshake = exports.persistOAuthHandshake = exports.normalizeOAuthRedirectUri = exports.OXY_CROSS_ORIGIN_RESTORE_ATTEMPTED_KEY = exports.OXY_SILENT_OAUTH_ATTEMPTED_KEY = exports.OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY = exports.OXY_OAUTH_STATE_STORAGE_KEY = exports.OXY_AUTHORIZE_URL = exports.DEFAULT_OAUTH_SCOPE = exports.generatePkcePair = exports.generateOAuthState = exports.computeCodeChallenge = exports.buildOAuthAuthorizeUrl = exports.runColdBoot = void 0;
24
+ exports.runSessionColdBoot = exports.TOKEN_REFRESH_LEAD_MS = exports.startTokenRefreshScheduler = exports.installAuthRefreshHandler = exports.createAuthRefreshHandler = exports.refreshDeviceSecretArm = exports.refreshPersistedSession = exports.AUTH_STATE_STORAGE_KEY = exports.createMemoryAuthStateStore = exports.createNativeAuthStateStore = exports.createWebAuthStateStore = exports.createAccountDialogController = exports.AccountDialogController = exports.switchableAccountIds = exports.projectSwitchableAccounts = exports.accountIdsOf = exports.activeUserOf = exports.activeSessionIdOf = exports.deviceStateToClientSessions = exports.createSessionClient = exports.createSessionClientHost = exports.SessionClient = exports.redeemHubTicketOnHub = exports.syncHubAfterSignIn = exports.parseHubSyncReturnUrl = exports.normalizeOfficialReturnOrigin = exports.isAllowedDeviceJoinOrigin = exports.isOfficialWebOrigin = exports.isLoopbackOrigin = exports.isIdpHubOrigin = exports.buildHubSyncUrl = exports.buildIdpHubOrigin = exports.consumeOAuthReturnPath = exports.persistOAuthReturnPath = exports.clearOAuthHandshake = exports.readOAuthHandshake = exports.persistOAuthHandshake = exports.normalizeOAuthRedirectUri = exports.OXY_OAUTH_RETURN_PATH_STORAGE_KEY = exports.OXY_CROSS_ORIGIN_RESTORE_ATTEMPTED_KEY = exports.OXY_SILENT_OAUTH_ATTEMPTED_KEY = exports.OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY = exports.OXY_OAUTH_STATE_STORAGE_KEY = exports.OXY_AUTHORIZE_URL = exports.DEFAULT_OAUTH_SCOPE = exports.generatePkcePair = exports.generateOAuthState = exports.computeCodeChallenge = exports.buildOAuthAuthorizeUrl = exports.runColdBoot = void 0;
25
+ exports.packageInfo = void 0;
25
26
  // Ensure crypto polyfills are loaded before anything else
26
27
  require("./crypto/polyfill");
27
28
  // ---------------------------------------------------------------------------
@@ -313,10 +314,13 @@ Object.defineProperty(exports, "OXY_OAUTH_STATE_STORAGE_KEY", { enumerable: true
313
314
  Object.defineProperty(exports, "OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY", { enumerable: true, get: function () { return oauthPkce_1.OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY; } });
314
315
  Object.defineProperty(exports, "OXY_SILENT_OAUTH_ATTEMPTED_KEY", { enumerable: true, get: function () { return oauthPkce_1.OXY_SILENT_OAUTH_ATTEMPTED_KEY; } });
315
316
  Object.defineProperty(exports, "OXY_CROSS_ORIGIN_RESTORE_ATTEMPTED_KEY", { enumerable: true, get: function () { return oauthPkce_1.OXY_CROSS_ORIGIN_RESTORE_ATTEMPTED_KEY; } });
317
+ Object.defineProperty(exports, "OXY_OAUTH_RETURN_PATH_STORAGE_KEY", { enumerable: true, get: function () { return oauthPkce_1.OXY_OAUTH_RETURN_PATH_STORAGE_KEY; } });
316
318
  Object.defineProperty(exports, "normalizeOAuthRedirectUri", { enumerable: true, get: function () { return oauthPkce_1.normalizeOAuthRedirectUri; } });
317
319
  Object.defineProperty(exports, "persistOAuthHandshake", { enumerable: true, get: function () { return oauthPkce_1.persistOAuthHandshake; } });
318
320
  Object.defineProperty(exports, "readOAuthHandshake", { enumerable: true, get: function () { return oauthPkce_1.readOAuthHandshake; } });
319
321
  Object.defineProperty(exports, "clearOAuthHandshake", { enumerable: true, get: function () { return oauthPkce_1.clearOAuthHandshake; } });
322
+ Object.defineProperty(exports, "persistOAuthReturnPath", { enumerable: true, get: function () { return oauthPkce_1.persistOAuthReturnPath; } });
323
+ Object.defineProperty(exports, "consumeOAuthReturnPath", { enumerable: true, get: function () { return oauthPkce_1.consumeOAuthReturnPath; } });
320
324
  var officialOrigins_1 = require("./utils/officialOrigins");
321
325
  Object.defineProperty(exports, "buildIdpHubOrigin", { enumerable: true, get: function () { return officialOrigins_1.buildIdpHubOrigin; } });
322
326
  Object.defineProperty(exports, "buildHubSyncUrl", { enumerable: true, get: function () { return officialOrigins_1.buildHubSyncUrl; } });
@@ -33,7 +33,7 @@ async function syncHubAfterSignIn(oxy, opts) {
33
33
  }
34
34
  const hubOrigin = (0, officialOrigins_1.buildIdpHubOrigin)();
35
35
  const issued = await oxy.issueHubTicket(hubOrigin);
36
- const returnUrl = `${location.origin}${location.pathname}${location.search}`;
36
+ const returnUrl = `${location.origin}${location.pathname}${location.search}${location.hash}`;
37
37
  const syncUrl = (0, officialOrigins_1.buildHubSyncUrl)(issued.ticket, returnUrl);
38
38
  window.location.assign(syncUrl);
39
39
  return true;
@@ -17,12 +17,14 @@
17
17
  * React Native. No `require()`, so the ESM build stays bundler-clean.
18
18
  */
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.OXY_CROSS_ORIGIN_RESTORE_ATTEMPTED_KEY = exports.OXY_SILENT_OAUTH_ATTEMPTED_KEY = exports.OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY = exports.OXY_OAUTH_STATE_STORAGE_KEY = exports.DEFAULT_OAUTH_SCOPE = exports.OXY_AUTHORIZE_URL = void 0;
20
+ exports.OXY_OAUTH_RETURN_PATH_STORAGE_KEY = exports.OXY_CROSS_ORIGIN_RESTORE_ATTEMPTED_KEY = exports.OXY_SILENT_OAUTH_ATTEMPTED_KEY = exports.OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY = exports.OXY_OAUTH_STATE_STORAGE_KEY = exports.DEFAULT_OAUTH_SCOPE = exports.OXY_AUTHORIZE_URL = void 0;
21
21
  exports.computeCodeChallenge = computeCodeChallenge;
22
22
  exports.generatePkcePair = generatePkcePair;
23
23
  exports.generateOAuthState = generateOAuthState;
24
24
  exports.buildOAuthAuthorizeUrl = buildOAuthAuthorizeUrl;
25
25
  exports.normalizeOAuthRedirectUri = normalizeOAuthRedirectUri;
26
+ exports.persistOAuthReturnPath = persistOAuthReturnPath;
27
+ exports.consumeOAuthReturnPath = consumeOAuthReturnPath;
26
28
  exports.persistOAuthHandshake = persistOAuthHandshake;
27
29
  exports.readOAuthHandshake = readOAuthHandshake;
28
30
  exports.clearOAuthHandshake = clearOAuthHandshake;
@@ -155,9 +157,19 @@ exports.OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY = 'oxy_oauth_code_verifier';
155
157
  exports.OXY_SILENT_OAUTH_ATTEMPTED_KEY = 'oxy.silent_oauth_attempted';
156
158
  /** `sessionStorage` key — blocks further cross-origin auto-restore in this tab. */
157
159
  exports.OXY_CROSS_ORIGIN_RESTORE_ATTEMPTED_KEY = 'oxy.cross_origin_restore_attempted';
160
+ /**
161
+ * `sessionStorage` key for the in-app path to return to after an authorize
162
+ * round trip. See {@link persistOAuthReturnPath}.
163
+ */
164
+ exports.OXY_OAUTH_RETURN_PATH_STORAGE_KEY = 'oxy.oauth_return_path';
158
165
  /**
159
166
  * Normalize a redirect URI to its origin. Official Oxy apps register apex
160
167
  * origins (`https://inbox.oxy.so`) — never path-qualified URLs.
168
+ *
169
+ * This is why the path cannot ride along in `redirect_uri`, and why
170
+ * {@link persistOAuthReturnPath} exists: the authorize round trip always lands
171
+ * back on the bare origin, so the page the user was on has to be carried
172
+ * out-of-band and restored on return.
161
173
  */
162
174
  function normalizeOAuthRedirectUri(input) {
163
175
  try {
@@ -167,6 +179,70 @@ function normalizeOAuthRedirectUri(input) {
167
179
  return input;
168
180
  }
169
181
  }
182
+ /**
183
+ * Is this a safe same-origin path to restore into the address bar?
184
+ *
185
+ * Only site-relative paths are accepted. `//evil.com` and `/\evil.com` are
186
+ * rejected because browsers resolve both as protocol-relative *absolute* URLs —
187
+ * without this check, anything able to write one key of `sessionStorage` could
188
+ * turn the post-authorize restore into an open redirect.
189
+ */
190
+ function isSafeReturnPath(value) {
191
+ if (!value.startsWith('/'))
192
+ return false;
193
+ if (value.startsWith('//') || value.startsWith('/\\'))
194
+ return false;
195
+ if (value.length > 2048)
196
+ return false;
197
+ return true;
198
+ }
199
+ /**
200
+ * Remember the page the user was on before a full-page authorize redirect.
201
+ *
202
+ * Silent cross-origin restore navigates the whole tab to the IdP and back, and
203
+ * `redirect_uri` must be a registered apex origin, so the browser returns to
204
+ * `/` no matter which page the visit started on. Without this, every deep link
205
+ * — a shared URL, a search result, an ad landing page — silently became the
206
+ * home page for signed-out visitors on their first navigation in a tab.
207
+ *
208
+ * Stored next to the PKCE handshake it belongs to, and cleared by
209
+ * {@link clearOAuthHandshake} so a stale entry can never be applied to an
210
+ * unrelated later navigation.
211
+ */
212
+ function persistOAuthReturnPath(path) {
213
+ const store = globalThis.sessionStorage;
214
+ if (!store || !isSafeReturnPath(path))
215
+ return;
216
+ try {
217
+ store.setItem(exports.OXY_OAUTH_RETURN_PATH_STORAGE_KEY, path);
218
+ }
219
+ catch (error) {
220
+ // Non-fatal: the round trip still completes, it just lands on the origin.
221
+ logger_1.logger.warn('Could not persist OAuth return path to sessionStorage', { component: 'oauthPkce' }, error);
222
+ }
223
+ }
224
+ /**
225
+ * Read and remove the persisted return path, or `null` when there is none or
226
+ * it failed validation.
227
+ *
228
+ * Single-use by design: it is consumed on the first return so a leftover value
229
+ * cannot redirect a later navigation in the same tab.
230
+ */
231
+ function consumeOAuthReturnPath() {
232
+ const store = globalThis.sessionStorage;
233
+ if (!store)
234
+ return null;
235
+ try {
236
+ const value = store.getItem(exports.OXY_OAUTH_RETURN_PATH_STORAGE_KEY);
237
+ store.removeItem(exports.OXY_OAUTH_RETURN_PATH_STORAGE_KEY);
238
+ if (!value || !isSafeReturnPath(value))
239
+ return null;
240
+ return value;
241
+ }
242
+ catch {
243
+ return null;
244
+ }
245
+ }
170
246
  /** Persist the OAuth handshake for a full-page redirect return (web only). */
171
247
  function persistOAuthHandshake(state, codeVerifier) {
172
248
  const store = globalThis.sessionStorage;
@@ -199,6 +275,9 @@ function clearOAuthHandshake() {
199
275
  try {
200
276
  store?.removeItem(exports.OXY_OAUTH_STATE_STORAGE_KEY);
201
277
  store?.removeItem(exports.OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY);
278
+ // The return path belongs to this handshake. Leaving it behind would let a
279
+ // later, unrelated navigation in the same tab be redirected by it.
280
+ store?.removeItem(exports.OXY_OAUTH_RETURN_PATH_STORAGE_KEY);
202
281
  }
203
282
  catch {
204
283
  // Best-effort cleanup only.