@oxy.so/core 1.0.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/LICENSE +202 -0
- package/NOTICE +16 -0
- package/README.md +221 -0
- package/dist/cjs/.tsbuildinfo +1 -0
- package/dist/cjs/HttpService.js +1270 -0
- package/dist/cjs/OxyServices.base.js +390 -0
- package/dist/cjs/OxyServices.errors.js +95 -0
- package/dist/cjs/OxyServices.js +72 -0
- package/dist/cjs/boot/sessionColdBoot.js +395 -0
- package/dist/cjs/constants/version.js +16 -0
- package/dist/cjs/crypto/aead.js +79 -0
- package/dist/cjs/crypto/ecdh.js +35 -0
- package/dist/cjs/crypto/identityMarker.js +255 -0
- package/dist/cjs/crypto/kdf.js +39 -0
- package/dist/cjs/crypto/keyManager.js +2234 -0
- package/dist/cjs/crypto/polyfill.js +131 -0
- package/dist/cjs/crypto/recoveryPhrase.js +242 -0
- package/dist/cjs/crypto/signatureService.js +273 -0
- package/dist/cjs/i18n/accountCategoryLabels.js +44 -0
- package/dist/cjs/i18n/accountRoleLabels.js +27 -0
- package/dist/cjs/i18n/index.js +90 -0
- package/dist/cjs/i18n/locales/ar-SA.json +282 -0
- package/dist/cjs/i18n/locales/ca-ES.json +282 -0
- package/dist/cjs/i18n/locales/de-DE.json +282 -0
- package/dist/cjs/i18n/locales/en-US.json +2161 -0
- package/dist/cjs/i18n/locales/es-ES.json +2161 -0
- package/dist/cjs/i18n/locales/fr-FR.json +282 -0
- package/dist/cjs/i18n/locales/it-IT.json +282 -0
- package/dist/cjs/i18n/locales/ja-JP.json +282 -0
- package/dist/cjs/i18n/locales/ko-KR.json +282 -0
- package/dist/cjs/i18n/locales/locales/ar-SA.json +282 -0
- package/dist/cjs/i18n/locales/locales/ca-ES.json +282 -0
- package/dist/cjs/i18n/locales/locales/de-DE.json +282 -0
- package/dist/cjs/i18n/locales/locales/en-US.json +2161 -0
- package/dist/cjs/i18n/locales/locales/es-ES.json +2161 -0
- package/dist/cjs/i18n/locales/locales/fr-FR.json +282 -0
- package/dist/cjs/i18n/locales/locales/it-IT.json +282 -0
- package/dist/cjs/i18n/locales/locales/ja-JP.json +282 -0
- package/dist/cjs/i18n/locales/locales/ko-KR.json +282 -0
- package/dist/cjs/i18n/locales/locales/pt-PT.json +282 -0
- package/dist/cjs/i18n/locales/locales/zh-CN.json +282 -0
- package/dist/cjs/i18n/locales/pt-PT.json +282 -0
- package/dist/cjs/i18n/locales/zh-CN.json +282 -0
- package/dist/cjs/i18n/reputationCategoryLabels.js +20 -0
- package/dist/cjs/i18n/trustTierLabels.js +19 -0
- package/dist/cjs/index.js +498 -0
- package/dist/cjs/inference/OxyInferenceClient.js +544 -0
- package/dist/cjs/logger/index.js +149 -0
- package/dist/cjs/mixins/OxyServices.accounts.js +555 -0
- package/dist/cjs/mixins/OxyServices.analytics.js +49 -0
- package/dist/cjs/mixins/OxyServices.appData.js +108 -0
- package/dist/cjs/mixins/OxyServices.assets.js +764 -0
- package/dist/cjs/mixins/OxyServices.auth.js +1254 -0
- package/dist/cjs/mixins/OxyServices.chains.js +73 -0
- package/dist/cjs/mixins/OxyServices.civic.js +611 -0
- package/dist/cjs/mixins/OxyServices.connectedApps.js +78 -0
- package/dist/cjs/mixins/OxyServices.contacts.js +50 -0
- package/dist/cjs/mixins/OxyServices.deviceBoot.js +191 -0
- package/dist/cjs/mixins/OxyServices.deviceTransfer.js +314 -0
- package/dist/cjs/mixins/OxyServices.devices.js +116 -0
- package/dist/cjs/mixins/OxyServices.features.js +335 -0
- package/dist/cjs/mixins/OxyServices.followGraph.js +221 -0
- package/dist/cjs/mixins/OxyServices.identity.js +461 -0
- package/dist/cjs/mixins/OxyServices.identityBackup.js +172 -0
- package/dist/cjs/mixins/OxyServices.inference.js +59 -0
- package/dist/cjs/mixins/OxyServices.language.js +125 -0
- package/dist/cjs/mixins/OxyServices.links.js +72 -0
- package/dist/cjs/mixins/OxyServices.location.js +43 -0
- package/dist/cjs/mixins/OxyServices.nodes.js +175 -0
- package/dist/cjs/mixins/OxyServices.notifications.js +63 -0
- package/dist/cjs/mixins/OxyServices.payment.js +158 -0
- package/dist/cjs/mixins/OxyServices.privacy.js +199 -0
- package/dist/cjs/mixins/OxyServices.reputation.js +289 -0
- package/dist/cjs/mixins/OxyServices.security.js +89 -0
- package/dist/cjs/mixins/OxyServices.store.js +266 -0
- package/dist/cjs/mixins/OxyServices.topics.js +137 -0
- package/dist/cjs/mixins/OxyServices.user.js +902 -0
- package/dist/cjs/mixins/OxyServices.utility.js +1098 -0
- package/dist/cjs/mixins/index.js +135 -0
- package/dist/cjs/mixins/mixinHelpers.js +53 -0
- package/dist/cjs/models/Topic.js +16 -0
- package/dist/cjs/models/interfaces.js +20 -0
- package/dist/cjs/models/session.js +2 -0
- package/dist/cjs/server/auth.js +167 -0
- package/dist/cjs/server/capabilityTicket.js +192 -0
- package/dist/cjs/server/cors.js +225 -0
- package/dist/cjs/server/index.js +97 -0
- package/dist/cjs/server/rateLimit.js +207 -0
- package/dist/cjs/server/safeFetch.js +458 -0
- package/dist/cjs/server/securityHeaders.js +377 -0
- package/dist/cjs/server/userInvalidation.js +156 -0
- package/dist/cjs/server/verifySecret.js +50 -0
- package/dist/cjs/session/SessionClient.js +865 -0
- package/dist/cjs/session/accountDelegationAccess.js +48 -0
- package/dist/cjs/session/accountDialogController.js +1131 -0
- package/dist/cjs/session/accountSwitchTargets.js +81 -0
- package/dist/cjs/session/authStateStore.js +404 -0
- package/dist/cjs/session/createSessionClient.js +32 -0
- package/dist/cjs/session/deviceDirectory.js +143 -0
- package/dist/cjs/session/deviceSwitcherRows.js +76 -0
- package/dist/cjs/session/identityPin.js +256 -0
- package/dist/cjs/session/identitySession.js +135 -0
- package/dist/cjs/session/projectSessionState.js +121 -0
- package/dist/cjs/session/refresh.js +398 -0
- package/dist/cjs/session/sessionClientHost.js +34 -0
- package/dist/cjs/session/sharedDeviceCredential.js +247 -0
- package/dist/cjs/session/socketLoader.js +55 -0
- package/dist/cjs/shared/utils/colorUtils.js +153 -0
- package/dist/cjs/shared/utils/errorUtils.js +183 -0
- package/dist/cjs/shared/utils/index.js +49 -0
- package/dist/cjs/shared/utils/networkUtils.js +183 -0
- package/dist/cjs/shared/utils/themeUtils.js +106 -0
- package/dist/cjs/utils/accountCacheSweep.js +80 -0
- package/dist/cjs/utils/accountUtils.js +140 -0
- package/dist/cjs/utils/activityId.js +6 -0
- package/dist/cjs/utils/apiUtils.js +91 -0
- package/dist/cjs/utils/asyncUtils.js +222 -0
- package/dist/cjs/utils/authHelpers.js +120 -0
- package/dist/cjs/utils/authWebUrl.js +19 -0
- package/dist/cjs/utils/avatarUtils.js +29 -0
- package/dist/cjs/utils/cache.js +235 -0
- package/dist/cjs/utils/cacheKey.js +85 -0
- package/dist/cjs/utils/coldBoot.js +122 -0
- package/dist/cjs/utils/commonsApproval.js +31 -0
- package/dist/cjs/utils/commonsDelivery.js +59 -0
- package/dist/cjs/utils/deviceManager.js +174 -0
- package/dist/cjs/utils/displayNamePolicyRanges.generated.js +60 -0
- package/dist/cjs/utils/edgeRegion.js +6 -0
- package/dist/cjs/utils/errorUtils.js +243 -0
- package/dist/cjs/utils/identityCacheSweep.js +97 -0
- package/dist/cjs/utils/languageUtils.js +224 -0
- package/dist/cjs/utils/oauthPkce.js +311 -0
- package/dist/cjs/utils/officialOrigins.js +64 -0
- package/dist/cjs/utils/oxyServiceEnvironment.js +19 -0
- package/dist/cjs/utils/platform.js +119 -0
- package/dist/cjs/utils/profileLinks.js +63 -0
- package/dist/cjs/utils/redactUrl.js +29 -0
- package/dist/cjs/utils/registrableApex.js +45 -0
- package/dist/cjs/utils/requestUtils.js +209 -0
- package/dist/cjs/utils/sessionUtils.js +181 -0
- package/dist/cjs/utils/textNormalization.js +158 -0
- package/dist/cjs/utils/userHandle.js +37 -0
- package/dist/cjs/utils/userIdentity.js +50 -0
- package/dist/cjs/utils/validationUtils.js +370 -0
- package/dist/cjs/utils/webauthnOrigin.js +51 -0
- package/dist/esm/.tsbuildinfo +1 -0
- package/dist/esm/HttpService.js +1266 -0
- package/dist/esm/OxyServices.base.js +386 -0
- package/dist/esm/OxyServices.errors.js +88 -0
- package/dist/esm/OxyServices.js +66 -0
- package/dist/esm/boot/sessionColdBoot.js +392 -0
- package/dist/esm/constants/version.js +13 -0
- package/dist/esm/crypto/aead.js +74 -0
- package/dist/esm/crypto/ecdh.js +32 -0
- package/dist/esm/crypto/identityMarker.js +248 -0
- package/dist/esm/crypto/kdf.js +36 -0
- package/dist/esm/crypto/keyManager.js +2227 -0
- package/dist/esm/crypto/polyfill.js +129 -0
- package/dist/esm/crypto/recoveryPhrase.js +238 -0
- package/dist/esm/crypto/signatureService.js +269 -0
- package/dist/esm/i18n/accountCategoryLabels.js +37 -0
- package/dist/esm/i18n/accountRoleLabels.js +20 -0
- package/dist/esm/i18n/index.js +83 -0
- package/dist/esm/i18n/locales/ar-SA.json +282 -0
- package/dist/esm/i18n/locales/ca-ES.json +282 -0
- package/dist/esm/i18n/locales/de-DE.json +282 -0
- package/dist/esm/i18n/locales/en-US.json +2161 -0
- package/dist/esm/i18n/locales/es-ES.json +2161 -0
- package/dist/esm/i18n/locales/fr-FR.json +282 -0
- package/dist/esm/i18n/locales/it-IT.json +282 -0
- package/dist/esm/i18n/locales/ja-JP.json +282 -0
- package/dist/esm/i18n/locales/ko-KR.json +282 -0
- package/dist/esm/i18n/locales/locales/ar-SA.json +282 -0
- package/dist/esm/i18n/locales/locales/ca-ES.json +282 -0
- package/dist/esm/i18n/locales/locales/de-DE.json +282 -0
- package/dist/esm/i18n/locales/locales/en-US.json +2161 -0
- package/dist/esm/i18n/locales/locales/es-ES.json +2161 -0
- package/dist/esm/i18n/locales/locales/fr-FR.json +282 -0
- package/dist/esm/i18n/locales/locales/it-IT.json +282 -0
- package/dist/esm/i18n/locales/locales/ja-JP.json +282 -0
- package/dist/esm/i18n/locales/locales/ko-KR.json +282 -0
- package/dist/esm/i18n/locales/locales/pt-PT.json +282 -0
- package/dist/esm/i18n/locales/locales/zh-CN.json +282 -0
- package/dist/esm/i18n/locales/pt-PT.json +282 -0
- package/dist/esm/i18n/locales/zh-CN.json +282 -0
- package/dist/esm/i18n/reputationCategoryLabels.js +13 -0
- package/dist/esm/i18n/trustTierLabels.js +12 -0
- package/dist/esm/index.js +254 -0
- package/dist/esm/inference/OxyInferenceClient.js +538 -0
- package/dist/esm/logger/index.js +140 -0
- package/dist/esm/mixins/OxyServices.accounts.js +543 -0
- package/dist/esm/mixins/OxyServices.analytics.js +46 -0
- package/dist/esm/mixins/OxyServices.appData.js +103 -0
- package/dist/esm/mixins/OxyServices.assets.js +761 -0
- package/dist/esm/mixins/OxyServices.auth.js +1247 -0
- package/dist/esm/mixins/OxyServices.chains.js +70 -0
- package/dist/esm/mixins/OxyServices.civic.js +605 -0
- package/dist/esm/mixins/OxyServices.connectedApps.js +75 -0
- package/dist/esm/mixins/OxyServices.contacts.js +47 -0
- package/dist/esm/mixins/OxyServices.deviceBoot.js +186 -0
- package/dist/esm/mixins/OxyServices.deviceTransfer.js +311 -0
- package/dist/esm/mixins/OxyServices.devices.js +113 -0
- package/dist/esm/mixins/OxyServices.features.js +332 -0
- package/dist/esm/mixins/OxyServices.followGraph.js +218 -0
- package/dist/esm/mixins/OxyServices.identity.js +457 -0
- package/dist/esm/mixins/OxyServices.identityBackup.js +169 -0
- package/dist/esm/mixins/OxyServices.inference.js +56 -0
- package/dist/esm/mixins/OxyServices.language.js +122 -0
- package/dist/esm/mixins/OxyServices.links.js +69 -0
- package/dist/esm/mixins/OxyServices.location.js +40 -0
- package/dist/esm/mixins/OxyServices.nodes.js +172 -0
- package/dist/esm/mixins/OxyServices.notifications.js +60 -0
- package/dist/esm/mixins/OxyServices.payment.js +155 -0
- package/dist/esm/mixins/OxyServices.privacy.js +196 -0
- package/dist/esm/mixins/OxyServices.reputation.js +286 -0
- package/dist/esm/mixins/OxyServices.security.js +86 -0
- package/dist/esm/mixins/OxyServices.store.js +263 -0
- package/dist/esm/mixins/OxyServices.topics.js +134 -0
- package/dist/esm/mixins/OxyServices.user.js +899 -0
- package/dist/esm/mixins/OxyServices.utility.js +1095 -0
- package/dist/esm/mixins/index.js +132 -0
- package/dist/esm/mixins/mixinHelpers.js +48 -0
- package/dist/esm/models/Topic.js +13 -0
- package/dist/esm/models/interfaces.js +17 -0
- package/dist/esm/models/session.js +1 -0
- package/dist/esm/server/auth.js +154 -0
- package/dist/esm/server/capabilityTicket.js +183 -0
- package/dist/esm/server/cors.js +220 -0
- package/dist/esm/server/index.js +47 -0
- package/dist/esm/server/rateLimit.js +201 -0
- package/dist/esm/server/safeFetch.js +447 -0
- package/dist/esm/server/securityHeaders.js +364 -0
- package/dist/esm/server/userInvalidation.js +152 -0
- package/dist/esm/server/verifySecret.js +47 -0
- package/dist/esm/session/SessionClient.js +861 -0
- package/dist/esm/session/accountDelegationAccess.js +45 -0
- package/dist/esm/session/accountDialogController.js +1126 -0
- package/dist/esm/session/accountSwitchTargets.js +77 -0
- package/dist/esm/session/authStateStore.js +398 -0
- package/dist/esm/session/createSessionClient.js +29 -0
- package/dist/esm/session/deviceDirectory.js +135 -0
- package/dist/esm/session/deviceSwitcherRows.js +72 -0
- package/dist/esm/session/identityPin.js +249 -0
- package/dist/esm/session/identitySession.js +131 -0
- package/dist/esm/session/projectSessionState.js +114 -0
- package/dist/esm/session/refresh.js +390 -0
- package/dist/esm/session/sessionClientHost.js +31 -0
- package/dist/esm/session/sharedDeviceCredential.js +239 -0
- package/dist/esm/session/socketLoader.js +19 -0
- package/dist/esm/shared/utils/colorUtils.js +143 -0
- package/dist/esm/shared/utils/errorUtils.js +170 -0
- package/dist/esm/shared/utils/index.js +15 -0
- package/dist/esm/shared/utils/networkUtils.js +173 -0
- package/dist/esm/shared/utils/themeUtils.js +98 -0
- package/dist/esm/utils/accountCacheSweep.js +75 -0
- package/dist/esm/utils/accountUtils.js +131 -0
- package/dist/esm/utils/activityId.js +2 -0
- package/dist/esm/utils/apiUtils.js +84 -0
- package/dist/esm/utils/asyncUtils.js +207 -0
- package/dist/esm/utils/authHelpers.js +111 -0
- package/dist/esm/utils/authWebUrl.js +16 -0
- package/dist/esm/utils/avatarUtils.js +26 -0
- package/dist/esm/utils/cache.js +227 -0
- package/dist/esm/utils/cacheKey.js +80 -0
- package/dist/esm/utils/coldBoot.js +119 -0
- package/dist/esm/utils/commonsApproval.js +27 -0
- package/dist/esm/utils/commonsDelivery.js +54 -0
- package/dist/esm/utils/deviceManager.js +170 -0
- package/dist/esm/utils/displayNamePolicyRanges.generated.js +57 -0
- package/dist/esm/utils/edgeRegion.js +2 -0
- package/dist/esm/utils/errorUtils.js +232 -0
- package/dist/esm/utils/identityCacheSweep.js +92 -0
- package/dist/esm/utils/languageUtils.js +212 -0
- package/dist/esm/utils/oauthPkce.js +297 -0
- package/dist/esm/utils/officialOrigins.js +59 -0
- package/dist/esm/utils/oxyServiceEnvironment.js +16 -0
- package/dist/esm/utils/platform.js +110 -0
- package/dist/esm/utils/profileLinks.js +60 -0
- package/dist/esm/utils/redactUrl.js +26 -0
- package/dist/esm/utils/registrableApex.js +42 -0
- package/dist/esm/utils/requestUtils.js +203 -0
- package/dist/esm/utils/sessionUtils.js +171 -0
- package/dist/esm/utils/textNormalization.js +154 -0
- package/dist/esm/utils/userHandle.js +33 -0
- package/dist/esm/utils/userIdentity.js +45 -0
- package/dist/esm/utils/validationUtils.js +350 -0
- package/dist/esm/utils/webauthnOrigin.js +48 -0
- package/dist/types/.tsbuildinfo +1 -0
- package/dist/types/HttpService.d.ts +413 -0
- package/dist/types/OxyServices.base.d.ts +187 -0
- package/dist/types/OxyServices.d.ts +139 -0
- package/dist/types/OxyServices.errors.d.ts +84 -0
- package/dist/types/boot/sessionColdBoot.d.ts +140 -0
- package/dist/types/constants/version.d.ts +13 -0
- package/dist/types/crypto/aead.d.ts +56 -0
- package/dist/types/crypto/ecdh.d.ts +29 -0
- package/dist/types/crypto/identityMarker.d.ts +94 -0
- package/dist/types/crypto/kdf.d.ts +25 -0
- package/dist/types/crypto/keyManager.d.ts +582 -0
- package/dist/types/crypto/polyfill.d.ts +25 -0
- package/dist/types/crypto/recoveryPhrase.d.ts +173 -0
- package/dist/types/crypto/signatureService.d.ts +136 -0
- package/dist/types/i18n/accountCategoryLabels.d.ts +34 -0
- package/dist/types/i18n/accountRoleLabels.d.ts +10 -0
- package/dist/types/i18n/index.d.ts +3 -0
- package/dist/types/i18n/reputationCategoryLabels.d.ts +10 -0
- package/dist/types/i18n/trustTierLabels.d.ts +9 -0
- package/dist/types/index.d.ts +145 -0
- package/dist/types/inference/OxyInferenceClient.d.ts +342 -0
- package/dist/types/logger/index.d.ts +104 -0
- package/dist/types/mixins/OxyServices.accounts.d.ts +868 -0
- package/dist/types/mixins/OxyServices.analytics.d.ts +72 -0
- package/dist/types/mixins/OxyServices.appData.d.ts +108 -0
- package/dist/types/mixins/OxyServices.assets.d.ts +329 -0
- package/dist/types/mixins/OxyServices.auth.d.ts +908 -0
- package/dist/types/mixins/OxyServices.chains.d.ts +156 -0
- package/dist/types/mixins/OxyServices.civic.d.ts +510 -0
- package/dist/types/mixins/OxyServices.connectedApps.d.ts +169 -0
- package/dist/types/mixins/OxyServices.contacts.d.ts +100 -0
- package/dist/types/mixins/OxyServices.deviceBoot.d.ts +185 -0
- package/dist/types/mixins/OxyServices.deviceTransfer.d.ts +149 -0
- package/dist/types/mixins/OxyServices.devices.d.ts +103 -0
- package/dist/types/mixins/OxyServices.features.d.ts +250 -0
- package/dist/types/mixins/OxyServices.followGraph.d.ts +223 -0
- package/dist/types/mixins/OxyServices.identity.d.ts +351 -0
- package/dist/types/mixins/OxyServices.identityBackup.d.ts +129 -0
- package/dist/types/mixins/OxyServices.inference.d.ts +95 -0
- package/dist/types/mixins/OxyServices.language.d.ts +87 -0
- package/dist/types/mixins/OxyServices.links.d.ts +104 -0
- package/dist/types/mixins/OxyServices.location.d.ts +70 -0
- package/dist/types/mixins/OxyServices.nodes.d.ts +198 -0
- package/dist/types/mixins/OxyServices.notifications.d.ts +143 -0
- package/dist/types/mixins/OxyServices.payment.d.ts +117 -0
- package/dist/types/mixins/OxyServices.privacy.d.ts +146 -0
- package/dist/types/mixins/OxyServices.reputation.d.ts +202 -0
- package/dist/types/mixins/OxyServices.security.d.ts +84 -0
- package/dist/types/mixins/OxyServices.store.d.ts +334 -0
- package/dist/types/mixins/OxyServices.topics.d.ts +110 -0
- package/dist/types/mixins/OxyServices.user.d.ts +558 -0
- package/dist/types/mixins/OxyServices.utility.d.ts +393 -0
- package/dist/types/mixins/index.d.ts +87 -0
- package/dist/types/mixins/mixinHelpers.d.ts +31 -0
- package/dist/types/models/Topic.d.ts +42 -0
- package/dist/types/models/interfaces.d.ts +675 -0
- package/dist/types/models/session.d.ts +51 -0
- package/dist/types/server/auth.d.ts +130 -0
- package/dist/types/server/capabilityTicket.d.ts +41 -0
- package/dist/types/server/cors.d.ts +98 -0
- package/dist/types/server/index.d.ts +35 -0
- package/dist/types/server/rateLimit.d.ts +40 -0
- package/dist/types/server/safeFetch.d.ts +137 -0
- package/dist/types/server/securityHeaders.d.ts +221 -0
- package/dist/types/server/userInvalidation.d.ts +114 -0
- package/dist/types/server/verifySecret.d.ts +29 -0
- package/dist/types/session/SessionClient.d.ts +418 -0
- package/dist/types/session/accountDelegationAccess.d.ts +40 -0
- package/dist/types/session/accountDialogController.d.ts +648 -0
- package/dist/types/session/accountSwitchTargets.d.ts +70 -0
- package/dist/types/session/authStateStore.d.ts +153 -0
- package/dist/types/session/createSessionClient.d.ts +30 -0
- package/dist/types/session/deviceDirectory.d.ts +182 -0
- package/dist/types/session/deviceSwitcherRows.d.ts +92 -0
- package/dist/types/session/identityPin.d.ts +87 -0
- package/dist/types/session/identitySession.d.ts +105 -0
- package/dist/types/session/projectSessionState.d.ts +74 -0
- package/dist/types/session/refresh.d.ts +179 -0
- package/dist/types/session/sessionClientHost.d.ts +18 -0
- package/dist/types/session/sharedDeviceCredential.d.ts +202 -0
- package/dist/types/session/socketLoader.d.ts +11 -0
- package/dist/types/shared/utils/colorUtils.d.ts +104 -0
- package/dist/types/shared/utils/errorUtils.d.ts +97 -0
- package/dist/types/shared/utils/index.d.ts +13 -0
- package/dist/types/shared/utils/networkUtils.d.ts +139 -0
- package/dist/types/shared/utils/themeUtils.d.ts +90 -0
- package/dist/types/utils/accountCacheSweep.d.ts +75 -0
- package/dist/types/utils/accountUtils.d.ts +100 -0
- package/dist/types/utils/activityId.d.ts +2 -0
- package/dist/types/utils/apiUtils.d.ts +95 -0
- package/dist/types/utils/asyncUtils.d.ts +62 -0
- package/dist/types/utils/authHelpers.d.ts +58 -0
- package/dist/types/utils/authWebUrl.d.ts +16 -0
- package/dist/types/utils/avatarUtils.d.ts +16 -0
- package/dist/types/utils/cache.d.ts +130 -0
- package/dist/types/utils/cacheKey.d.ts +66 -0
- package/dist/types/utils/coldBoot.d.ts +131 -0
- package/dist/types/utils/commonsApproval.d.ts +13 -0
- package/dist/types/utils/commonsDelivery.d.ts +105 -0
- package/dist/types/utils/deviceManager.d.ts +65 -0
- package/dist/types/utils/displayNamePolicyRanges.generated.d.ts +57 -0
- package/dist/types/utils/edgeRegion.d.ts +2 -0
- package/dist/types/utils/errorUtils.d.ts +125 -0
- package/dist/types/utils/identityCacheSweep.d.ts +80 -0
- package/dist/types/utils/languageUtils.d.ts +100 -0
- package/dist/types/utils/oauthPkce.d.ts +157 -0
- package/dist/types/utils/officialOrigins.d.ts +13 -0
- package/dist/types/utils/oxyServiceEnvironment.d.ts +17 -0
- package/dist/types/utils/platform.d.ts +49 -0
- package/dist/types/utils/profileLinks.d.ts +44 -0
- package/dist/types/utils/redactUrl.d.ts +17 -0
- package/dist/types/utils/registrableApex.d.ts +27 -0
- package/dist/types/utils/requestUtils.d.ts +123 -0
- package/dist/types/utils/sessionUtils.d.ts +54 -0
- package/dist/types/utils/textNormalization.d.ts +103 -0
- package/dist/types/utils/userHandle.d.ts +26 -0
- package/dist/types/utils/userIdentity.d.ts +21 -0
- package/dist/types/utils/validationUtils.d.ts +268 -0
- package/dist/types/utils/webauthnOrigin.d.ts +32 -0
- package/package.json +168 -0
- package/src/HttpService.ts +1553 -0
- package/src/OxyServices.base.ts +478 -0
- package/src/OxyServices.errors.ts +122 -0
- package/src/OxyServices.ts +172 -0
- package/src/__tests__/authSocket.test.ts +96 -0
- package/src/__tests__/httpServiceActivityId.test.ts +49 -0
- package/src/__tests__/httpServiceAuthSelfAwait.test.ts +108 -0
- package/src/__tests__/httpServiceCache.test.ts +318 -0
- package/src/__tests__/httpServiceCsrf.test.ts +235 -0
- package/src/__tests__/httpServiceFormEncoded.test.ts +142 -0
- package/src/__tests__/httpServiceQueueDeadlock.test.ts +128 -0
- package/src/__tests__/httpServiceUnwrapEnvelope.test.ts +115 -0
- package/src/__tests__/inSessionRefresh.test.ts +260 -0
- package/src/__tests__/linkedClient.test.ts +370 -0
- package/src/__tests__/parseHttpErrorBody.test.ts +116 -0
- package/src/__tests__/serverValueImportsDeclared.test.ts +120 -0
- package/src/__tests__/userIdentity.test.ts +143 -0
- package/src/boot/__tests__/sessionColdBoot.identity.test.ts +393 -0
- package/src/boot/__tests__/sessionColdBoot.sharedDevice.test.ts +325 -0
- package/src/boot/__tests__/sessionColdBoot.test.ts +570 -0
- package/src/boot/sessionColdBoot.ts +529 -0
- package/src/constants/version.ts +15 -0
- package/src/crypto/__tests__/backupMaterial.test.ts +86 -0
- package/src/crypto/__tests__/cryptoPrimitives.test.ts +226 -0
- package/src/crypto/__tests__/identityMocks.ts +125 -0
- package/src/crypto/__tests__/keyManager.atomicity.test.ts +272 -0
- 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.recoveryMnemonic.test.ts +138 -0
- package/src/crypto/__tests__/keyManager.storageMigration.test.ts +227 -0
- package/src/crypto/__tests__/keyManager.test.ts +329 -0
- package/src/crypto/__tests__/polyfill.test.ts +109 -0
- package/src/crypto/__tests__/recoveryPhrase.test.ts +61 -0
- package/src/crypto/__tests__/scopedSeed.test.ts +126 -0
- package/src/crypto/__tests__/signChallengeShared.test.ts +63 -0
- package/src/crypto/__tests__/signedRecord.test.ts +230 -0
- package/src/crypto/aead.ts +97 -0
- package/src/crypto/ecdh.ts +37 -0
- package/src/crypto/identityMarker.ts +291 -0
- package/src/crypto/kdf.ts +43 -0
- package/src/crypto/keyManager.ts +2607 -0
- package/src/crypto/polyfill.ts +143 -0
- package/src/crypto/recoveryPhrase.ts +337 -0
- package/src/crypto/signatureService.ts +350 -0
- package/src/i18n/__tests__/accountCategoryLabels.test.ts +62 -0
- package/src/i18n/__tests__/accountRoleLabels.test.ts +54 -0
- package/src/i18n/__tests__/reputationCategoryLabels.test.ts +56 -0
- package/src/i18n/__tests__/trustTierLabels.test.ts +47 -0
- package/src/i18n/accountCategoryLabels.ts +44 -0
- package/src/i18n/accountRoleLabels.ts +26 -0
- package/src/i18n/index.ts +88 -0
- package/src/i18n/locales/ar-SA.json +282 -0
- package/src/i18n/locales/ca-ES.json +282 -0
- package/src/i18n/locales/de-DE.json +282 -0
- package/src/i18n/locales/en-US.json +2161 -0
- package/src/i18n/locales/es-ES.json +2161 -0
- package/src/i18n/locales/fr-FR.json +282 -0
- package/src/i18n/locales/it-IT.json +282 -0
- package/src/i18n/locales/ja-JP.json +282 -0
- package/src/i18n/locales/ko-KR.json +282 -0
- package/src/i18n/locales/pt-PT.json +282 -0
- package/src/i18n/locales/zh-CN.json +282 -0
- package/src/i18n/reputationCategoryLabels.ts +20 -0
- package/src/i18n/trustTierLabels.ts +18 -0
- package/src/index.ts +881 -0
- package/src/inference/OxyInferenceClient.ts +888 -0
- package/src/inference/__tests__/OxyInferenceClient.test.ts +834 -0
- package/src/logger/__tests__/logger.test.ts +207 -0
- package/src/logger/index.ts +217 -0
- package/src/mixins/OxyServices.accounts.ts +1330 -0
- package/src/mixins/OxyServices.analytics.ts +53 -0
- package/src/mixins/OxyServices.appData.ts +158 -0
- package/src/mixins/OxyServices.assets.ts +861 -0
- package/src/mixins/OxyServices.auth.ts +1858 -0
- package/src/mixins/OxyServices.chains.ts +134 -0
- package/src/mixins/OxyServices.civic.ts +945 -0
- package/src/mixins/OxyServices.connectedApps.ts +174 -0
- package/src/mixins/OxyServices.contacts.ts +73 -0
- package/src/mixins/OxyServices.deviceBoot.ts +210 -0
- package/src/mixins/OxyServices.deviceTransfer.ts +393 -0
- package/src/mixins/OxyServices.devices.ts +120 -0
- package/src/mixins/OxyServices.features.ts +454 -0
- package/src/mixins/OxyServices.followGraph.ts +293 -0
- package/src/mixins/OxyServices.identity.ts +705 -0
- package/src/mixins/OxyServices.identityBackup.ts +250 -0
- package/src/mixins/OxyServices.inference.ts +57 -0
- package/src/mixins/OxyServices.language.ts +144 -0
- package/src/mixins/OxyServices.links.ts +107 -0
- package/src/mixins/OxyServices.location.ts +46 -0
- package/src/mixins/OxyServices.nodes.ts +337 -0
- package/src/mixins/OxyServices.notifications.ts +142 -0
- package/src/mixins/OxyServices.payment.ts +163 -0
- package/src/mixins/OxyServices.privacy.ts +219 -0
- package/src/mixins/OxyServices.reputation.ts +436 -0
- package/src/mixins/OxyServices.security.ts +110 -0
- package/src/mixins/OxyServices.store.ts +585 -0
- package/src/mixins/OxyServices.topics.ts +169 -0
- package/src/mixins/OxyServices.user.ts +1137 -0
- package/src/mixins/OxyServices.utility.ts +1370 -0
- package/src/mixins/__tests__/OxyServices.civic.test.ts +1095 -0
- package/src/mixins/__tests__/OxyServices.deviceBoot.test.ts +124 -0
- package/src/mixins/__tests__/OxyServices.deviceTransfer.test.ts +271 -0
- package/src/mixins/__tests__/OxyServices.identity.test.ts +376 -0
- package/src/mixins/__tests__/OxyServices.links.test.ts +154 -0
- package/src/mixins/__tests__/OxyServices.nodes.test.ts +341 -0
- package/src/mixins/__tests__/OxyServices.rotateKey.test.ts +276 -0
- package/src/mixins/__tests__/OxyServices.serviceAssetMetadata.test.ts +192 -0
- package/src/mixins/__tests__/OxyServices.serviceAssetMetadataBySha.test.ts +135 -0
- package/src/mixins/__tests__/accounts.test.ts +727 -0
- package/src/mixins/__tests__/appData.test.ts +203 -0
- package/src/mixins/__tests__/assetCredentials.test.ts +47 -0
- package/src/mixins/__tests__/assetUpload.test.ts +191 -0
- package/src/mixins/__tests__/backgroundCredential.httpIntegration.test.ts +135 -0
- package/src/mixins/__tests__/chains.test.ts +113 -0
- package/src/mixins/__tests__/commonsSignIn.test.ts +1144 -0
- package/src/mixins/__tests__/connectedApps.test.ts +123 -0
- package/src/mixins/__tests__/deviceTokenMintPinned.test.ts +113 -0
- package/src/mixins/__tests__/discoveryErrorHandling.test.ts +266 -0
- package/src/mixins/__tests__/followCacheInvalidation.test.ts +173 -0
- package/src/mixins/__tests__/followGraph.test.ts +147 -0
- package/src/mixins/__tests__/followGraphPagination.test.ts +271 -0
- package/src/mixins/__tests__/getFileDownloadUrl.test.ts +346 -0
- package/src/mixins/__tests__/getFollowStatuses.test.ts +95 -0
- package/src/mixins/__tests__/getUsersByIds.test.ts +149 -0
- package/src/mixins/__tests__/identityBackup.test.ts +315 -0
- package/src/mixins/__tests__/identityWriteCacheInvalidation.test.ts +407 -0
- package/src/mixins/__tests__/inferenceFactory.test.ts +58 -0
- package/src/mixins/__tests__/onTokensChanged.test.ts +129 -0
- package/src/mixins/__tests__/preSessionSkipAuth.test.ts +284 -0
- package/src/mixins/__tests__/privacyCacheInvalidation.test.ts +155 -0
- package/src/mixins/__tests__/reputation.test.ts +522 -0
- package/src/mixins/__tests__/serviceAuth.test.ts +832 -0
- package/src/mixins/__tests__/serviceTokenJwks.test.ts +208 -0
- package/src/mixins/__tests__/store.test.ts +304 -0
- package/src/mixins/__tests__/topics.test.ts +156 -0
- package/src/mixins/__tests__/userReadCacheBypass.test.ts +129 -0
- package/src/mixins/__tests__/userTokenAuth.test.ts +746 -0
- package/src/mixins/__tests__/verifyChallenge.test.ts +130 -0
- package/src/mixins/__tests__/verifyServiceActingAs.test.ts +167 -0
- package/src/mixins/__tests__/webauthnAuth.test.ts +205 -0
- package/src/mixins/index.ts +201 -0
- package/src/mixins/mixinHelpers.ts +69 -0
- package/src/models/Topic.ts +46 -0
- package/src/models/interfaces.ts +786 -0
- package/src/models/session.ts +55 -0
- package/src/server/__tests__/auth.test.ts +78 -0
- package/src/server/__tests__/capabilityTicket.test.ts +111 -0
- package/src/server/__tests__/cors.socket.test.ts +225 -0
- package/src/server/__tests__/cors.test.ts +158 -0
- package/src/server/__tests__/rateLimit.test.ts +257 -0
- package/src/server/__tests__/safeFetch.test.ts +232 -0
- package/src/server/__tests__/securityHeaders.test.ts +348 -0
- package/src/server/__tests__/serviceTokenAttribution.test.ts +396 -0
- package/src/server/__tests__/userInvalidation.test.ts +203 -0
- package/src/server/__tests__/verifySecret.test.ts +40 -0
- package/src/server/auth.ts +271 -0
- package/src/server/capabilityTicket.ts +258 -0
- package/src/server/cors.ts +277 -0
- package/src/server/index.ts +136 -0
- package/src/server/rateLimit.ts +313 -0
- package/src/server/safeFetch.ts +586 -0
- package/src/server/securityHeaders.ts +467 -0
- package/src/server/userInvalidation.ts +199 -0
- package/src/server/verifySecret.ts +52 -0
- package/src/session/SessionClient.ts +1001 -0
- package/src/session/__tests__/SessionClient.additive.test.ts +153 -0
- package/src/session/__tests__/SessionClient.broadcastChannel.test.ts +114 -0
- package/src/session/__tests__/SessionClient.diagnostics.test.ts +91 -0
- package/src/session/__tests__/SessionClient.directory.test.ts +688 -0
- package/src/session/__tests__/SessionClient.httpIntegration.test.ts +65 -0
- package/src/session/__tests__/SessionClient.identityPin.test.ts +175 -0
- package/src/session/__tests__/SessionClient.rest.test.ts +119 -0
- package/src/session/__tests__/SessionClient.serverEvents.test.ts +72 -0
- package/src/session/__tests__/SessionClient.socket.test.ts +202 -0
- package/src/session/__tests__/SessionClient.socketFactory.test.ts +81 -0
- package/src/session/__tests__/SessionClient.state.test.ts +78 -0
- package/src/session/__tests__/SessionClient.switchTokenOrder.test.ts +170 -0
- package/src/session/__tests__/accountDelegationAccess.test.ts +104 -0
- package/src/session/__tests__/accountDialogController.test.ts +1969 -0
- package/src/session/__tests__/accountDialogShape.test.ts +118 -0
- package/src/session/__tests__/accountSwitchTargets.test.ts +137 -0
- package/src/session/__tests__/authStateStore.test.ts +451 -0
- package/src/session/__tests__/deviceDirectory.test.ts +422 -0
- package/src/session/__tests__/deviceSwitcherRows.test.ts +223 -0
- package/src/session/__tests__/identityPin.test.ts +131 -0
- package/src/session/__tests__/identitySession.test.ts +194 -0
- package/src/session/__tests__/projectSessionState.test.ts +105 -0
- package/src/session/__tests__/refresh.identityPin.test.ts +307 -0
- package/src/session/__tests__/refresh.test.ts +443 -0
- package/src/session/__tests__/sessionIntegration.test.ts +209 -0
- package/src/session/__tests__/sharedDeviceCredential.test.ts +300 -0
- package/src/session/accountDelegationAccess.ts +72 -0
- package/src/session/accountDialogController.ts +1494 -0
- package/src/session/accountSwitchTargets.ts +93 -0
- package/src/session/authStateStore.ts +502 -0
- package/src/session/createSessionClient.ts +39 -0
- package/src/session/deviceDirectory.ts +269 -0
- package/src/session/deviceSwitcherRows.ts +145 -0
- package/src/session/identityPin.ts +310 -0
- package/src/session/identitySession.ts +206 -0
- package/src/session/projectSessionState.ts +137 -0
- package/src/session/refresh.ts +517 -0
- package/src/session/sessionClientHost.ts +39 -0
- package/src/session/sharedDeviceCredential.ts +349 -0
- package/src/session/socketLoader.ts +30 -0
- package/src/shared/utils/colorUtils.ts +155 -0
- package/src/shared/utils/errorUtils.ts +181 -0
- package/src/shared/utils/index.ts +59 -0
- package/src/shared/utils/networkUtils.ts +248 -0
- package/src/shared/utils/themeUtils.ts +115 -0
- package/src/types/buffer.d.ts +97 -0
- package/src/types/expo-crypto.d.ts +16 -0
- package/src/types/expo-secure-store.d.ts +17 -0
- package/src/utils/__tests__/accountUtils.test.ts +151 -0
- package/src/utils/__tests__/activityId.test.ts +45 -0
- package/src/utils/__tests__/asyncUtils.test.ts +187 -0
- package/src/utils/__tests__/authWebUrl.test.ts +15 -0
- package/src/utils/__tests__/cacheKey.test.ts +0 -0
- package/src/utils/__tests__/coldBoot.test.ts +496 -0
- package/src/utils/__tests__/commonsApproval.test.ts +60 -0
- package/src/utils/__tests__/identityCacheSweep.test.ts +151 -0
- package/src/utils/__tests__/languageUtils.test.ts +219 -0
- package/src/utils/__tests__/oauthPkce.test.ts +272 -0
- package/src/utils/__tests__/officialOrigins.test.ts +31 -0
- package/src/utils/__tests__/oxyServiceEnvironment.test.ts +7 -0
- package/src/utils/__tests__/profileLinks.test.ts +180 -0
- package/src/utils/__tests__/redactUrl.test.ts +33 -0
- package/src/utils/__tests__/registrableApex.test.ts +62 -0
- package/src/utils/__tests__/textNormalization.test.ts +205 -0
- package/src/utils/__tests__/validationUtils.test.ts +703 -0
- package/src/utils/__tests__/webauthnOrigin.test.ts +83 -0
- package/src/utils/accountCacheSweep.ts +93 -0
- package/src/utils/accountUtils.ts +194 -0
- package/src/utils/activityId.ts +2 -0
- package/src/utils/apiUtils.ts +137 -0
- package/src/utils/asyncUtils.ts +280 -0
- package/src/utils/authHelpers.ts +148 -0
- package/src/utils/authWebUrl.ts +17 -0
- package/src/utils/avatarUtils.ts +39 -0
- package/src/utils/cache.ts +273 -0
- package/src/utils/cacheKey.ts +93 -0
- package/src/utils/coldBoot.ts +218 -0
- package/src/utils/commonsApproval.ts +39 -0
- package/src/utils/commonsDelivery.ts +132 -0
- package/src/utils/deviceManager.ts +203 -0
- package/src/utils/displayNamePolicyRanges.generated.ts +69 -0
- package/src/utils/edgeRegion.ts +2 -0
- package/src/utils/errorUtils.ts +352 -0
- package/src/utils/identityCacheSweep.ts +104 -0
- package/src/utils/languageUtils.ts +256 -0
- package/src/utils/oauthPkce.ts +392 -0
- package/src/utils/officialOrigins.ts +62 -0
- package/src/utils/oxyServiceEnvironment.ts +17 -0
- package/src/utils/platform.ts +139 -0
- package/src/utils/profileLinks.ts +91 -0
- package/src/utils/redactUrl.ts +28 -0
- package/src/utils/registrableApex.ts +41 -0
- package/src/utils/requestUtils.ts +237 -0
- package/src/utils/sessionUtils.ts +206 -0
- package/src/utils/textNormalization.ts +162 -0
- package/src/utils/userHandle.ts +49 -0
- package/src/utils/userIdentity.ts +60 -0
- package/src/utils/validationUtils.ts +390 -0
- package/src/utils/webauthnOrigin.ts +52 -0
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TOKEN_REFRESH_LEAD_MS = void 0;
|
|
4
|
+
exports.refreshDeviceSecretArm = refreshDeviceSecretArm;
|
|
5
|
+
exports.refreshPersistedSession = refreshPersistedSession;
|
|
6
|
+
exports.createAuthRefreshHandler = createAuthRefreshHandler;
|
|
7
|
+
exports.installAuthRefreshHandler = installAuthRefreshHandler;
|
|
8
|
+
exports.startTokenRefreshScheduler = startTokenRefreshScheduler;
|
|
9
|
+
const identitySession_1 = require("./identitySession");
|
|
10
|
+
const platform_1 = require("../utils/platform");
|
|
11
|
+
const errorUtils_1 = require("../utils/errorUtils");
|
|
12
|
+
const logger_1 = require("../logger");
|
|
13
|
+
/**
|
|
14
|
+
* Lead time (ms) before access-token expiry at which the proactive scheduler
|
|
15
|
+
* re-mints. Mirrors `HttpService`'s per-request `TOKEN_REFRESH_LEAD_SECONDS`
|
|
16
|
+
* (60s) so the scheduled re-mint and the request-time preflight use the same
|
|
17
|
+
* window — the scheduler just fires it during idle/background.
|
|
18
|
+
*/
|
|
19
|
+
exports.TOKEN_REFRESH_LEAD_MS = 60000;
|
|
20
|
+
/**
|
|
21
|
+
* Max `setTimeout` delay (2^31 − 1 ms, ~24.8 days). A larger delay overflows
|
|
22
|
+
* the int32 timer field and fires IMMEDIATELY — with a long-TTL token that
|
|
23
|
+
* turns the reschedule-on-finish loop into a tight busy refresh. Clamp to it.
|
|
24
|
+
*/
|
|
25
|
+
const MAX_TIMEOUT_DELAY_MS = 2147483647;
|
|
26
|
+
/**
|
|
27
|
+
* Floor (ms) on ANY scheduled delay. An already-expired / in-lead-window token
|
|
28
|
+
* computes a non-positive `exp − now − lead`; without this floor that becomes
|
|
29
|
+
* `setTimeout(…, 0)`, and a FAILING re-mint (offline / server error) would
|
|
30
|
+
* re-arm at 0 in the finally block → a tight 100%-CPU busy loop. The floor
|
|
31
|
+
* guarantees every re-arm yields the event loop.
|
|
32
|
+
*/
|
|
33
|
+
const MIN_SCHEDULE_DELAY_MS = 1000;
|
|
34
|
+
/**
|
|
35
|
+
* Backoff schedule (ms) applied when a scheduled re-mint FAILS: first retry
|
|
36
|
+
* after {@link MIN_FAILURE_BACKOFF_MS}, doubling up to {@link MAX_FAILURE_BACKOFF_MS}.
|
|
37
|
+
* Reset to 0 on any success or token change. This is what converts the former
|
|
38
|
+
* zero-delay failure loop into a bounded, backing-off retry.
|
|
39
|
+
*/
|
|
40
|
+
const MIN_FAILURE_BACKOFF_MS = 5000;
|
|
41
|
+
const MAX_FAILURE_BACKOFF_MS = 5 * 60000;
|
|
42
|
+
/**
|
|
43
|
+
* Arm 1 — the device-secret mint, run under the owning client's PROCESS-WIDE
|
|
44
|
+
* single-flight (`httpService.runSingleFlightDeviceSecretMint`).
|
|
45
|
+
*
|
|
46
|
+
* Concurrent lanes (cold boot, the proactive scheduler, a request-time preflight,
|
|
47
|
+
* a 401 retry, the socket token transport, or a tab-focus reconcile) must not
|
|
48
|
+
* each persist a different view of the mint response. Routing EVERY lane through
|
|
49
|
+
* this one single-flight makes concurrent callers await the SAME in-flight mint
|
|
50
|
+
* and all receive its result, so the durable store converges on one credential.
|
|
51
|
+
*
|
|
52
|
+
* On success it persists `nextDeviceSecret` (read-back-verified) BEFORE planting
|
|
53
|
+
* the access token; a failed durable persist yields `persist-failed` WITHOUT
|
|
54
|
+
* planting. This function performs NO store mutation on failure — the caller
|
|
55
|
+
* applies the drop/clear policy (which differs web vs native) from the returned
|
|
56
|
+
* status.
|
|
57
|
+
*
|
|
58
|
+
* `pin` makes the mint IDENTITY-BOUND: the request carries the pinned
|
|
59
|
+
* `accountId` (so the server mints that account's token without touching
|
|
60
|
+
* `activeAccountId`), and the persisted `sessionId`/`userId` are resolved from
|
|
61
|
+
* the PINNED account entry — never from `state.activeAccountId`, whose drift is
|
|
62
|
+
* exactly what the pin exists to stop.
|
|
63
|
+
*/
|
|
64
|
+
async function refreshDeviceSecretArm(deps) {
|
|
65
|
+
const { oxy, store } = deps;
|
|
66
|
+
const pin = deps.pin ?? null;
|
|
67
|
+
return oxy.httpService.runSingleFlightDeviceSecretMint(async () => {
|
|
68
|
+
const persisted = await store.load();
|
|
69
|
+
if (!persisted?.deviceId || !persisted?.deviceSecret) {
|
|
70
|
+
return { status: 'no-secret' };
|
|
71
|
+
}
|
|
72
|
+
let mint;
|
|
73
|
+
try {
|
|
74
|
+
// Unpinned callers pass NO third argument at all, so the account-mode call
|
|
75
|
+
// shape (and therefore the request body) is untouched by this feature.
|
|
76
|
+
mint = pin
|
|
77
|
+
? await oxy.mintFromDeviceSecret(persisted.deviceId, persisted.deviceSecret, {
|
|
78
|
+
accountId: pin.accountId,
|
|
79
|
+
})
|
|
80
|
+
: await oxy.mintFromDeviceSecret(persisted.deviceId, persisted.deviceSecret);
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
if ((0, errorUtils_1.extractErrorStatus)(error) === 401) {
|
|
84
|
+
// Structural read (not `instanceof Error`): the thrown value can be a
|
|
85
|
+
// plain ApiError-shaped object or come from another realm.
|
|
86
|
+
const message = error?.message;
|
|
87
|
+
const body = typeof message === 'string' ? message : '';
|
|
88
|
+
// ONLY the server's explicit `invalid_device_secret` proves the presented
|
|
89
|
+
// secret is bad and may clear the durable device credential. `no_active_session`
|
|
90
|
+
// is an authoritative signed-out. ANY OTHER 401 — a middleware/CSRF/proxy 401,
|
|
91
|
+
// an ALB/starting-instance 401, a CORS error page, etc., all common during a
|
|
92
|
+
// deploy/restart window — is NOT proof the secret diverged: treat it as
|
|
93
|
+
// transient and KEEP the credential so a later attempt self-heals. Wiping the
|
|
94
|
+
// credential on an ambiguous 401 is what logged users out on every deploy,
|
|
95
|
+
// ecosystem-wide.
|
|
96
|
+
if (body.includes('invalid_device_secret'))
|
|
97
|
+
return { status: 'invalid-secret' };
|
|
98
|
+
if (body.includes('no_active_session'))
|
|
99
|
+
return { status: 'no-session' };
|
|
100
|
+
// A pinned mint whose account left the device set. The secret is intact —
|
|
101
|
+
// never classify this as a bad secret, or the caller would drop a healthy
|
|
102
|
+
// credential over a stale identity binding.
|
|
103
|
+
if (body.includes('account_not_on_device'))
|
|
104
|
+
return { status: 'account-not-on-device' };
|
|
105
|
+
return { status: 'transient' };
|
|
106
|
+
}
|
|
107
|
+
return { status: 'transient' };
|
|
108
|
+
}
|
|
109
|
+
// The account this session is BOUND to: the pinned one when identity-bound
|
|
110
|
+
// (the server already minted for it), else the device's active account.
|
|
111
|
+
const boundAccountId = pin ? pin.accountId : mint.state.activeAccountId;
|
|
112
|
+
const bound = mint.state.accounts.find((a) => a.accountId === boundAccountId);
|
|
113
|
+
const next = {
|
|
114
|
+
...persisted,
|
|
115
|
+
deviceId: mint.state.deviceId,
|
|
116
|
+
deviceSecret: mint.nextDeviceSecret,
|
|
117
|
+
accessToken: mint.accessToken,
|
|
118
|
+
expiresAt: mint.expiresAt,
|
|
119
|
+
...(bound ? { sessionId: bound.sessionId, userId: bound.accountId } : {}),
|
|
120
|
+
};
|
|
121
|
+
// Persist nextDeviceSecret (read-back-verified) BEFORE planting the token.
|
|
122
|
+
// A failed durable persist must NOT plant.
|
|
123
|
+
const persistedOk = await store.save(next);
|
|
124
|
+
if (!persistedOk) {
|
|
125
|
+
return { status: 'persist-failed' };
|
|
126
|
+
}
|
|
127
|
+
oxy.setTokens(mint.accessToken);
|
|
128
|
+
return {
|
|
129
|
+
status: 'ok',
|
|
130
|
+
token: mint.accessToken,
|
|
131
|
+
sessionId: next.sessionId,
|
|
132
|
+
userId: next.userId,
|
|
133
|
+
state: mint.state,
|
|
134
|
+
};
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Re-mint the persisted session and return the fresh access token, or `null`
|
|
139
|
+
* when no arm could produce one.
|
|
140
|
+
*
|
|
141
|
+
* Arm 1 (`POST /session/device/token`, via {@link refreshDeviceSecretArm}): mint
|
|
142
|
+
* from the persisted `deviceId` + `deviceSecret`. On a 401 the secret is diverged
|
|
143
|
+
* or the device has no live session: drop the secret so the mint lane stops (or
|
|
144
|
+
* clear the store on web, where there is no fallback), then fall to arm 2 on
|
|
145
|
+
* native. A transient error — or a durable-persist failure — leaves the store and
|
|
146
|
+
* returns `null` WITHOUT falling to shared-key (those are not bad-secret signals).
|
|
147
|
+
*
|
|
148
|
+
* Arm 2 (native shared-keychain): when the secret is absent or was just rejected,
|
|
149
|
+
* re-mint via `signInWithSharedIdentity` (which plants tokens). On success the
|
|
150
|
+
* recovered `{deviceId, deviceSecret, …}` is PERSISTED so the fast device-secret
|
|
151
|
+
* lane is repopulated (mirrors the cold boot's `shared-key-signin` step) — an
|
|
152
|
+
* in-session shared-key recovery must not leave the fast-lane credential empty.
|
|
153
|
+
*
|
|
154
|
+
* IDENTITY-BOUND clients (`deps.identity`) run a different arm 2: the
|
|
155
|
+
* shared-keychain lane is DISABLED (its cross-app slot may hold a different
|
|
156
|
+
* identity) and replaced by {@link establishIdentitySession}, which re-signs a
|
|
157
|
+
* challenge with the PRIMARY local key and rewrites the pin. Arm 1 is pinned.
|
|
158
|
+
*/
|
|
159
|
+
async function refreshPersistedSession(deps) {
|
|
160
|
+
const { oxy, store } = deps;
|
|
161
|
+
const identity = deps.identity ?? null;
|
|
162
|
+
// The shared keychain is never an identity-bound client's recovery path.
|
|
163
|
+
const allowSharedKeyFallback = identity ? false : (deps.allowSharedKeyFallback ?? (0, platform_1.isNative)());
|
|
164
|
+
// Resolved per call: a re-established identity session can move the pin, and a
|
|
165
|
+
// replaced/removed local key clears it (in which case arm 1 must NOT mint —
|
|
166
|
+
// an unpinned mint would adopt whatever account the device switched to).
|
|
167
|
+
const pin = identity ? await (0, identitySession_1.resolveIdentityPin)(identity) : null;
|
|
168
|
+
if (identity && !pin) {
|
|
169
|
+
return recoverIdentitySession(oxy, store, identity);
|
|
170
|
+
}
|
|
171
|
+
const arm1 = await refreshDeviceSecretArm({ oxy, store, pin });
|
|
172
|
+
switch (arm1.status) {
|
|
173
|
+
case 'ok':
|
|
174
|
+
return arm1.token;
|
|
175
|
+
case 'transient':
|
|
176
|
+
logger_1.logger.debug('Persisted deviceSecret mint failed (transient) — keeping store', { component: 'refresh', method: 'refreshPersistedSession' });
|
|
177
|
+
return null;
|
|
178
|
+
case 'persist-failed':
|
|
179
|
+
// The server rotated the secret but it did not durably persist. Do NOT fall
|
|
180
|
+
// to shared-key and do NOT plant — a later attempt re-mints (the process
|
|
181
|
+
// mirror still holds the rotated secret the server accepts) and can persist
|
|
182
|
+
// once storage recovers. Never advertise a session on an unsaved secret.
|
|
183
|
+
logger_1.logger.error('Device-secret mint rotated the secret but it could not be durably persisted — refusing to plant (a later attempt re-mints)', undefined, { component: 'refresh', method: 'refreshPersistedSession' });
|
|
184
|
+
return null;
|
|
185
|
+
case 'invalid-secret':
|
|
186
|
+
case 'no-session': {
|
|
187
|
+
// 401: secret diverged or no live session. When a key-based arm 2 can still
|
|
188
|
+
// recover (native shared key, or an identity-bound client's own primary
|
|
189
|
+
// key) drop ONLY the secret and keep the deviceId; otherwise (web) the
|
|
190
|
+
// session is over — clear the store.
|
|
191
|
+
const persisted = await store.load();
|
|
192
|
+
if (allowSharedKeyFallback || identity) {
|
|
193
|
+
if (persisted) {
|
|
194
|
+
await store.save({ ...persisted, deviceSecret: undefined });
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
await store.clear();
|
|
199
|
+
}
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
case 'account-not-on-device':
|
|
203
|
+
// The pinned account left this device's session set. The secret is healthy —
|
|
204
|
+
// leave the store untouched and let the identity arm re-establish.
|
|
205
|
+
logger_1.logger.debug('Pinned device-secret mint rejected: the pinned account is no longer on this device — re-establishing from the identity key', { component: 'refresh', method: 'refreshPersistedSession' });
|
|
206
|
+
break;
|
|
207
|
+
case 'no-secret':
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
if (identity) {
|
|
211
|
+
return recoverIdentitySession(oxy, store, identity);
|
|
212
|
+
}
|
|
213
|
+
if (allowSharedKeyFallback) {
|
|
214
|
+
try {
|
|
215
|
+
const session = await oxy.signInWithSharedIdentity();
|
|
216
|
+
if (session?.accessToken) {
|
|
217
|
+
// Repopulate the fast device-secret lane from the shared-key re-mint.
|
|
218
|
+
if (session.deviceId && session.deviceSecret) {
|
|
219
|
+
await store.save({
|
|
220
|
+
sessionId: session.sessionId,
|
|
221
|
+
userId: session.user.id,
|
|
222
|
+
deviceId: session.deviceId,
|
|
223
|
+
deviceSecret: session.deviceSecret,
|
|
224
|
+
accessToken: session.accessToken,
|
|
225
|
+
expiresAt: session.expiresAt,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
return session.accessToken;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
logger_1.logger.debug('Shared-key re-mint fallback failed', { component: 'refresh', method: 'refreshPersistedSession' }, error);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Arm 2 for an IDENTITY-BOUND client: re-establish the session from the PRIMARY
|
|
239
|
+
* local key and rewrite the pin (`establishIdentitySession` plants the token and
|
|
240
|
+
* persists the device credential itself).
|
|
241
|
+
*
|
|
242
|
+
* Returns `null` — never throws — when there is no local identity, the verify
|
|
243
|
+
* yielded no token, or the exchange failed: the caller treats that as "could not
|
|
244
|
+
* refresh", exactly like the shared-key arm. A locked keychain therefore ends
|
|
245
|
+
* signed out rather than falling back to the device's active account.
|
|
246
|
+
*/
|
|
247
|
+
async function recoverIdentitySession(oxy, store, binding) {
|
|
248
|
+
try {
|
|
249
|
+
const established = await (0, identitySession_1.establishIdentitySession)({ oxy, store, binding });
|
|
250
|
+
return established?.session.accessToken ?? null;
|
|
251
|
+
}
|
|
252
|
+
catch (error) {
|
|
253
|
+
logger_1.logger.debug('Identity-key re-sign-in failed', { component: 'refresh', method: 'recoverIdentitySession' }, error);
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Build the reactive `AuthRefreshHandler` (arm 1 + arm 2). Install it via
|
|
259
|
+
* {@link installAuthRefreshHandler} or directly on
|
|
260
|
+
* `oxy.httpService.setAuthRefreshHandler`. `HttpService` owns single-flight
|
|
261
|
+
* dedup + cooldown, so the timer, the request-time preflight, and a 401 all
|
|
262
|
+
* collapse to one network attempt.
|
|
263
|
+
*/
|
|
264
|
+
function createAuthRefreshHandler(deps) {
|
|
265
|
+
return async (_reason) => {
|
|
266
|
+
return refreshPersistedSession(deps);
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Install the unified refresh handler on the owner client's `HttpService`.
|
|
271
|
+
* Returns a disposer that removes it.
|
|
272
|
+
*/
|
|
273
|
+
function installAuthRefreshHandler(deps) {
|
|
274
|
+
deps.oxy.httpService.setAuthRefreshHandler(createAuthRefreshHandler(deps));
|
|
275
|
+
return () => {
|
|
276
|
+
deps.oxy.httpService.setAuthRefreshHandler(null);
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Start the proactive re-mint scheduler against `oxy`.
|
|
281
|
+
*
|
|
282
|
+
* Schedules a single timer to fire {@link TOKEN_REFRESH_LEAD_MS} before the
|
|
283
|
+
* current access token's `exp`, calling
|
|
284
|
+
* `oxy.httpService.refreshAccessToken('preflight')` (which runs the installed
|
|
285
|
+
* handler; deduped + cooldown-guarded). After every attempt it reschedules
|
|
286
|
+
* from the possibly-rotated token. It also reschedules whenever the token
|
|
287
|
+
* changes (a sign-out that clears the token cancels the timer) and, on web
|
|
288
|
+
* tab-focus, re-mints immediately if already inside the lead window (a
|
|
289
|
+
* long-hidden tab throttles timers, so the token can be expired on return).
|
|
290
|
+
*
|
|
291
|
+
* No-ops cleanly when there is no token or an opaque/no-`exp` token — the
|
|
292
|
+
* reactive 401 path stays the only re-mint trigger in that case. The timer is
|
|
293
|
+
* `.unref?.()`-ed so it never keeps a Node/Jest event loop alive.
|
|
294
|
+
*/
|
|
295
|
+
function startTokenRefreshScheduler(oxy) {
|
|
296
|
+
let disposed = false;
|
|
297
|
+
let timer = null;
|
|
298
|
+
// 0 = no active backoff; grows on consecutive failures, resets on success /
|
|
299
|
+
// token change. Keeps a failing re-mint from re-arming at zero delay.
|
|
300
|
+
let failureBackoffMs = 0;
|
|
301
|
+
const clearTimer = () => {
|
|
302
|
+
if (timer !== null) {
|
|
303
|
+
clearTimeout(timer);
|
|
304
|
+
timer = null;
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
/** Arm the timer for `delayMs`, flooring at {@link MIN_SCHEDULE_DELAY_MS} and capping at the int32 max. */
|
|
308
|
+
const armTimer = (delayMs) => {
|
|
309
|
+
clearTimer();
|
|
310
|
+
const clamped = Math.min(Math.max(delayMs, MIN_SCHEDULE_DELAY_MS), MAX_TIMEOUT_DELAY_MS);
|
|
311
|
+
timer = setTimeout(runRefresh, clamped);
|
|
312
|
+
// Never keep a Node/Jest event loop alive for a background re-mint timer.
|
|
313
|
+
timer.unref?.();
|
|
314
|
+
};
|
|
315
|
+
/** Schedule the next re-mint from the current token's expiry (the healthy path). */
|
|
316
|
+
const scheduleFromExpiry = () => {
|
|
317
|
+
clearTimer();
|
|
318
|
+
if (disposed || !oxy.getAccessToken()) {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
const expSeconds = oxy.getAccessTokenExpiry();
|
|
322
|
+
if (expSeconds === null) {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
armTimer(expSeconds * 1000 - Date.now() - exports.TOKEN_REFRESH_LEAD_MS);
|
|
326
|
+
};
|
|
327
|
+
const runRefresh = () => {
|
|
328
|
+
// Clear any pending timer up front so an out-of-band trigger (focus) plus
|
|
329
|
+
// a fired timer can never double-run.
|
|
330
|
+
clearTimer();
|
|
331
|
+
void oxy.httpService.refreshAccessToken('preflight')
|
|
332
|
+
.then((token) => Boolean(token))
|
|
333
|
+
.catch(() => false)
|
|
334
|
+
.then((ok) => {
|
|
335
|
+
if (disposed) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
if (ok) {
|
|
339
|
+
// Success — drop any backoff and re-arm from the rotated token's exp.
|
|
340
|
+
failureBackoffMs = 0;
|
|
341
|
+
scheduleFromExpiry();
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
// Failure — back off (never re-arm at zero) and retry.
|
|
345
|
+
failureBackoffMs =
|
|
346
|
+
failureBackoffMs === 0
|
|
347
|
+
? MIN_FAILURE_BACKOFF_MS
|
|
348
|
+
: Math.min(failureBackoffMs * 2, MAX_FAILURE_BACKOFF_MS);
|
|
349
|
+
armTimer(failureBackoffMs);
|
|
350
|
+
});
|
|
351
|
+
};
|
|
352
|
+
/** Public (re)schedule entry: a fresh token / focus signal — drop backoff and arm from expiry. */
|
|
353
|
+
const schedule = () => {
|
|
354
|
+
failureBackoffMs = 0;
|
|
355
|
+
scheduleFromExpiry();
|
|
356
|
+
};
|
|
357
|
+
const onFocus = () => {
|
|
358
|
+
if (disposed || !oxy.getAccessToken()) {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
const expSeconds = oxy.getAccessTokenExpiry();
|
|
362
|
+
if (expSeconds === null) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
const remainingMs = expSeconds * 1000 - Date.now();
|
|
366
|
+
if (remainingMs <= exports.TOKEN_REFRESH_LEAD_MS) {
|
|
367
|
+
runRefresh();
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
schedule();
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
const unsubscribeTokens = oxy.onTokensChanged(() => {
|
|
374
|
+
if (!disposed) {
|
|
375
|
+
schedule();
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
let removeFocusListener = null;
|
|
379
|
+
if (typeof document !== 'undefined') {
|
|
380
|
+
const handler = () => {
|
|
381
|
+
if (document.visibilityState === 'visible') {
|
|
382
|
+
onFocus();
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
document.addEventListener('visibilitychange', handler);
|
|
386
|
+
removeFocusListener = () => document.removeEventListener('visibilitychange', handler);
|
|
387
|
+
}
|
|
388
|
+
schedule();
|
|
389
|
+
return {
|
|
390
|
+
dispose() {
|
|
391
|
+
disposed = true;
|
|
392
|
+
clearTimer();
|
|
393
|
+
unsubscribeTokens();
|
|
394
|
+
removeFocusListener?.();
|
|
395
|
+
removeFocusListener = null;
|
|
396
|
+
},
|
|
397
|
+
};
|
|
398
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSessionClientHost = createSessionClientHost;
|
|
4
|
+
/**
|
|
5
|
+
* Thin `SessionClientHost` adapter over an `OxyServices` instance.
|
|
6
|
+
*
|
|
7
|
+
* `SessionClient` is host-agnostic: it only needs a REST + token surface.
|
|
8
|
+
* `OxyServices` already exposes all of that except `getCurrentAccountId`,
|
|
9
|
+
* which has no direct equivalent — the adapter holds a mutable ref set by
|
|
10
|
+
* the caller (`OxyContext` in `@oxy.so/services`) via `setCurrentAccountId`.
|
|
11
|
+
*
|
|
12
|
+
* Shared here (rather than duplicated per consumer) because it is entirely
|
|
13
|
+
* platform-agnostic: every method it calls exists identically on
|
|
14
|
+
* `OxyServices` regardless of host (web, Expo/RN, Node).
|
|
15
|
+
*/
|
|
16
|
+
function createSessionClientHost(oxyServices) {
|
|
17
|
+
let currentAccountId = null;
|
|
18
|
+
let deviceCredential = null;
|
|
19
|
+
return {
|
|
20
|
+
makeRequest: (method, url, data, options) => oxyServices.makeRequest(method, url, data, options),
|
|
21
|
+
getBaseURL: () => oxyServices.getBaseURL(),
|
|
22
|
+
getAccessToken: () => oxyServices.getAccessToken(),
|
|
23
|
+
getDeviceCredential: () => deviceCredential,
|
|
24
|
+
onTokensChanged: (listener) => oxyServices.onTokensChanged(listener),
|
|
25
|
+
setTokens: (accessToken) => oxyServices.setTokens(accessToken),
|
|
26
|
+
getCurrentAccountId: () => currentAccountId,
|
|
27
|
+
setCurrentAccountId: (id) => {
|
|
28
|
+
currentAccountId = id;
|
|
29
|
+
},
|
|
30
|
+
setDeviceCredential: (credential) => {
|
|
31
|
+
deviceCredential = credential;
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The shared native DeviceSession credential — one device, one session, many apps.
|
|
4
|
+
*
|
|
5
|
+
* ## What this is, and what it deliberately is NOT
|
|
6
|
+
*
|
|
7
|
+
* Two different secrets can make a native Oxy app boot signed in, and conflating
|
|
8
|
+
* them is the bug this module exists to end:
|
|
9
|
+
*
|
|
10
|
+
* Commons private identity key → identity and signed approval ONLY. It is
|
|
11
|
+
* self-custody, irreplaceable, and must never
|
|
12
|
+
* become the general app session transport.
|
|
13
|
+
* Shared DeviceSession credential → THIS module. An ordinary `deviceId` +
|
|
14
|
+
* `deviceSecret` pair that restores ordinary
|
|
15
|
+
* official apps, follows the device's active
|
|
16
|
+
* context, and is individually rotatable and
|
|
17
|
+
* revocable server-side.
|
|
18
|
+
*
|
|
19
|
+
* An ordinary app needs the second one. Handing it the first — which is what the
|
|
20
|
+
* `shared-key-signin` lane does today — gives every sibling app the ability to
|
|
21
|
+
* sign as the user's cryptographic identity to obtain something as mundane as a
|
|
22
|
+
* session. That lane stays as a recovery/compatibility path; this one supersedes
|
|
23
|
+
* it for the ordinary case.
|
|
24
|
+
*
|
|
25
|
+
* ## Why sharing one credential is safe against the server
|
|
26
|
+
*
|
|
27
|
+
* `POST /session/device/token` does NOT rotate: the response echoes the presented
|
|
28
|
+
* secret back as `nextDeviceSecret` precisely so several first-party apps sharing
|
|
29
|
+
* one `DeviceSession` can refresh concurrently without invalidating one another.
|
|
30
|
+
* So N apps holding one credential is a supported server state, not a race — and
|
|
31
|
+
* because they then share ONE `DeviceSession`, they automatically share its
|
|
32
|
+
* `activeContextId` and its token-free `session_state` broadcasts.
|
|
33
|
+
*
|
|
34
|
+
* ## The one safety rule everything here is built around
|
|
35
|
+
*
|
|
36
|
+
* A read that FAILED and a slot that is EMPTY must never be the same value. The
|
|
37
|
+
* empty answer authorises writes (seed the slot); the failed answer must
|
|
38
|
+
* authorise nothing. {@link SharedDeviceCredentialRead} keeps them apart at the
|
|
39
|
+
* type level, and every decision below fails closed on anything that is not a
|
|
40
|
+
* positive `absent`/`present`.
|
|
41
|
+
*
|
|
42
|
+
* Platform-agnostic: the actual keychain / keystore access is injected as a
|
|
43
|
+
* {@link SharedDeviceCredentialStore} by `@oxy.so/services`. ESM-safe, no
|
|
44
|
+
* `require()`, no react/react-native/expo imports.
|
|
45
|
+
*/
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.readLocalDeviceCredential = readLocalDeviceCredential;
|
|
48
|
+
exports.normalizeSharedDeviceSessionRead = normalizeSharedDeviceSessionRead;
|
|
49
|
+
exports.decideSharedDeviceJoin = decideSharedDeviceJoin;
|
|
50
|
+
exports.decideSharedDevicePublish = decideSharedDevicePublish;
|
|
51
|
+
exports.publishProvenDeviceCredential = publishProvenDeviceCredential;
|
|
52
|
+
exports.createSharedMirroringAuthStateStore = createSharedMirroringAuthStateStore;
|
|
53
|
+
const logger_1 = require("../logger");
|
|
54
|
+
/** The usable `{deviceId, deviceSecret}` pair in a persisted state, or null. */
|
|
55
|
+
function readLocalDeviceCredential(state) {
|
|
56
|
+
if (!state?.deviceId || !state.deviceSecret) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
return { deviceId: state.deviceId, deviceSecret: state.deviceSecret };
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Narrow an UNTRUSTED bridge payload into a {@link SharedDeviceCredentialRead}.
|
|
63
|
+
*
|
|
64
|
+
* Anything unrecognised resolves to `unavailable`, never `absent`. A native
|
|
65
|
+
* module returning a shape this build does not understand (an older app in the
|
|
66
|
+
* signing group, a partially-applied upgrade) means we do not KNOW whether the
|
|
67
|
+
* device has a shared session — and "do not know" must never authorise a write
|
|
68
|
+
* that would overwrite one.
|
|
69
|
+
*/
|
|
70
|
+
function normalizeSharedDeviceSessionRead(raw) {
|
|
71
|
+
if (!raw || typeof raw !== 'object') {
|
|
72
|
+
return { state: 'unavailable', cause: new Error('shared device session bridge returned a non-object') };
|
|
73
|
+
}
|
|
74
|
+
const payload = raw;
|
|
75
|
+
if (payload.status === 'absent') {
|
|
76
|
+
return { state: 'absent' };
|
|
77
|
+
}
|
|
78
|
+
if (payload.status === 'present') {
|
|
79
|
+
const deviceId = payload.deviceId;
|
|
80
|
+
const deviceSecret = payload.deviceSecret;
|
|
81
|
+
if (typeof deviceId === 'string' &&
|
|
82
|
+
deviceId.length > 0 &&
|
|
83
|
+
typeof deviceSecret === 'string' &&
|
|
84
|
+
deviceSecret.length > 0) {
|
|
85
|
+
return { state: 'present', credential: { deviceId, deviceSecret } };
|
|
86
|
+
}
|
|
87
|
+
// A `present` verdict whose payload is incomplete is a broken slot, not an
|
|
88
|
+
// empty one. Reporting `absent` here would let the next successful sign-in
|
|
89
|
+
// overwrite whatever is really in there.
|
|
90
|
+
return {
|
|
91
|
+
state: 'unavailable',
|
|
92
|
+
cause: new Error('shared device session bridge reported `present` with an incomplete credential'),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
if (payload.status === 'unavailable') {
|
|
96
|
+
const reason = typeof payload.reason === 'string' ? payload.reason : 'unknown';
|
|
97
|
+
return { state: 'unavailable', cause: new Error(`shared device session slot unavailable: ${reason}`) };
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
state: 'unavailable',
|
|
101
|
+
cause: new Error(`shared device session bridge returned an unrecognised status: ${String(payload.status)}`),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Should this app adopt the shared credential? Pure.
|
|
106
|
+
*
|
|
107
|
+
* Adoption happens in exactly ONE case: the shared slot holds a credential and
|
|
108
|
+
* this app has none of its own. That single rule delivers the product
|
|
109
|
+
* requirement — a newly installed official app joins the device's existing
|
|
110
|
+
* session without another QR — while making the two failure modes that matter
|
|
111
|
+
* unreachable:
|
|
112
|
+
*
|
|
113
|
+
* - An app that is already signed in is NEVER moved onto another credential, so
|
|
114
|
+
* "no user is signed out merely because one app updates first" holds by
|
|
115
|
+
* construction, in both upgrade directions.
|
|
116
|
+
* - A failed read never looks like an empty slot, so a locked keychain resolves
|
|
117
|
+
* to "keep what I have" rather than "this is a fresh device".
|
|
118
|
+
*
|
|
119
|
+
* The cost is stated plainly: two apps that each already own a DIFFERENT device
|
|
120
|
+
* session stay on their own until one of them loses its credential. Converging
|
|
121
|
+
* them would mean signing one of them out or a server-side device merge, and
|
|
122
|
+
* neither is something a boot path may do silently.
|
|
123
|
+
*/
|
|
124
|
+
function decideSharedDeviceJoin(local, shared) {
|
|
125
|
+
if (readLocalDeviceCredential(local) !== null) {
|
|
126
|
+
return { action: 'skip', reason: 'local-credential-present' };
|
|
127
|
+
}
|
|
128
|
+
switch (shared.state) {
|
|
129
|
+
case 'present':
|
|
130
|
+
return { action: 'adopt', credential: shared.credential };
|
|
131
|
+
case 'absent':
|
|
132
|
+
return { action: 'skip', reason: 'shared-empty' };
|
|
133
|
+
case 'unavailable':
|
|
134
|
+
return { action: 'skip', reason: 'shared-unreadable' };
|
|
135
|
+
case 'unsupported':
|
|
136
|
+
return { action: 'skip', reason: 'shared-unsupported' };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Should this app write the credential it just proved into the shared slot? Pure.
|
|
141
|
+
*
|
|
142
|
+
* `proven` means the server accepted it moments ago — a successful sign-in or a
|
|
143
|
+
* successful mint. Only a proven credential is ever published, so the slot can
|
|
144
|
+
* never be seeded with something no app could use.
|
|
145
|
+
*
|
|
146
|
+
* A slot already held by a DIFFERENT `deviceId` is left alone. Overwriting it
|
|
147
|
+
* would silently migrate every other app on this device onto our session at their
|
|
148
|
+
* next cold boot — a real, user-visible change of who they are signed in as, and
|
|
149
|
+
* not something a background persist may decide.
|
|
150
|
+
*/
|
|
151
|
+
function decideSharedDevicePublish(proven, shared) {
|
|
152
|
+
switch (shared.state) {
|
|
153
|
+
case 'unsupported':
|
|
154
|
+
return { action: 'skip', reason: 'shared-unsupported' };
|
|
155
|
+
case 'unavailable':
|
|
156
|
+
return { action: 'skip', reason: 'shared-unreadable' };
|
|
157
|
+
case 'absent':
|
|
158
|
+
return { action: 'publish' };
|
|
159
|
+
case 'present': {
|
|
160
|
+
if (shared.credential.deviceId !== proven.deviceId) {
|
|
161
|
+
return { action: 'skip', reason: 'owned-by-another-device' };
|
|
162
|
+
}
|
|
163
|
+
if (shared.credential.deviceSecret === proven.deviceSecret) {
|
|
164
|
+
return { action: 'skip', reason: 'already-current' };
|
|
165
|
+
}
|
|
166
|
+
// Same device, newer secret. Sign-in rotates the secret (the mint does
|
|
167
|
+
// not), so the just-proven one is the credential a fresh install should
|
|
168
|
+
// join with.
|
|
169
|
+
return { action: 'publish' };
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Read the shared slot, apply {@link decideSharedDevicePublish}, and write when
|
|
175
|
+
* it says so. Best-effort by contract: the caller's own durable credential is
|
|
176
|
+
* already persisted, so a failure here only means a future install will have to
|
|
177
|
+
* sign in interactively.
|
|
178
|
+
*/
|
|
179
|
+
async function publishProvenDeviceCredential(deps) {
|
|
180
|
+
const read = await deps.shared.read();
|
|
181
|
+
const decision = decideSharedDevicePublish(deps.credential, read);
|
|
182
|
+
if (decision.action === 'skip') {
|
|
183
|
+
if (decision.reason === 'shared-unreadable') {
|
|
184
|
+
logger_1.logger.debug('shared device credential slot unreadable — not publishing (an unreadable slot is never an empty one)', { component: 'sharedDeviceCredential', method: 'publishProvenDeviceCredential' });
|
|
185
|
+
}
|
|
186
|
+
return { status: 'skipped', reason: decision.reason };
|
|
187
|
+
}
|
|
188
|
+
const published = await deps.shared.publish(deps.credential);
|
|
189
|
+
return published ? { status: 'published' } : { status: 'publish-failed' };
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Wrap a platform {@link AuthStateStore} so that every durable credential it
|
|
193
|
+
* persists is ALSO mirrored into the shared slot.
|
|
194
|
+
*
|
|
195
|
+
* Writes mirror automatically; reads do NOT adopt. That split is deliberate:
|
|
196
|
+
*
|
|
197
|
+
* - Mirroring on write is the right place because `save()` is where a proven
|
|
198
|
+
* credential lands, on every lane there is — interactive sign-in, the cold
|
|
199
|
+
* boot mint, the refresh scheduler, the 401 re-mint, shared-key recovery. One
|
|
200
|
+
* seam, no lane left out, and no new call site to forget.
|
|
201
|
+
* - Adopting on read would hide a change of WHO THIS APP IS SIGNED IN AS inside
|
|
202
|
+
* a storage primitive, and would run on every `load()`. Adoption is an
|
|
203
|
+
* explicit, once-per-boot cold-boot step instead (`shared-device-adopt`).
|
|
204
|
+
*
|
|
205
|
+
* `clear()` deliberately does NOT clear the shared slot. This app signing out is
|
|
206
|
+
* not authority over the device-wide join point: other apps may still be signed
|
|
207
|
+
* in on that same credential, and once the server session is really gone the
|
|
208
|
+
* credential mints `no_active_session` for everyone anyway.
|
|
209
|
+
*/
|
|
210
|
+
function createSharedMirroringAuthStateStore(deps) {
|
|
211
|
+
const { local, shared } = deps;
|
|
212
|
+
// Process-local memo of the credential we last observed the shared slot to
|
|
213
|
+
// hold. Purely an optimization to keep the refresh scheduler from re-reading
|
|
214
|
+
// the keychain every mint; a slot wiped out from under us is re-seeded on the
|
|
215
|
+
// next launch rather than mid-process.
|
|
216
|
+
let mirrored = null;
|
|
217
|
+
return {
|
|
218
|
+
load: () => local.load(),
|
|
219
|
+
clear: () => local.clear(),
|
|
220
|
+
save: async (state) => {
|
|
221
|
+
// The durable local write is the contract this store owes its caller —
|
|
222
|
+
// run it first and report ITS result, unchanged. The mirror is additive.
|
|
223
|
+
const durablePersisted = await local.save(state);
|
|
224
|
+
const credential = readLocalDeviceCredential(state);
|
|
225
|
+
if (!credential) {
|
|
226
|
+
return durablePersisted;
|
|
227
|
+
}
|
|
228
|
+
if (mirrored !== null &&
|
|
229
|
+
mirrored.deviceId === credential.deviceId &&
|
|
230
|
+
mirrored.deviceSecret === credential.deviceSecret) {
|
|
231
|
+
return durablePersisted;
|
|
232
|
+
}
|
|
233
|
+
try {
|
|
234
|
+
const outcome = await publishProvenDeviceCredential({ shared, credential });
|
|
235
|
+
if (outcome.status === 'published' || (outcome.status === 'skipped' && outcome.reason === 'already-current')) {
|
|
236
|
+
mirrored = credential;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
// A store implementation is contractually non-throwing, but a mirror
|
|
241
|
+
// failure must never take down the durable write that already succeeded.
|
|
242
|
+
logger_1.logger.debug('mirroring the device credential into the shared slot threw — the local credential is unaffected', { component: 'sharedDeviceCredential', method: 'save' }, error);
|
|
243
|
+
}
|
|
244
|
+
return durablePersisted;
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
}
|