@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,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two questions an account chooser asks of an account-graph node: is this
|
|
3
|
+
* kind switchable at all, and may THIS caller become it.
|
|
4
|
+
*
|
|
5
|
+
* Pure and I/O-free. They live here rather than beside the surfaces that ask
|
|
6
|
+
* them because there is more than one such surface — the Console's workspace
|
|
7
|
+
* tree, the Accounts app's managed-account rows — and a second enumeration of
|
|
8
|
+
* switch targets is a second place for the rule to go missing, which is
|
|
9
|
+
* precisely how the Console went on offering `channel` rows after the rule
|
|
10
|
+
* learned to drop them.
|
|
11
|
+
*
|
|
12
|
+
* The DEVICE switcher no longer asks anything here: it renders the server's
|
|
13
|
+
* device directory (ADR 0002, `deviceDirectory.ts`), whose `available` field is
|
|
14
|
+
* the server's own authorization verdict. These predicates answer a different
|
|
15
|
+
* question — one about the caller's account GRAPH, which is a list of accounts
|
|
16
|
+
* to manage, not a list of identities the device can become.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { isOperatorSwitchTargetKind } from '@oxy.so/contracts';
|
|
20
|
+
import type {
|
|
21
|
+
AccountRelationship,
|
|
22
|
+
AccountKind,
|
|
23
|
+
AccountMember,
|
|
24
|
+
} from '../mixins/OxyServices.accounts';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Whether the caller can BECOME this account — the one question every account
|
|
28
|
+
* switcher asks, answered here so no surface has to re-derive it.
|
|
29
|
+
*
|
|
30
|
+
* Two independent grounds, either of which suffices:
|
|
31
|
+
*
|
|
32
|
+
* - **It is already the caller's own identity** (`relationship: 'self'`).
|
|
33
|
+
* `GET /accounts` resolves its caller through `resolveOperatorId`, so `self`
|
|
34
|
+
* is the HUMAN operator's personal account even while they are operating an
|
|
35
|
+
* org — never the operated account. Kind is irrelevant on this ground: the
|
|
36
|
+
* caller IS that account, so returning to it asks the server for nothing.
|
|
37
|
+
* - **The server will mint a session for it** — `isOperatorSwitchTargetKind(kind)`
|
|
38
|
+
* is the exact predicate `POST /accounts/:id/switch` enforces, so a row
|
|
39
|
+
* offered on this ground is never a dead button.
|
|
40
|
+
*
|
|
41
|
+
* `isOperatorSwitchTargetKind` ALONE is not this question, and reaching for it
|
|
42
|
+
* directly is the mistake this function exists to prevent: it is false for
|
|
43
|
+
* `personal` as well as `channel`, so a switcher gated on it alone renders an
|
|
44
|
+
* empty list rather than a filtered one. Equally, `kind !== 'channel'` is not
|
|
45
|
+
* this question either — it silently admits every kind invented after it was
|
|
46
|
+
* written, which is the same trap the kind predicates were introduced to close
|
|
47
|
+
* on the server.
|
|
48
|
+
*
|
|
49
|
+
* And it is the OPERATOR predicate, never `isDelegatedActAsEligibleKind`. The
|
|
50
|
+
* two differ only on `bot`, so the delegated one produces a list that looks
|
|
51
|
+
* right and contains one row too many — a seat inside the identity that exists
|
|
52
|
+
* to act while nobody is present. That is not a hypothetical: it put a live bot
|
|
53
|
+
* session on a person's own device, beside their personal login.
|
|
54
|
+
*
|
|
55
|
+
* Takes a structural subset rather than a whole {@link AccountNode} so a caller
|
|
56
|
+
* holding an already-projected row can ask it too.
|
|
57
|
+
*/
|
|
58
|
+
export function isSwitchTargetAccount(
|
|
59
|
+
node: { kind?: AccountKind | null; relationship?: AccountRelationship },
|
|
60
|
+
): boolean {
|
|
61
|
+
return node.relationship === 'self' || isOperatorSwitchTargetKind(node.kind);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Whether the caller may switch INTO this account — the server-side
|
|
66
|
+
* `account:act_as` gate plus the structural {@link isSwitchTargetAccount} rule.
|
|
67
|
+
*
|
|
68
|
+
* `relationship: 'self'` always passes (returning to the caller's own personal
|
|
69
|
+
* account). Every other ground requires a switch-eligible kind AND
|
|
70
|
+
* `account:act_as` in the resolved membership permissions. When permissions are
|
|
71
|
+
* absent but the relationship is `owner`, the owner baseline is assumed — the
|
|
72
|
+
* API always resolves effective permissions for owned accounts, but test
|
|
73
|
+
* fixtures and stale rows may omit the membership blob.
|
|
74
|
+
*/
|
|
75
|
+
export function canSwitchIntoAccount(
|
|
76
|
+
node: {
|
|
77
|
+
kind?: AccountKind | null;
|
|
78
|
+
relationship?: AccountRelationship;
|
|
79
|
+
callerMembership?: AccountMember | null;
|
|
80
|
+
},
|
|
81
|
+
): boolean {
|
|
82
|
+
if (node.relationship === 'self') {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
if (!isSwitchTargetAccount(node)) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
const permissions = node.callerMembership?.permissions;
|
|
89
|
+
if (permissions) {
|
|
90
|
+
return permissions.includes('account:act_as');
|
|
91
|
+
}
|
|
92
|
+
return node.relationship === 'owner';
|
|
93
|
+
}
|
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persisted auth state — ONE shape, web + native.
|
|
3
|
+
*
|
|
4
|
+
* The zero-cookie device transport persists the device credential per ORIGIN so
|
|
5
|
+
* a reload restores the session locally without a redirect: `deviceId` +
|
|
6
|
+
* `deviceSecret` mint a fresh access token via `POST /session/device/token`.
|
|
7
|
+
* This module is the storage seam: a tiny `load / save / clear` interface plus
|
|
8
|
+
* platform factories, so the cold boot (`sessionColdBoot`) and the unified re-mint
|
|
9
|
+
* handler (`refresh.ts`) never touch a platform storage API directly.
|
|
10
|
+
*
|
|
11
|
+
* Platform-agnostic — the native factory takes an INJECTED key/value store
|
|
12
|
+
* (`@oxy.so/services` passes a SecureStore-backed adapter) so `@oxy.so/core`
|
|
13
|
+
* never imports `expo-secure-store`. The web factory is self-contained
|
|
14
|
+
* (`localStorage`) and degrades to in-memory when storage is unavailable
|
|
15
|
+
* (sandboxed iframe `SecurityError`, private-mode quota, SSR).
|
|
16
|
+
*
|
|
17
|
+
* ESM-safe (no `require()`).
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { logger } from '../logger';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The persisted session credential set for a single origin.
|
|
24
|
+
*
|
|
25
|
+
* `sessionId` + `userId` identify the owning device session and active account.
|
|
26
|
+
* `deviceId` + `deviceSecret` are the zero-cookie mint credential: the client
|
|
27
|
+
* presents BOTH at `POST /session/device/token` — the secret is the proof, the
|
|
28
|
+
* deviceId selects the device doc. The secret is rotated in-use: the mint returns
|
|
29
|
+
* `nextDeviceSecret`, which the cold boot / re-mint handler persist BEFORE
|
|
30
|
+
* planting the minted access token (multi-tab anti-loss).
|
|
31
|
+
*
|
|
32
|
+
* `accessToken` + `expiresAt` are OPTIONAL warm-boot fields. Persisting them lets
|
|
33
|
+
* the cold boot plant a still-valid access token on the very first paint WITHOUT
|
|
34
|
+
* a blocking mint round-trip — the proactive scheduler then rotates it in the
|
|
35
|
+
* background. They are a strict optimization: the store is fully functional (via
|
|
36
|
+
* `deviceSecret`) when they are absent or stale, and the access token is
|
|
37
|
+
* short-lived, so persisting it adds no exposure the already-persisted
|
|
38
|
+
* `deviceSecret` does not.
|
|
39
|
+
*/
|
|
40
|
+
export interface PersistedAuthState {
|
|
41
|
+
sessionId: string;
|
|
42
|
+
userId: string;
|
|
43
|
+
/**
|
|
44
|
+
* The stable device identifier this session is bound to (zero-cookie
|
|
45
|
+
* transport). Persisted alongside {@link deviceSecret} because the
|
|
46
|
+
* `POST /session/device/token` mint presents BOTH. Sourced from every login
|
|
47
|
+
* lane (password / 2FA / QR claim / challenge verify) via the response's
|
|
48
|
+
* `deviceId`.
|
|
49
|
+
*/
|
|
50
|
+
deviceId?: string;
|
|
51
|
+
/**
|
|
52
|
+
* The rotating device secret (zero-cookie transport). Possession of it mints a
|
|
53
|
+
* short access token for the device's active account via
|
|
54
|
+
* `POST /session/device/token`. Rotated in-use.
|
|
55
|
+
*/
|
|
56
|
+
deviceSecret?: string;
|
|
57
|
+
/** Optional warm-boot access token (short-lived; see interface docs). */
|
|
58
|
+
accessToken?: string;
|
|
59
|
+
/** Optional warm-boot access-token expiry, ISO-8601. */
|
|
60
|
+
expiresAt?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The storage seam consumed by the cold boot and the re-mint handler. Async
|
|
65
|
+
* throughout so one interface fits both synchronous web `localStorage` and
|
|
66
|
+
* asynchronous native SecureStore/AsyncStorage. `clear()` (sign-out) wipes the
|
|
67
|
+
* per-sign-in credential blob.
|
|
68
|
+
*/
|
|
69
|
+
export interface AuthStateStore {
|
|
70
|
+
load(): Promise<PersistedAuthState | null>;
|
|
71
|
+
/**
|
|
72
|
+
* Persist the credential blob and report whether it durably landed.
|
|
73
|
+
*
|
|
74
|
+
* Resolves `true` when the state is retained consistent with this store's
|
|
75
|
+
* durability guarantee — a durable backing whose read-back matched, or a
|
|
76
|
+
* degraded/in-memory store that held it in memory. Resolves `false` when a
|
|
77
|
+
* DURABLE backing was expected but the write did NOT land (read-back mismatch
|
|
78
|
+
* or a thrown write); the in-memory mirror still keeps the session live for
|
|
79
|
+
* this process, but it will be lost on reload.
|
|
80
|
+
*
|
|
81
|
+
* A lane persisting a ROTATED device secret (the mint's `nextDeviceSecret`)
|
|
82
|
+
* MUST treat `false` as fatal for that mint: it must NOT plant/advertise a
|
|
83
|
+
* session built on a secret that will not survive a reload.
|
|
84
|
+
*/
|
|
85
|
+
save(state: PersistedAuthState): Promise<boolean>;
|
|
86
|
+
clear(): Promise<void>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The minimal async key/value surface a native store must provide. Matches
|
|
91
|
+
* both `expo-secure-store` (wrapped) and `@react-native-async-storage`.
|
|
92
|
+
*/
|
|
93
|
+
export interface NativeKeyValueStorage {
|
|
94
|
+
getItem(key: string): Promise<string | null>;
|
|
95
|
+
setItem(key: string, value: string): Promise<void>;
|
|
96
|
+
removeItem(key: string): Promise<void>;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Versioned DURABLE storage key. Holds ONLY the small, re-mint-critical fields
|
|
101
|
+
* (`sessionId`, `userId`, `deviceId`, `deviceSecret`) — never the large JWT
|
|
102
|
+
* `accessToken`. Keeping this blob small (<2KB) matters on Android
|
|
103
|
+
* `expo-secure-store`, whose backing store can silently fail to persist an
|
|
104
|
+
* oversize value; bundling the token here previously took the mint credential
|
|
105
|
+
* down with it on every write, losing the session on cold restart.
|
|
106
|
+
*
|
|
107
|
+
* The `.v1` suffix lets a future shape change ship a `.v2` key without reading a
|
|
108
|
+
* stale/incompatible `.v1` blob. Distinct from the `oxy_shared_*` keychain keys
|
|
109
|
+
* in `KeyManager`, so it never collides.
|
|
110
|
+
*
|
|
111
|
+
* BACK-COMPAT: pre-split builds wrote the WHOLE state (including `accessToken` /
|
|
112
|
+
* `expiresAt`) into this single key. `load()` still reads those token fields
|
|
113
|
+
* from here when the warm key ({@link AUTH_STATE_TOKEN_STORAGE_KEY}) is absent,
|
|
114
|
+
* so upgrading users are not signed out; the next `save()` splits them apart.
|
|
115
|
+
*/
|
|
116
|
+
export const AUTH_STATE_STORAGE_KEY = 'oxy.auth.v1';
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Versioned BEST-EFFORT warm-token storage key. Holds the short-lived
|
|
120
|
+
* `{ accessToken, expiresAt }` pair only. Its write is genuinely non-fatal — a
|
|
121
|
+
* failure (quota / oversize keychain value) is swallowed because the session is
|
|
122
|
+
* fully re-mintable from the durable `deviceSecret`. Kept separate from
|
|
123
|
+
* {@link AUTH_STATE_STORAGE_KEY} so a failed token write can NEVER abort or
|
|
124
|
+
* corrupt the durable credential write.
|
|
125
|
+
*/
|
|
126
|
+
export const AUTH_STATE_TOKEN_STORAGE_KEY = 'oxy.auth.token.v1';
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Parse + shape-validate a stored blob. Returns `null` for anything that is
|
|
130
|
+
* not a well-formed {@link PersistedAuthState} (absent, malformed JSON, wrong
|
|
131
|
+
* types) so a corrupt entry degrades to "signed out" rather than throwing.
|
|
132
|
+
*/
|
|
133
|
+
function deserialize(raw: string | null): PersistedAuthState | null {
|
|
134
|
+
if (!raw) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
let parsed: unknown;
|
|
138
|
+
try {
|
|
139
|
+
parsed = JSON.parse(raw);
|
|
140
|
+
} catch {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
const candidate = parsed as Record<string, unknown>;
|
|
147
|
+
const hasDeviceCredential =
|
|
148
|
+
typeof candidate.deviceId === 'string' &&
|
|
149
|
+
candidate.deviceId.length > 0 &&
|
|
150
|
+
typeof candidate.deviceSecret === 'string' &&
|
|
151
|
+
candidate.deviceSecret.length > 0;
|
|
152
|
+
|
|
153
|
+
const hasSessionIdentity =
|
|
154
|
+
typeof candidate.sessionId === 'string' &&
|
|
155
|
+
typeof candidate.userId === 'string' &&
|
|
156
|
+
candidate.sessionId.length > 0 &&
|
|
157
|
+
candidate.userId.length > 0;
|
|
158
|
+
|
|
159
|
+
// Device-only bootstrap (post join, pre-sign-in): durable credential without session.
|
|
160
|
+
if (!hasSessionIdentity && !hasDeviceCredential) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const state: PersistedAuthState = {
|
|
165
|
+
sessionId: hasSessionIdentity ? (candidate.sessionId as string) : '',
|
|
166
|
+
userId: hasSessionIdentity ? (candidate.userId as string) : '',
|
|
167
|
+
};
|
|
168
|
+
if (typeof candidate.deviceId === 'string' && candidate.deviceId.length > 0) {
|
|
169
|
+
state.deviceId = candidate.deviceId;
|
|
170
|
+
}
|
|
171
|
+
if (typeof candidate.deviceSecret === 'string' && candidate.deviceSecret.length > 0) {
|
|
172
|
+
state.deviceSecret = candidate.deviceSecret;
|
|
173
|
+
}
|
|
174
|
+
if (typeof candidate.accessToken === 'string' && candidate.accessToken.length > 0) {
|
|
175
|
+
state.accessToken = candidate.accessToken;
|
|
176
|
+
}
|
|
177
|
+
if (typeof candidate.expiresAt === 'string' && candidate.expiresAt.length > 0) {
|
|
178
|
+
state.expiresAt = candidate.expiresAt;
|
|
179
|
+
}
|
|
180
|
+
return state;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** The parsed warm-token blob: the short-lived optimization fields only. */
|
|
184
|
+
interface WarmToken {
|
|
185
|
+
accessToken?: string;
|
|
186
|
+
expiresAt?: string;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Parse the best-effort warm-token blob. Returns `null` for anything not a
|
|
191
|
+
* well-formed object so a corrupt warm entry simply forgoes the warm-boot
|
|
192
|
+
* optimization (the durable credential re-mints a fresh token).
|
|
193
|
+
*/
|
|
194
|
+
function parseWarmToken(raw: string | null): WarmToken | null {
|
|
195
|
+
if (!raw) {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
let parsed: unknown;
|
|
199
|
+
try {
|
|
200
|
+
parsed = JSON.parse(raw);
|
|
201
|
+
} catch {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
const candidate = parsed as Record<string, unknown>;
|
|
208
|
+
const warm: WarmToken = {};
|
|
209
|
+
if (typeof candidate.accessToken === 'string' && candidate.accessToken.length > 0) {
|
|
210
|
+
warm.accessToken = candidate.accessToken;
|
|
211
|
+
}
|
|
212
|
+
if (typeof candidate.expiresAt === 'string' && candidate.expiresAt.length > 0) {
|
|
213
|
+
warm.expiresAt = candidate.expiresAt;
|
|
214
|
+
}
|
|
215
|
+
return warm;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** Serialize ONLY the small, re-mint-critical fields for the durable key. */
|
|
219
|
+
function serializeDurable(state: PersistedAuthState): string {
|
|
220
|
+
const durable: Record<string, string> = {
|
|
221
|
+
sessionId: state.sessionId,
|
|
222
|
+
userId: state.userId,
|
|
223
|
+
};
|
|
224
|
+
if (state.deviceId) {
|
|
225
|
+
durable.deviceId = state.deviceId;
|
|
226
|
+
}
|
|
227
|
+
if (state.deviceSecret) {
|
|
228
|
+
durable.deviceSecret = state.deviceSecret;
|
|
229
|
+
}
|
|
230
|
+
return JSON.stringify(durable);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Serialize the warm-token blob, or `null` when there is no token to persist
|
|
235
|
+
* (so the caller clears the warm key rather than writing an empty object).
|
|
236
|
+
*/
|
|
237
|
+
function serializeWarmToken(state: PersistedAuthState): string | null {
|
|
238
|
+
const warm: WarmToken = {};
|
|
239
|
+
if (state.accessToken) {
|
|
240
|
+
warm.accessToken = state.accessToken;
|
|
241
|
+
}
|
|
242
|
+
if (state.expiresAt) {
|
|
243
|
+
warm.expiresAt = state.expiresAt;
|
|
244
|
+
}
|
|
245
|
+
if (!warm.accessToken && !warm.expiresAt) {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
return JSON.stringify(warm);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Compose the unchanged {@link PersistedAuthState} return shape from the two
|
|
253
|
+
* on-disk keys. `accessToken` / `expiresAt` come from the warm key when it is
|
|
254
|
+
* present; when the warm key is ABSENT the token fields fall back to whatever
|
|
255
|
+
* the durable blob carried — the pre-split combined `oxy.auth.v1` blob (BACK-COMPAT).
|
|
256
|
+
*/
|
|
257
|
+
function composeState(durableRaw: string | null, warmRaw: string | null): PersistedAuthState | null {
|
|
258
|
+
const state = deserialize(durableRaw);
|
|
259
|
+
if (!state) {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
if (warmRaw !== null) {
|
|
263
|
+
// New split layout: the warm key is authoritative for the token fields.
|
|
264
|
+
// Drop anything the durable blob may have carried, then overlay the warm
|
|
265
|
+
// values (a warm key with no token → the session simply has no warm token).
|
|
266
|
+
delete state.accessToken;
|
|
267
|
+
delete state.expiresAt;
|
|
268
|
+
const warm = parseWarmToken(warmRaw);
|
|
269
|
+
if (warm?.accessToken) {
|
|
270
|
+
state.accessToken = warm.accessToken;
|
|
271
|
+
}
|
|
272
|
+
if (warm?.expiresAt) {
|
|
273
|
+
state.expiresAt = warm.expiresAt;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
// else: BACK-COMPAT — the warm key is absent, so `deserialize` already applied
|
|
277
|
+
// any `accessToken` / `expiresAt` from the old combined blob.
|
|
278
|
+
return state;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* A process-lifetime, in-memory {@link AuthStateStore}. Used directly for
|
|
283
|
+
* tests/SSR and as the degraded fallback of the web store when `localStorage`
|
|
284
|
+
* is unreachable. Not durable across reloads — that is acceptable for the
|
|
285
|
+
* fallback because the alternative (throwing) would break cold boot entirely.
|
|
286
|
+
*/
|
|
287
|
+
export function createMemoryAuthStateStore(): AuthStateStore {
|
|
288
|
+
let current: PersistedAuthState | null = null;
|
|
289
|
+
return {
|
|
290
|
+
load: async () => current,
|
|
291
|
+
save: async (state) => {
|
|
292
|
+
// Memory IS this store's durability backing — the write always lands.
|
|
293
|
+
current = state;
|
|
294
|
+
return true;
|
|
295
|
+
},
|
|
296
|
+
clear: async () => {
|
|
297
|
+
current = null;
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Read the ambient `localStorage`, tolerating the case where merely ACCESSING
|
|
304
|
+
* `window.localStorage` throws. In a sandboxed / cross-origin iframe the getter
|
|
305
|
+
* itself raises `SecurityError` (not the method calls), so the access must be
|
|
306
|
+
* inside the try. Returns `null` when storage is unavailable.
|
|
307
|
+
*/
|
|
308
|
+
function safeGetLocalStorage(): Storage | null {
|
|
309
|
+
try {
|
|
310
|
+
if (typeof globalThis === 'undefined') {
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
const store = (globalThis as { localStorage?: Storage }).localStorage;
|
|
314
|
+
return store ?? null;
|
|
315
|
+
} catch {
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* A `localStorage`-backed {@link AuthStateStore} split across the durable
|
|
322
|
+
* {@link AUTH_STATE_STORAGE_KEY} (mint credential) and the best-effort
|
|
323
|
+
* {@link AUTH_STATE_TOKEN_STORAGE_KEY} (warm access token).
|
|
324
|
+
*
|
|
325
|
+
* Resilience:
|
|
326
|
+
* - If `localStorage` is unreachable (sandboxed-iframe `SecurityError`, SSR),
|
|
327
|
+
* the whole store degrades to an in-memory {@link createMemoryAuthStateStore}
|
|
328
|
+
* for this page's lifetime — never throws on construction.
|
|
329
|
+
* - Individual `getItem`/`setItem`/`removeItem` are each wrapped: a read that
|
|
330
|
+
* throws yields `null`; a write that throws (quota, private mode) is
|
|
331
|
+
* swallowed. The re-mint lane treats a failed persist as "no durable state"
|
|
332
|
+
* rather than crashing.
|
|
333
|
+
*/
|
|
334
|
+
export function createWebAuthStateStore(): AuthStateStore {
|
|
335
|
+
const storage = safeGetLocalStorage();
|
|
336
|
+
if (!storage) {
|
|
337
|
+
return createMemoryAuthStateStore();
|
|
338
|
+
}
|
|
339
|
+
// In-memory mirror so a FAILED persist (quota / private mode / locked store)
|
|
340
|
+
// does not silently lose the session for this page's lifetime. `undefined`
|
|
341
|
+
// means "never written this session" → fall back to storage; any set value
|
|
342
|
+
// (including `null` after clear) is authoritative and preferred over storage.
|
|
343
|
+
let sessionMirror: PersistedAuthState | null | undefined;
|
|
344
|
+
return {
|
|
345
|
+
load: async () => {
|
|
346
|
+
if (sessionMirror !== undefined) {
|
|
347
|
+
return sessionMirror;
|
|
348
|
+
}
|
|
349
|
+
try {
|
|
350
|
+
return composeState(
|
|
351
|
+
storage.getItem(AUTH_STATE_STORAGE_KEY),
|
|
352
|
+
storage.getItem(AUTH_STATE_TOKEN_STORAGE_KEY),
|
|
353
|
+
);
|
|
354
|
+
} catch {
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
save: async (state) => {
|
|
359
|
+
sessionMirror = state; // mirror FIRST — authoritative even if persist fails
|
|
360
|
+
// Durable credential FIRST, then VERIFY it landed. The in-memory mirror
|
|
361
|
+
// keeps the session live for this page, but a failed DURABLE write means
|
|
362
|
+
// the mint credential will NOT survive a reload — surface it, never swallow.
|
|
363
|
+
let durablePersisted = false;
|
|
364
|
+
try {
|
|
365
|
+
const durableJson = serializeDurable(state);
|
|
366
|
+
storage.setItem(AUTH_STATE_STORAGE_KEY, durableJson);
|
|
367
|
+
durablePersisted = storage.getItem(AUTH_STATE_STORAGE_KEY) === durableJson;
|
|
368
|
+
if (!durablePersisted) {
|
|
369
|
+
logger.error(
|
|
370
|
+
'[authStateStore] durable credential read-back mismatch after save — the device credential did not persist; the session survives this process via the in-memory mirror but will be lost on reload',
|
|
371
|
+
undefined,
|
|
372
|
+
{ component: 'authStateStore' },
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
} catch (error) {
|
|
376
|
+
logger.error(
|
|
377
|
+
'[authStateStore] durable credential persist threw — the device credential did not persist; the session survives this process via the in-memory mirror but will be lost on reload',
|
|
378
|
+
error,
|
|
379
|
+
{ component: 'authStateStore' },
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
// Warm token AFTER, best-effort. Its failure is genuinely non-fatal (the
|
|
383
|
+
// durable credential re-mints a fresh token) and must never abort or
|
|
384
|
+
// corrupt the durable write above.
|
|
385
|
+
try {
|
|
386
|
+
const warmJson = serializeWarmToken(state);
|
|
387
|
+
if (warmJson) {
|
|
388
|
+
storage.setItem(AUTH_STATE_TOKEN_STORAGE_KEY, warmJson);
|
|
389
|
+
} else {
|
|
390
|
+
storage.removeItem(AUTH_STATE_TOKEN_STORAGE_KEY);
|
|
391
|
+
}
|
|
392
|
+
} catch {
|
|
393
|
+
// Quota / private-mode / disabled storage — non-fatal warm-boot loss only.
|
|
394
|
+
}
|
|
395
|
+
// Report ONLY the durable-credential landing; the warm-token outcome above
|
|
396
|
+
// is intentionally excluded (it is a best-effort optimization).
|
|
397
|
+
return durablePersisted;
|
|
398
|
+
},
|
|
399
|
+
clear: async () => {
|
|
400
|
+
sessionMirror = null;
|
|
401
|
+
try {
|
|
402
|
+
storage.removeItem(AUTH_STATE_STORAGE_KEY);
|
|
403
|
+
} catch {
|
|
404
|
+
// Non-fatal — see save().
|
|
405
|
+
}
|
|
406
|
+
try {
|
|
407
|
+
storage.removeItem(AUTH_STATE_TOKEN_STORAGE_KEY);
|
|
408
|
+
} catch {
|
|
409
|
+
// Non-fatal — see save().
|
|
410
|
+
}
|
|
411
|
+
},
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* A native {@link AuthStateStore} over an injected async key/value store.
|
|
417
|
+
*
|
|
418
|
+
* `@oxy.so/core` never imports `expo-secure-store`; `@oxy.so/services` constructs
|
|
419
|
+
* the SecureStore-backed adapter and passes it here. Persistence is split across
|
|
420
|
+
* the durable {@link AUTH_STATE_STORAGE_KEY} (mint credential) and the
|
|
421
|
+
* best-effort {@link AUTH_STATE_TOKEN_STORAGE_KEY} (warm access token) — the
|
|
422
|
+
* durable write is read-back-verified and its failure surfaced (not swallowed),
|
|
423
|
+
* while the warm-token write and all reads degrade gracefully exactly like the
|
|
424
|
+
* web store.
|
|
425
|
+
*/
|
|
426
|
+
export function createNativeAuthStateStore(storage: NativeKeyValueStorage): AuthStateStore {
|
|
427
|
+
// Same in-memory mirror as the web store — a locked/failed SecureStore write
|
|
428
|
+
// must not silently lose the session for the app's lifetime.
|
|
429
|
+
let sessionMirror: PersistedAuthState | null | undefined;
|
|
430
|
+
return {
|
|
431
|
+
load: async () => {
|
|
432
|
+
if (sessionMirror !== undefined) {
|
|
433
|
+
return sessionMirror;
|
|
434
|
+
}
|
|
435
|
+
try {
|
|
436
|
+
const [durableRaw, warmRaw] = await Promise.all([
|
|
437
|
+
storage.getItem(AUTH_STATE_STORAGE_KEY),
|
|
438
|
+
storage.getItem(AUTH_STATE_TOKEN_STORAGE_KEY),
|
|
439
|
+
]);
|
|
440
|
+
return composeState(durableRaw, warmRaw);
|
|
441
|
+
} catch {
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
save: async (state) => {
|
|
446
|
+
sessionMirror = state;
|
|
447
|
+
// Durable credential FIRST, then VERIFY. On Android SecureStore an
|
|
448
|
+
// oversize/failed write can resolve WITHOUT throwing, so a read-back is the
|
|
449
|
+
// only reliable proof. The mirror keeps the session live for this app run,
|
|
450
|
+
// but a failed DURABLE write means the mint credential will NOT survive a
|
|
451
|
+
// cold restart — surface it, never swallow.
|
|
452
|
+
let durablePersisted = false;
|
|
453
|
+
try {
|
|
454
|
+
const durableJson = serializeDurable(state);
|
|
455
|
+
await storage.setItem(AUTH_STATE_STORAGE_KEY, durableJson);
|
|
456
|
+
durablePersisted = (await storage.getItem(AUTH_STATE_STORAGE_KEY)) === durableJson;
|
|
457
|
+
if (!durablePersisted) {
|
|
458
|
+
logger.error(
|
|
459
|
+
'[authStateStore] durable credential read-back mismatch after save — the device credential did not persist (likely oversize SecureStore value); the session survives this app run via the in-memory mirror but will be lost on cold restart',
|
|
460
|
+
undefined,
|
|
461
|
+
{ component: 'authStateStore' },
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
} catch (error) {
|
|
465
|
+
logger.error(
|
|
466
|
+
'[authStateStore] durable credential persist threw — the device credential did not persist; the session survives this app run via the in-memory mirror but will be lost on cold restart',
|
|
467
|
+
error,
|
|
468
|
+
{ component: 'authStateStore' },
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
// Warm token AFTER, best-effort. Its failure is genuinely non-fatal (the
|
|
472
|
+
// durable credential re-mints a fresh token) and must never abort or
|
|
473
|
+
// corrupt the durable write above.
|
|
474
|
+
try {
|
|
475
|
+
const warmJson = serializeWarmToken(state);
|
|
476
|
+
if (warmJson) {
|
|
477
|
+
await storage.setItem(AUTH_STATE_TOKEN_STORAGE_KEY, warmJson);
|
|
478
|
+
} else {
|
|
479
|
+
await storage.removeItem(AUTH_STATE_TOKEN_STORAGE_KEY);
|
|
480
|
+
}
|
|
481
|
+
} catch {
|
|
482
|
+
// Locked / oversize keychain — non-fatal warm-boot loss only.
|
|
483
|
+
}
|
|
484
|
+
// Report ONLY the durable-credential landing; the warm-token outcome above
|
|
485
|
+
// is intentionally excluded (it is a best-effort optimization).
|
|
486
|
+
return durablePersisted;
|
|
487
|
+
},
|
|
488
|
+
clear: async () => {
|
|
489
|
+
sessionMirror = null;
|
|
490
|
+
try {
|
|
491
|
+
await storage.removeItem(AUTH_STATE_STORAGE_KEY);
|
|
492
|
+
} catch {
|
|
493
|
+
// Non-fatal.
|
|
494
|
+
}
|
|
495
|
+
try {
|
|
496
|
+
await storage.removeItem(AUTH_STATE_TOKEN_STORAGE_KEY);
|
|
497
|
+
} catch {
|
|
498
|
+
// Non-fatal.
|
|
499
|
+
}
|
|
500
|
+
},
|
|
501
|
+
};
|
|
502
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { OxyServices } from '../OxyServices';
|
|
2
|
+
import { SessionClient, type TokenTransport } from './SessionClient';
|
|
3
|
+
import type { SocketIOFactory } from './socketLoader';
|
|
4
|
+
import { createSessionClientHost } from './sessionClientHost';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Wires a `SessionClient` over the given `OxyServices` instance: builds the
|
|
8
|
+
* `SessionClientHost` adapter and passes it through together with a
|
|
9
|
+
* caller-supplied `TokenTransport`.
|
|
10
|
+
*
|
|
11
|
+
* The transport is a required parameter (not constructed here) because it is
|
|
12
|
+
* the one piece of this integration that is NOT platform-agnostic: `services`
|
|
13
|
+
* branches native (shared-keychain sign-in) vs. web (silent sign-in), while
|
|
14
|
+
* `auth-sdk` is web-only. Each consumer builds its own transport and passes
|
|
15
|
+
* it in; this factory only wires the platform-agnostic parts (host + client)
|
|
16
|
+
* so neither consumer re-implements them.
|
|
17
|
+
*
|
|
18
|
+
* The host is returned alongside the client (not just the client) so the
|
|
19
|
+
* caller can call `host.setCurrentAccountId(...)` as the active account
|
|
20
|
+
* changes.
|
|
21
|
+
*
|
|
22
|
+
* `socketFactory` is the statically-injected `socket.io-client` `io` export.
|
|
23
|
+
* Consumers that bundle socket.io-client as a real dependency pass it so
|
|
24
|
+
* realtime sync never depends on core's lazy dynamic import of a bare
|
|
25
|
+
* specifier (bundler-fragile in Metro/Expo-web and Vite against the published
|
|
26
|
+
* dist). When omitted, the client falls back to the lazy loader.
|
|
27
|
+
*/
|
|
28
|
+
export function createSessionClient(
|
|
29
|
+
oxyServices: OxyServices,
|
|
30
|
+
transport: TokenTransport,
|
|
31
|
+
socketFactory?: SocketIOFactory,
|
|
32
|
+
): {
|
|
33
|
+
client: SessionClient;
|
|
34
|
+
host: ReturnType<typeof createSessionClientHost>;
|
|
35
|
+
} {
|
|
36
|
+
const host = createSessionClientHost(oxyServices);
|
|
37
|
+
const client = new SessionClient(host, { transport, socketFactory });
|
|
38
|
+
return { client, host };
|
|
39
|
+
}
|