@oxyhq/core 12.8.0 → 12.9.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/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/boot/sessionColdBoot.js +16 -3
- package/dist/cjs/crypto/identityMarker.js +255 -0
- package/dist/cjs/crypto/keyManager.js +844 -106
- package/dist/cjs/index.js +8 -4
- package/dist/cjs/mixins/OxyServices.auth.js +21 -6
- package/dist/cjs/mixins/OxyServices.deviceBoot.js +9 -1
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/boot/sessionColdBoot.js +16 -3
- package/dist/esm/crypto/identityMarker.js +248 -0
- package/dist/esm/crypto/keyManager.js +843 -106
- package/dist/esm/index.js +2 -1
- package/dist/esm/mixins/OxyServices.auth.js +21 -6
- package/dist/esm/mixins/OxyServices.deviceBoot.js +9 -1
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/boot/sessionColdBoot.d.ts +25 -0
- package/dist/types/crypto/identityMarker.d.ts +94 -0
- package/dist/types/crypto/keyManager.d.ts +212 -3
- package/dist/types/index.d.ts +4 -2
- package/dist/types/mixins/OxyServices.auth.d.ts +27 -2
- package/dist/types/mixins/OxyServices.deviceBoot.d.ts +8 -0
- package/package.json +1 -1
- package/src/boot/__tests__/sessionColdBoot.test.ts +113 -0
- package/src/boot/sessionColdBoot.ts +42 -3
- package/src/crypto/__tests__/identityMocks.ts +125 -0
- package/src/crypto/__tests__/keyManager.atomicity.test.ts +79 -94
- package/src/crypto/__tests__/keyManager.cacheSafety.test.ts +175 -0
- package/src/crypto/__tests__/keyManager.identityStatus.test.ts +217 -0
- package/src/crypto/__tests__/keyManager.recoveryLadder.test.ts +179 -0
- package/src/crypto/__tests__/keyManager.storageMigration.test.ts +227 -0
- package/src/crypto/__tests__/keyManager.test.ts +77 -87
- package/src/crypto/identityMarker.ts +291 -0
- package/src/crypto/keyManager.ts +1026 -105
- package/src/index.ts +7 -1
- package/src/mixins/OxyServices.auth.ts +31 -7
- package/src/mixins/OxyServices.deviceBoot.ts +9 -1
- package/src/mixins/__tests__/OxyServices.deviceBoot.test.ts +4 -2
- package/src/mixins/__tests__/commonsSignIn.test.ts +84 -1
package/dist/cjs/index.js
CHANGED
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
* If a symbol does not appear here, it is NOT part of the public API.
|
|
19
19
|
*/
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
exports.
|
|
22
|
-
exports.
|
|
23
|
-
exports.
|
|
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 = void 0;
|
|
21
|
+
exports.normalizeLocale = exports.getBaseLanguage = 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.ServiceCredentialMismatchError = exports.oxyClient = exports.OXY_CLOUD_URL = exports.OxyAuthenticationTimeoutError = exports.OxyAuthenticationError = exports.AssetUrlResolutionError = exports.OxyServices = void 0;
|
|
22
|
+
exports.safeJsonParse = exports.buildPaginationParams = 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 = void 0;
|
|
23
|
+
exports.buildOAuthAuthorizeUrl = exports.runColdBoot = 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 = 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 = void 0;
|
|
25
25
|
// Ensure crypto polyfills are loaded before anything else
|
|
26
26
|
require("./crypto/polyfill");
|
|
27
27
|
// ---------------------------------------------------------------------------
|
|
@@ -100,6 +100,10 @@ var keyManager_1 = require("./crypto/keyManager");
|
|
|
100
100
|
Object.defineProperty(exports, "KeyManager", { enumerable: true, get: function () { return keyManager_1.KeyManager; } });
|
|
101
101
|
Object.defineProperty(exports, "IdentityAlreadyExistsError", { enumerable: true, get: function () { return keyManager_1.IdentityAlreadyExistsError; } });
|
|
102
102
|
Object.defineProperty(exports, "IdentityPersistError", { enumerable: true, get: function () { return keyManager_1.IdentityPersistError; } });
|
|
103
|
+
Object.defineProperty(exports, "IdentityUnavailableError", { enumerable: true, get: function () { return keyManager_1.IdentityUnavailableError; } });
|
|
104
|
+
var identityMarker_1 = require("./crypto/identityMarker");
|
|
105
|
+
Object.defineProperty(exports, "readIdentityMarker", { enumerable: true, get: function () { return identityMarker_1.readIdentityMarker; } });
|
|
106
|
+
Object.defineProperty(exports, "updateIdentityMarker", { enumerable: true, get: function () { return identityMarker_1.updateIdentityMarker; } });
|
|
103
107
|
var signatureService_1 = require("./crypto/signatureService");
|
|
104
108
|
Object.defineProperty(exports, "SignatureService", { enumerable: true, get: function () { return signatureService_1.SignatureService; } });
|
|
105
109
|
var recoveryPhrase_1 = require("./crypto/recoveryPhrase");
|
|
@@ -298,12 +298,16 @@ function OxyServicesAuthMixin(Base) {
|
|
|
298
298
|
* The client must sign this challenge with their private key
|
|
299
299
|
*
|
|
300
300
|
* @param publicKey - The user's public key
|
|
301
|
+
* @param requestOptions - Optional per-call transport overrides (`retry`,
|
|
302
|
+
* `timeout`). Interactive callers omit it (defaults keep retries); the
|
|
303
|
+
* cold-boot `shared-key-signin` step passes `{ retry: false }` so a slow
|
|
304
|
+
* network cannot multiply boot latency via the inner retry loop.
|
|
301
305
|
*/
|
|
302
|
-
async requestChallenge(publicKey) {
|
|
306
|
+
async requestChallenge(publicKey, requestOptions) {
|
|
303
307
|
try {
|
|
304
308
|
return await this.makeRequest('POST', '/auth/challenge', {
|
|
305
309
|
publicKey,
|
|
306
|
-
}, { cache: false });
|
|
310
|
+
}, { cache: false, ...requestOptions });
|
|
307
311
|
}
|
|
308
312
|
catch (error) {
|
|
309
313
|
throw this.handleError(error);
|
|
@@ -318,8 +322,12 @@ function OxyServicesAuthMixin(Base) {
|
|
|
318
322
|
* @param timestamp - Timestamp when the signature was created
|
|
319
323
|
* @param deviceName - Optional device name
|
|
320
324
|
* @param deviceFingerprint - Optional device fingerprint
|
|
325
|
+
* @param requestOptions - Optional per-call transport overrides (`retry`,
|
|
326
|
+
* `timeout`). Interactive callers omit it (defaults keep retries); the
|
|
327
|
+
* cold-boot `shared-key-signin` step passes `{ retry: false }` so a slow
|
|
328
|
+
* network cannot multiply boot latency via the inner retry loop.
|
|
321
329
|
*/
|
|
322
|
-
async verifyChallenge(publicKey, challenge, signature, timestamp, deviceName, deviceFingerprint) {
|
|
330
|
+
async verifyChallenge(publicKey, challenge, signature, timestamp, deviceName, deviceFingerprint, requestOptions) {
|
|
323
331
|
try {
|
|
324
332
|
const res = await this.makeRequest('POST', '/auth/verify', {
|
|
325
333
|
publicKey,
|
|
@@ -328,7 +336,7 @@ function OxyServicesAuthMixin(Base) {
|
|
|
328
336
|
timestamp,
|
|
329
337
|
deviceName,
|
|
330
338
|
deviceFingerprint,
|
|
331
|
-
}, { cache: false });
|
|
339
|
+
}, { cache: false, ...requestOptions });
|
|
332
340
|
// Plant the freshly-minted tokens, mirroring `claimSessionByToken`.
|
|
333
341
|
// `/auth/verify` returns the first access token (and refresh token) in
|
|
334
342
|
// its body, so installing it here means callers get an authenticated
|
|
@@ -466,6 +474,13 @@ function OxyServicesAuthMixin(Base) {
|
|
|
466
474
|
*
|
|
467
475
|
* The cold-boot wiring that CALLS this lives in `OxyContext`
|
|
468
476
|
* (`@oxyhq/services`); this method just performs the exchange.
|
|
477
|
+
*
|
|
478
|
+
* @param opts.requestOptions - Optional per-call transport overrides
|
|
479
|
+
* (`retry`, `timeout`) forwarded to BOTH the `requestChallenge` and
|
|
480
|
+
* `verifyChallenge` round-trips. Interactive flows omit it (defaults keep
|
|
481
|
+
* retries); the cold-boot `shared-key-signin` step passes `{ retry: false }`
|
|
482
|
+
* so this network step cannot multiply boot latency via the inner retry
|
|
483
|
+
* loop. The token-refresh scheduler / 401 lane still retry later.
|
|
469
484
|
*/
|
|
470
485
|
async signInWithSharedIdentity(opts = {}) {
|
|
471
486
|
try {
|
|
@@ -479,10 +494,10 @@ function OxyServicesAuthMixin(Base) {
|
|
|
479
494
|
if (!sharedPublicKey) {
|
|
480
495
|
return null;
|
|
481
496
|
}
|
|
482
|
-
const { challenge } = await this.requestChallenge(sharedPublicKey);
|
|
497
|
+
const { challenge } = await this.requestChallenge(sharedPublicKey, opts.requestOptions);
|
|
483
498
|
const signed = await signatureService_1.SignatureService.signChallengeWithSharedKey(challenge);
|
|
484
499
|
// `signed.challenge` carries the SIGNATURE (mirrors `signChallenge`).
|
|
485
|
-
return await this.verifyChallenge(signed.publicKey, challenge, signed.challenge, signed.timestamp, opts.deviceName, opts.deviceFingerprint);
|
|
500
|
+
return await this.verifyChallenge(signed.publicKey, challenge, signed.challenge, signed.timestamp, opts.deviceName, opts.deviceFingerprint, opts.requestOptions);
|
|
486
501
|
}
|
|
487
502
|
catch (error) {
|
|
488
503
|
throw this.handleError(error);
|
|
@@ -32,11 +32,19 @@ function OxyServicesDeviceBootMixin(Base) {
|
|
|
32
32
|
* `no_active_session`) to decide whether to drop the secret and fall back or
|
|
33
33
|
* resolve signed-out.
|
|
34
34
|
*
|
|
35
|
+
* `retry: false`: the mint is a single logical attempt. The proactive
|
|
36
|
+
* token-refresh scheduler and the reactive 401 lane already own backoff and
|
|
37
|
+
* re-arm, so `HttpService`'s inner retry loop here would only multiply the
|
|
38
|
+
* mint's latency on a slow/black-hole network (3 retries × 5s timeout ≈ 20s
|
|
39
|
+
* per lane) with no correctness benefit — it is the dominant term in the cold
|
|
40
|
+
* boot's worst-case time-to-route. A transient failure surfaces once and the
|
|
41
|
+
* scheduler/401 path retries it later.
|
|
42
|
+
*
|
|
35
43
|
* @throws if the response does not match {@link deviceTokenMintResponseSchema}.
|
|
36
44
|
*/
|
|
37
45
|
async mintFromDeviceSecret(deviceId, deviceSecret) {
|
|
38
46
|
try {
|
|
39
|
-
const res = await this.makeRequest('POST', '/session/device/token', { deviceId, deviceSecret }, { cache: false, skipAuth: true });
|
|
47
|
+
const res = await this.makeRequest('POST', '/session/device/token', { deviceId, deviceSecret }, { cache: false, skipAuth: true, retry: false });
|
|
40
48
|
const parsed = (0, contracts_1.safeParseContract)(contracts_1.deviceTokenMintResponseSchema, res);
|
|
41
49
|
if (!parsed) {
|
|
42
50
|
throw new Error('session/device/token returned an unexpected response shape');
|