@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,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locale utilities for OxyServices.
|
|
3
|
+
*
|
|
4
|
+
* The Oxy platform models account languages as full BCP-47 locales
|
|
5
|
+
* (`language-REGION`, e.g. `es-ES`, `es-MX`, `pt-BR`). Region is significant:
|
|
6
|
+
* "Spanish (Spain)" is a different locale than "Spanish (Mexico)". A user's
|
|
7
|
+
* account locales live on `User.languages` as an ordered list with the PRIMARY
|
|
8
|
+
* (UI) locale first — there is no singular `language` field.
|
|
9
|
+
*
|
|
10
|
+
* This module is the single source of truth for the supported-locale catalog
|
|
11
|
+
* and every locale operation the SDK exposes: parsing base subtags, canonical
|
|
12
|
+
* normalization, validation, metadata lookup, and resolving a user's locales.
|
|
13
|
+
* It is pure and side-effect free.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { User } from '../models/interfaces';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A supported BCP-47 locale in the Oxy catalog.
|
|
20
|
+
*/
|
|
21
|
+
export interface SupportedLanguage {
|
|
22
|
+
/** Canonical BCP-47 locale tag, `language-REGION` (e.g. `'es-ES'`). */
|
|
23
|
+
code: string;
|
|
24
|
+
/** ISO 639-1 base language subtag, lowercased (e.g. `'es'`). */
|
|
25
|
+
language: string;
|
|
26
|
+
/** ISO 3166-1 alpha-2 region subtag, uppercased (e.g. `'ES'`). */
|
|
27
|
+
region: string;
|
|
28
|
+
/** English display name, `'Language (Region)'` (e.g. `'Spanish (Spain)'`). */
|
|
29
|
+
name: string;
|
|
30
|
+
/** Endonym — the name as written in the locale itself (e.g. `'Español (España)'`). */
|
|
31
|
+
nativeName: string;
|
|
32
|
+
/** `true` when the locale is written right-to-left. Omitted for LTR locales. */
|
|
33
|
+
rtl?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The supported-locale catalog. Ordered by prominence for display; the order
|
|
38
|
+
* is not otherwise significant. Every entry is a full `language-REGION` tag,
|
|
39
|
+
* with the base subtag lowercased and the region uppercased.
|
|
40
|
+
*/
|
|
41
|
+
export const SUPPORTED_LANGUAGES: readonly SupportedLanguage[] = [
|
|
42
|
+
// English
|
|
43
|
+
{ code: 'en-US', language: 'en', region: 'US', name: 'English (United States)', nativeName: 'English (United States)' },
|
|
44
|
+
{ code: 'en-GB', language: 'en', region: 'GB', name: 'English (United Kingdom)', nativeName: 'English (United Kingdom)' },
|
|
45
|
+
{ code: 'en-AU', language: 'en', region: 'AU', name: 'English (Australia)', nativeName: 'English (Australia)' },
|
|
46
|
+
{ code: 'en-CA', language: 'en', region: 'CA', name: 'English (Canada)', nativeName: 'English (Canada)' },
|
|
47
|
+
{ code: 'en-IN', language: 'en', region: 'IN', name: 'English (India)', nativeName: 'English (India)' },
|
|
48
|
+
|
|
49
|
+
// Spanish
|
|
50
|
+
{ code: 'es-ES', language: 'es', region: 'ES', name: 'Spanish (Spain)', nativeName: 'Español (España)' },
|
|
51
|
+
{ code: 'es-MX', language: 'es', region: 'MX', name: 'Spanish (Mexico)', nativeName: 'Español (México)' },
|
|
52
|
+
{ code: 'es-US', language: 'es', region: 'US', name: 'Spanish (United States)', nativeName: 'Español (Estados Unidos)' },
|
|
53
|
+
{ code: 'es-AR', language: 'es', region: 'AR', name: 'Spanish (Argentina)', nativeName: 'Español (Argentina)' },
|
|
54
|
+
{ code: 'es-CO', language: 'es', region: 'CO', name: 'Spanish (Colombia)', nativeName: 'Español (Colombia)' },
|
|
55
|
+
|
|
56
|
+
// Portuguese
|
|
57
|
+
{ code: 'pt-BR', language: 'pt', region: 'BR', name: 'Portuguese (Brazil)', nativeName: 'Português (Brasil)' },
|
|
58
|
+
{ code: 'pt-PT', language: 'pt', region: 'PT', name: 'Portuguese (Portugal)', nativeName: 'Português (Portugal)' },
|
|
59
|
+
|
|
60
|
+
// French
|
|
61
|
+
{ code: 'fr-FR', language: 'fr', region: 'FR', name: 'French (France)', nativeName: 'Français (France)' },
|
|
62
|
+
{ code: 'fr-CA', language: 'fr', region: 'CA', name: 'French (Canada)', nativeName: 'Français (Canada)' },
|
|
63
|
+
|
|
64
|
+
// German
|
|
65
|
+
{ code: 'de-DE', language: 'de', region: 'DE', name: 'German (Germany)', nativeName: 'Deutsch (Deutschland)' },
|
|
66
|
+
{ code: 'de-AT', language: 'de', region: 'AT', name: 'German (Austria)', nativeName: 'Deutsch (Österreich)' },
|
|
67
|
+
{ code: 'de-CH', language: 'de', region: 'CH', name: 'German (Switzerland)', nativeName: 'Deutsch (Schweiz)' },
|
|
68
|
+
|
|
69
|
+
// Italian
|
|
70
|
+
{ code: 'it-IT', language: 'it', region: 'IT', name: 'Italian (Italy)', nativeName: 'Italiano (Italia)' },
|
|
71
|
+
|
|
72
|
+
// Dutch
|
|
73
|
+
{ code: 'nl-NL', language: 'nl', region: 'NL', name: 'Dutch (Netherlands)', nativeName: 'Nederlands (Nederland)' },
|
|
74
|
+
{ code: 'nl-BE', language: 'nl', region: 'BE', name: 'Dutch (Belgium)', nativeName: 'Nederlands (België)' },
|
|
75
|
+
|
|
76
|
+
// Nordic
|
|
77
|
+
{ code: 'sv-SE', language: 'sv', region: 'SE', name: 'Swedish (Sweden)', nativeName: 'Svenska (Sverige)' },
|
|
78
|
+
{ code: 'nb-NO', language: 'nb', region: 'NO', name: 'Norwegian Bokmål (Norway)', nativeName: 'Norsk bokmål (Norge)' },
|
|
79
|
+
{ code: 'da-DK', language: 'da', region: 'DK', name: 'Danish (Denmark)', nativeName: 'Dansk (Danmark)' },
|
|
80
|
+
{ code: 'fi-FI', language: 'fi', region: 'FI', name: 'Finnish (Finland)', nativeName: 'Suomi (Suomi)' },
|
|
81
|
+
|
|
82
|
+
// Central & Eastern Europe
|
|
83
|
+
{ code: 'pl-PL', language: 'pl', region: 'PL', name: 'Polish (Poland)', nativeName: 'Polski (Polska)' },
|
|
84
|
+
{ code: 'cs-CZ', language: 'cs', region: 'CZ', name: 'Czech (Czechia)', nativeName: 'Čeština (Česko)' },
|
|
85
|
+
{ code: 'sk-SK', language: 'sk', region: 'SK', name: 'Slovak (Slovakia)', nativeName: 'Slovenčina (Slovensko)' },
|
|
86
|
+
{ code: 'hu-HU', language: 'hu', region: 'HU', name: 'Hungarian (Hungary)', nativeName: 'Magyar (Magyarország)' },
|
|
87
|
+
{ code: 'ro-RO', language: 'ro', region: 'RO', name: 'Romanian (Romania)', nativeName: 'Română (România)' },
|
|
88
|
+
{ code: 'bg-BG', language: 'bg', region: 'BG', name: 'Bulgarian (Bulgaria)', nativeName: 'Български (България)' },
|
|
89
|
+
{ code: 'hr-HR', language: 'hr', region: 'HR', name: 'Croatian (Croatia)', nativeName: 'Hrvatski (Hrvatska)' },
|
|
90
|
+
{ code: 'sr-RS', language: 'sr', region: 'RS', name: 'Serbian (Serbia)', nativeName: 'Српски (Србија)' },
|
|
91
|
+
{ code: 'sl-SI', language: 'sl', region: 'SI', name: 'Slovenian (Slovenia)', nativeName: 'Slovenščina (Slovenija)' },
|
|
92
|
+
{ code: 'lt-LT', language: 'lt', region: 'LT', name: 'Lithuanian (Lithuania)', nativeName: 'Lietuvių (Lietuva)' },
|
|
93
|
+
{ code: 'lv-LV', language: 'lv', region: 'LV', name: 'Latvian (Latvia)', nativeName: 'Latviešu (Latvija)' },
|
|
94
|
+
{ code: 'et-EE', language: 'et', region: 'EE', name: 'Estonian (Estonia)', nativeName: 'Eesti (Eesti)' },
|
|
95
|
+
{ code: 'uk-UA', language: 'uk', region: 'UA', name: 'Ukrainian (Ukraine)', nativeName: 'Українська (Україна)' },
|
|
96
|
+
{ code: 'ru-RU', language: 'ru', region: 'RU', name: 'Russian (Russia)', nativeName: 'Русский (Россия)' },
|
|
97
|
+
{ code: 'el-GR', language: 'el', region: 'GR', name: 'Greek (Greece)', nativeName: 'Ελληνικά (Ελλάδα)' },
|
|
98
|
+
|
|
99
|
+
// Turkish
|
|
100
|
+
{ code: 'tr-TR', language: 'tr', region: 'TR', name: 'Turkish (Türkiye)', nativeName: 'Türkçe (Türkiye)' },
|
|
101
|
+
|
|
102
|
+
// Catalan
|
|
103
|
+
{ code: 'ca-ES', language: 'ca', region: 'ES', name: 'Catalan (Spain)', nativeName: 'Català (Espanya)' },
|
|
104
|
+
|
|
105
|
+
// East, South & Southeast Asia
|
|
106
|
+
{ code: 'ja-JP', language: 'ja', region: 'JP', name: 'Japanese (Japan)', nativeName: '日本語 (日本)' },
|
|
107
|
+
{ code: 'ko-KR', language: 'ko', region: 'KR', name: 'Korean (South Korea)', nativeName: '한국어 (대한민국)' },
|
|
108
|
+
{ code: 'zh-CN', language: 'zh', region: 'CN', name: 'Chinese (Simplified, China)', nativeName: '中文 (中国)' },
|
|
109
|
+
{ code: 'zh-TW', language: 'zh', region: 'TW', name: 'Chinese (Traditional, Taiwan)', nativeName: '中文 (台灣)' },
|
|
110
|
+
{ code: 'zh-HK', language: 'zh', region: 'HK', name: 'Chinese (Traditional, Hong Kong)', nativeName: '中文 (香港)' },
|
|
111
|
+
{ code: 'hi-IN', language: 'hi', region: 'IN', name: 'Hindi (India)', nativeName: 'हिन्दी (भारत)' },
|
|
112
|
+
{ code: 'bn-BD', language: 'bn', region: 'BD', name: 'Bengali (Bangladesh)', nativeName: 'বাংলা (বাংলাদেশ)' },
|
|
113
|
+
{ code: 'id-ID', language: 'id', region: 'ID', name: 'Indonesian (Indonesia)', nativeName: 'Bahasa Indonesia (Indonesia)' },
|
|
114
|
+
{ code: 'ms-MY', language: 'ms', region: 'MY', name: 'Malay (Malaysia)', nativeName: 'Bahasa Melayu (Malaysia)' },
|
|
115
|
+
{ code: 'th-TH', language: 'th', region: 'TH', name: 'Thai (Thailand)', nativeName: 'ไทย (ประเทศไทย)' },
|
|
116
|
+
{ code: 'vi-VN', language: 'vi', region: 'VN', name: 'Vietnamese (Vietnam)', nativeName: 'Tiếng Việt (Việt Nam)' },
|
|
117
|
+
|
|
118
|
+
// Right-to-left
|
|
119
|
+
{ code: 'ar-SA', language: 'ar', region: 'SA', name: 'Arabic (Saudi Arabia)', nativeName: 'العربية (السعودية)', rtl: true },
|
|
120
|
+
{ code: 'ar-EG', language: 'ar', region: 'EG', name: 'Arabic (Egypt)', nativeName: 'العربية (مصر)', rtl: true },
|
|
121
|
+
{ code: 'he-IL', language: 'he', region: 'IL', name: 'Hebrew (Israel)', nativeName: 'עברית (ישראל)', rtl: true },
|
|
122
|
+
{ code: 'fa-IR', language: 'fa', region: 'IR', name: 'Persian (Iran)', nativeName: 'فارسی (ایران)', rtl: true },
|
|
123
|
+
{ code: 'ur-PK', language: 'ur', region: 'PK', name: 'Urdu (Pakistan)', nativeName: 'اردو (پاکستان)', rtl: true },
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
/** Canonical fallback locale used when a display value cannot be resolved. */
|
|
127
|
+
export const FALLBACK_LOCALE = 'en-US';
|
|
128
|
+
|
|
129
|
+
/** O(1) lookup of a catalog entry by its canonical code. */
|
|
130
|
+
const LOCALE_BY_CODE: ReadonlyMap<string, SupportedLanguage> = new Map(
|
|
131
|
+
SUPPORTED_LANGUAGES.map((entry) => [entry.code, entry]),
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Extract the base language subtag from a locale, lowercased.
|
|
136
|
+
*
|
|
137
|
+
* Tolerant of bare base subtags and of extra subtags (script/variant):
|
|
138
|
+
* `'es-ES'` → `'es'`, `'es'` → `'es'`, `'zh-Hant-TW'` → `'zh'`. Returns an
|
|
139
|
+
* empty string for empty input.
|
|
140
|
+
*/
|
|
141
|
+
export function getBaseLanguage(locale: string): string {
|
|
142
|
+
return locale.trim().toLowerCase().split('-')[0] ?? '';
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Normalize a locale string to its canonical `language-REGION` form when it is
|
|
147
|
+
* a supported locale, otherwise `undefined`.
|
|
148
|
+
*
|
|
149
|
+
* Canonicalization lowercases the base subtag and uppercases the region
|
|
150
|
+
* subtag (`'es-es'` → `'es-ES'`, `'EN-us'` → `'en-US'`) and validates the
|
|
151
|
+
* result against {@link SUPPORTED_LANGUAGES}. A bare base subtag (`'es'`) has
|
|
152
|
+
* no region and is therefore not a locale — it returns `undefined`.
|
|
153
|
+
*/
|
|
154
|
+
export function normalizeLocale(input: string): string | undefined {
|
|
155
|
+
const trimmed = input.trim();
|
|
156
|
+
if (!trimmed) return undefined;
|
|
157
|
+
|
|
158
|
+
const parts = trimmed.split('-');
|
|
159
|
+
if (parts.length < 2) return undefined;
|
|
160
|
+
|
|
161
|
+
const base = parts[0]?.toLowerCase();
|
|
162
|
+
const region = parts[parts.length - 1]?.toUpperCase();
|
|
163
|
+
if (!base || !region) return undefined;
|
|
164
|
+
|
|
165
|
+
const canonical = `${base}-${region}`;
|
|
166
|
+
return LOCALE_BY_CODE.has(canonical) ? canonical : undefined;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Whether `input` resolves to a supported BCP-47 locale.
|
|
171
|
+
*/
|
|
172
|
+
export function isSupportedLocale(input: string): boolean {
|
|
173
|
+
return normalizeLocale(input) !== undefined;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Resolve catalog metadata for a locale.
|
|
178
|
+
*
|
|
179
|
+
* @param code - A locale tag (any case; e.g. `'es-ES'`, `'es-es'`).
|
|
180
|
+
* @returns The {@link SupportedLanguage} entry, or `null` when the tag is empty
|
|
181
|
+
* or not a supported locale.
|
|
182
|
+
*/
|
|
183
|
+
export function getLanguageMetadata(code: string | null | undefined): SupportedLanguage | null {
|
|
184
|
+
if (!code) return null;
|
|
185
|
+
const canonical = normalizeLocale(code);
|
|
186
|
+
if (!canonical) return null;
|
|
187
|
+
return LOCALE_BY_CODE.get(canonical) ?? null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* English display name for a locale (e.g. `'Spanish (Spain)'`).
|
|
192
|
+
* Falls back to the input tag, then {@link FALLBACK_LOCALE}.
|
|
193
|
+
*/
|
|
194
|
+
export function getLanguageName(code: string | null | undefined): string {
|
|
195
|
+
return getLanguageMetadata(code)?.name || code || FALLBACK_LOCALE;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Native display name (endonym) for a locale (e.g. `'Español (España)'`).
|
|
200
|
+
* Falls back to the input tag, then {@link FALLBACK_LOCALE}.
|
|
201
|
+
*/
|
|
202
|
+
export function getNativeLanguageName(code: string | null | undefined): string {
|
|
203
|
+
return getLanguageMetadata(code)?.nativeName || code || FALLBACK_LOCALE;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Base language subtags whose scripts are written right-to-left. Used to drive
|
|
208
|
+
* `I18nManager.forceRTL(...)` on React Native and `<html dir="rtl">` on web.
|
|
209
|
+
* Includes Arabic (`ar`), Hebrew (`he` / legacy `iw`), Persian (`fa`), and
|
|
210
|
+
* Urdu (`ur`).
|
|
211
|
+
*/
|
|
212
|
+
const RTL_LANGUAGE_BASES: ReadonlySet<string> = new Set(['ar', 'he', 'iw', 'fa', 'ur']);
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Whether a locale (or bare base subtag) is written right-to-left. Unknown
|
|
216
|
+
* tags are treated as left-to-right.
|
|
217
|
+
*/
|
|
218
|
+
export function isRTLLocale(locale?: string | null): boolean {
|
|
219
|
+
if (!locale) return false;
|
|
220
|
+
return RTL_LANGUAGE_BASES.has(getBaseLanguage(locale));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Resolve the ordered list of account locales for a user, primary first.
|
|
225
|
+
*
|
|
226
|
+
* Reads `user.languages` (the only source — there is no singular `language`
|
|
227
|
+
* field), then normalizes each entry to its canonical `language-REGION` form,
|
|
228
|
+
* drops non-string and unsupported entries, and de-duplicates while preserving
|
|
229
|
+
* first-seen order. Pure and side-effect free — never throws on bad input.
|
|
230
|
+
*/
|
|
231
|
+
export function getUserLanguages(user: Pick<User, 'languages'> | null | undefined): string[] {
|
|
232
|
+
const source = user?.languages;
|
|
233
|
+
if (!Array.isArray(source)) return [];
|
|
234
|
+
|
|
235
|
+
const result: string[] = [];
|
|
236
|
+
const seen = new Set<string>();
|
|
237
|
+
|
|
238
|
+
for (const entry of source) {
|
|
239
|
+
if (typeof entry !== 'string') continue;
|
|
240
|
+
const canonical = normalizeLocale(entry);
|
|
241
|
+
if (!canonical || seen.has(canonical)) continue;
|
|
242
|
+
seen.add(canonical);
|
|
243
|
+
result.push(canonical);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return result;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Resolve the single PRIMARY locale for a user (the first entry from
|
|
251
|
+
* {@link getUserLanguages}), or `undefined` when the user has no supported
|
|
252
|
+
* locale.
|
|
253
|
+
*/
|
|
254
|
+
export function getPrimaryLanguage(user: Pick<User, 'languages'> | null | undefined): string | undefined {
|
|
255
|
+
return getUserLanguages(user)[0];
|
|
256
|
+
}
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth 2.0 Authorization Code + PKCE helpers for "Sign in with Oxy" third-party
|
|
3
|
+
* sign-in.
|
|
4
|
+
*
|
|
5
|
+
* Third-party Relying Parties (SPAs, static sites, and native apps that are NOT
|
|
6
|
+
* Oxy first-party) authenticate through the standard OAuth flow against
|
|
7
|
+
* `auth.oxy.so/authorize` — never FedCM, SSO bounces, or Oxy session cookies.
|
|
8
|
+
* Public clients (no secret) prove possession of the authorization code with
|
|
9
|
+
* PKCE (RFC 7636, S256): the RP generates a random `code_verifier`, sends its
|
|
10
|
+
* `code_challenge = BASE64URL(SHA-256(code_verifier))` on the authorize
|
|
11
|
+
* redirect, and later replays the raw verifier on the token exchange.
|
|
12
|
+
*
|
|
13
|
+
* All cross-platform crypto (random bytes, SHA-256) is delegated to the shared
|
|
14
|
+
* `@oxy.so/protocol` platform loaders — the exact primitives the rest of core's
|
|
15
|
+
* crypto already uses — so these helpers run identically on web, Node, and
|
|
16
|
+
* React Native. No `require()`, so the ESM build stays bundler-clean.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { isNodeJS, isReactNative, loadExpoCrypto, loadNodeCrypto, sha256 } from '@oxy.so/protocol';
|
|
20
|
+
import { logger } from '../logger';
|
|
21
|
+
|
|
22
|
+
/** The central Oxy IdP authorization endpoint used by default. */
|
|
23
|
+
export const OXY_AUTHORIZE_URL = 'https://auth.oxy.so/authorize';
|
|
24
|
+
|
|
25
|
+
/** Default OAuth scope requested for a "Sign in with Oxy" third-party flow. */
|
|
26
|
+
export const DEFAULT_OAUTH_SCOPE = 'openid profile';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Number of random bytes behind a PKCE `code_verifier`. 64 bytes → 86 base64url
|
|
30
|
+
* characters, comfortably inside RFC 7636 §4.1's required 43–128 range.
|
|
31
|
+
*/
|
|
32
|
+
const PKCE_VERIFIER_BYTES = 64;
|
|
33
|
+
|
|
34
|
+
/** Number of random bytes behind an OAuth `state` (CSRF) token. */
|
|
35
|
+
const OAUTH_STATE_BYTES = 32;
|
|
36
|
+
|
|
37
|
+
/** RFC 4648 §5 base64url alphabet (URL- and filename-safe, no padding). */
|
|
38
|
+
const BASE64URL_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
|
|
39
|
+
|
|
40
|
+
/** A generated PKCE verifier/challenge pair. */
|
|
41
|
+
export interface PkcePair {
|
|
42
|
+
/** The high-entropy secret replayed on the token exchange (kept client-side). */
|
|
43
|
+
codeVerifier: string;
|
|
44
|
+
/** `BASE64URL(SHA-256(codeVerifier))` — sent on the authorize redirect. */
|
|
45
|
+
codeChallenge: string;
|
|
46
|
+
/** The PKCE transformation method. Always `S256`. */
|
|
47
|
+
method: 'S256';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Parameters for {@link buildOAuthAuthorizeUrl}. */
|
|
51
|
+
export interface BuildOAuthAuthorizeUrlParams {
|
|
52
|
+
/** Authorize endpoint; defaults to {@link OXY_AUTHORIZE_URL}. */
|
|
53
|
+
authorizeBaseUrl?: string;
|
|
54
|
+
/** The registered `ApplicationCredential` public key (`oxy_dk_…`). */
|
|
55
|
+
clientId: string;
|
|
56
|
+
/** Exact registered redirect URI to return the authorization code to. */
|
|
57
|
+
redirectUri: string;
|
|
58
|
+
/** Requested scope; defaults to {@link DEFAULT_OAUTH_SCOPE}. */
|
|
59
|
+
scope?: string;
|
|
60
|
+
/** Opaque CSRF token from {@link generateOAuthState}. */
|
|
61
|
+
state: string;
|
|
62
|
+
/** The PKCE `codeChallenge` from {@link generatePkcePair}. */
|
|
63
|
+
codeChallenge: string;
|
|
64
|
+
/**
|
|
65
|
+
* OAuth `prompt` parameter — `login` forces a fresh authentication even when
|
|
66
|
+
* the IdP already has a session, `consent` forces the consent screen even for
|
|
67
|
+
* an already-granted scope. Both are ordinary OAuth/OIDC and are here for
|
|
68
|
+
* third-party relying parties building their own authorize link.
|
|
69
|
+
*
|
|
70
|
+
* `'none'` is deliberately NOT in this union. It is the silent-SSO value, and
|
|
71
|
+
* it was the only value this SDK ever sent — from the cold-boot cross-origin
|
|
72
|
+
* restore deleted in #691 phase 7b. Accepting it again would hand consumers a
|
|
73
|
+
* one-line rebuild of the automatic, gesture-less full-page bounce to the IdP
|
|
74
|
+
* that the popup transport exists to eliminate.
|
|
75
|
+
*/
|
|
76
|
+
prompt?: 'login' | 'consent';
|
|
77
|
+
/**
|
|
78
|
+
* How the IdP should deliver the authorization response. Omitted (the
|
|
79
|
+
* default) means the ordinary top-level redirect back to `redirectUri`.
|
|
80
|
+
*
|
|
81
|
+
* `web_message` asks the IdP to `postMessage` the result to its opener
|
|
82
|
+
* instead, so a popup sign-in never navigates the relying party's tab. It is
|
|
83
|
+
* a REQUEST, not a guarantee: an IdP with no opener still redirects, which is
|
|
84
|
+
* why the popup transport must handle both outcomes. Only the authorization
|
|
85
|
+
* code, the `state`, and a typed OAuth error ever cross that channel — never
|
|
86
|
+
* a token, device secret, or the PKCE verifier, which the opener keeps.
|
|
87
|
+
*/
|
|
88
|
+
responseMode?: 'web_message';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Cryptographically-secure random bytes, cross-platform.
|
|
93
|
+
*
|
|
94
|
+
* Mirrors the platform gating the rest of core's crypto already uses:
|
|
95
|
+
* `expo-crypto` on React Native, Node's built-in `crypto` on the server, and
|
|
96
|
+
* the Web Crypto API in the browser (also the fallback if Node's `crypto`
|
|
97
|
+
* fails to load in an unusual bundled-Node environment).
|
|
98
|
+
*/
|
|
99
|
+
async function getSecureRandomBytes(byteLength: number): Promise<Uint8Array> {
|
|
100
|
+
if (isReactNative()) {
|
|
101
|
+
const crypto = await loadExpoCrypto();
|
|
102
|
+
return Uint8Array.from(await crypto.getRandomBytesAsync(byteLength));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (isNodeJS()) {
|
|
106
|
+
try {
|
|
107
|
+
const nodeCrypto = await loadNodeCrypto();
|
|
108
|
+
return Uint8Array.from(nodeCrypto.randomBytes(byteLength));
|
|
109
|
+
} catch (error) {
|
|
110
|
+
logger.warn(
|
|
111
|
+
'[oxy.oauth] Node crypto unavailable for PKCE random bytes, falling back to Web Crypto',
|
|
112
|
+
{ component: 'oauthPkce' },
|
|
113
|
+
error,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const bytes = new Uint8Array(byteLength);
|
|
119
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
120
|
+
return bytes;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Encode raw bytes as unpadded base64url (RFC 4648 §5). */
|
|
124
|
+
function bytesToBase64Url(bytes: Uint8Array): string {
|
|
125
|
+
let output = '';
|
|
126
|
+
for (let i = 0; i < bytes.length; i += 3) {
|
|
127
|
+
const byte0 = bytes[i];
|
|
128
|
+
const hasByte1 = i + 1 < bytes.length;
|
|
129
|
+
const hasByte2 = i + 2 < bytes.length;
|
|
130
|
+
const byte1 = hasByte1 ? bytes[i + 1] : 0;
|
|
131
|
+
const byte2 = hasByte2 ? bytes[i + 2] : 0;
|
|
132
|
+
|
|
133
|
+
output += BASE64URL_ALPHABET[byte0 >> 2];
|
|
134
|
+
output += BASE64URL_ALPHABET[((byte0 & 0x03) << 4) | (byte1 >> 4)];
|
|
135
|
+
if (hasByte1) {
|
|
136
|
+
output += BASE64URL_ALPHABET[((byte1 & 0x0f) << 2) | (byte2 >> 6)];
|
|
137
|
+
}
|
|
138
|
+
if (hasByte2) {
|
|
139
|
+
output += BASE64URL_ALPHABET[byte2 & 0x3f];
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return output;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Decode a lowercase-hex string into its raw bytes. */
|
|
146
|
+
function hexToBytes(hex: string): Uint8Array {
|
|
147
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
148
|
+
for (let i = 0; i < bytes.length; i += 1) {
|
|
149
|
+
bytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
150
|
+
}
|
|
151
|
+
return bytes;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Compute the PKCE S256 `code_challenge` for a given verifier:
|
|
156
|
+
* `BASE64URL(SHA-256(ASCII(codeVerifier)))` (RFC 7636 §4.2). The verifier is
|
|
157
|
+
* base64url (ASCII), so its UTF-8 and ASCII byte encodings are identical.
|
|
158
|
+
*
|
|
159
|
+
* Reuses `@oxy.so/protocol`'s cross-platform {@link sha256} (which returns
|
|
160
|
+
* lowercase hex); the digest bytes are recovered and re-encoded as base64url.
|
|
161
|
+
*/
|
|
162
|
+
export async function computeCodeChallenge(codeVerifier: string): Promise<string> {
|
|
163
|
+
const digestHex = await sha256(codeVerifier);
|
|
164
|
+
return bytesToBase64Url(hexToBytes(digestHex));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Generate a fresh PKCE verifier/challenge pair for an OAuth authorization-code
|
|
169
|
+
* flow. The verifier is 64 random bytes as base64url (86 chars, within RFC 7636
|
|
170
|
+
* §4.1's 43–128 range and drawn only from the unreserved set); the challenge is
|
|
171
|
+
* its S256 transform.
|
|
172
|
+
*/
|
|
173
|
+
export async function generatePkcePair(): Promise<PkcePair> {
|
|
174
|
+
const codeVerifier = bytesToBase64Url(await getSecureRandomBytes(PKCE_VERIFIER_BYTES));
|
|
175
|
+
const codeChallenge = await computeCodeChallenge(codeVerifier);
|
|
176
|
+
return { codeVerifier, codeChallenge, method: 'S256' };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Generate an opaque, single-use OAuth `state` token (32 random bytes as
|
|
181
|
+
* base64url) for CSRF protection across the authorize redirect.
|
|
182
|
+
*/
|
|
183
|
+
export async function generateOAuthState(): Promise<string> {
|
|
184
|
+
return bytesToBase64Url(await getSecureRandomBytes(OAUTH_STATE_BYTES));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Build the `auth.oxy.so/authorize` redirect URL for an OAuth authorization-code
|
|
189
|
+
* + PKCE (S256) flow. Built via the WHATWG `URL` API so a custom
|
|
190
|
+
* `authorizeBaseUrl` that already carries a query string keeps its existing
|
|
191
|
+
* params (the OAuth params are merged in, not clobbered by a naive `?` concat).
|
|
192
|
+
* All values are percent-encoded by `URL.searchParams`.
|
|
193
|
+
*/
|
|
194
|
+
export function buildOAuthAuthorizeUrl(params: BuildOAuthAuthorizeUrlParams): string {
|
|
195
|
+
const {
|
|
196
|
+
authorizeBaseUrl = OXY_AUTHORIZE_URL,
|
|
197
|
+
clientId,
|
|
198
|
+
redirectUri,
|
|
199
|
+
scope = DEFAULT_OAUTH_SCOPE,
|
|
200
|
+
state,
|
|
201
|
+
codeChallenge,
|
|
202
|
+
} = params;
|
|
203
|
+
|
|
204
|
+
const url = new URL(authorizeBaseUrl);
|
|
205
|
+
url.searchParams.set('client_id', clientId);
|
|
206
|
+
url.searchParams.set('redirect_uri', redirectUri);
|
|
207
|
+
url.searchParams.set('response_type', 'code');
|
|
208
|
+
url.searchParams.set('state', state);
|
|
209
|
+
url.searchParams.set('scope', scope);
|
|
210
|
+
url.searchParams.set('code_challenge', codeChallenge);
|
|
211
|
+
url.searchParams.set('code_challenge_method', 'S256');
|
|
212
|
+
if (params.prompt) {
|
|
213
|
+
url.searchParams.set('prompt', params.prompt);
|
|
214
|
+
}
|
|
215
|
+
if (params.responseMode) {
|
|
216
|
+
url.searchParams.set('response_mode', params.responseMode);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return url.toString();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** `sessionStorage` key for the OAuth CSRF `state` across an authorize redirect. */
|
|
223
|
+
export const OXY_OAUTH_STATE_STORAGE_KEY = 'oxy_oauth_state';
|
|
224
|
+
|
|
225
|
+
/** `sessionStorage` key for the PKCE `code_verifier` across an authorize redirect. */
|
|
226
|
+
export const OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY = 'oxy_oauth_code_verifier';
|
|
227
|
+
|
|
228
|
+
/** `sessionStorage` key — the exact `redirect_uri` sent on the authorize request. */
|
|
229
|
+
export const OXY_OAUTH_REDIRECT_URI_STORAGE_KEY = 'oxy.oauth_redirect_uri';
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* `sessionStorage` key for the in-app path to return to after an authorize
|
|
233
|
+
* round trip. See {@link persistOAuthReturnPath}.
|
|
234
|
+
*/
|
|
235
|
+
export const OXY_OAUTH_RETURN_PATH_STORAGE_KEY = 'oxy.oauth_return_path';
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Normalize a redirect URI to its origin. Official Oxy apps register apex
|
|
239
|
+
* origins (`https://inbox.oxy.so`) — never path-qualified URLs.
|
|
240
|
+
*
|
|
241
|
+
* This is why the path cannot ride along in `redirect_uri`, and why
|
|
242
|
+
* {@link persistOAuthReturnPath} exists: the authorize round trip always lands
|
|
243
|
+
* back on the bare origin, so the page the user was on has to be carried
|
|
244
|
+
* out-of-band and restored on return.
|
|
245
|
+
*/
|
|
246
|
+
export function normalizeOAuthRedirectUri(input: string): string {
|
|
247
|
+
try {
|
|
248
|
+
return new URL(input).origin;
|
|
249
|
+
} catch {
|
|
250
|
+
return input;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Collapse `https://app.example/` → `https://app.example` for OAuth binding.
|
|
256
|
+
* Path-qualified redirect URIs are preserved — matches the API token exchange.
|
|
257
|
+
*/
|
|
258
|
+
export function canonicalizeOAuthRedirectUri(redirectUri: string): string {
|
|
259
|
+
try {
|
|
260
|
+
const parsed = new URL(redirectUri);
|
|
261
|
+
if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
|
|
262
|
+
return redirectUri;
|
|
263
|
+
}
|
|
264
|
+
if (parsed.pathname === '/' && !parsed.search && !parsed.hash) {
|
|
265
|
+
return parsed.origin;
|
|
266
|
+
}
|
|
267
|
+
return redirectUri;
|
|
268
|
+
} catch {
|
|
269
|
+
return redirectUri;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Is this a safe same-origin path to restore into the address bar?
|
|
275
|
+
*
|
|
276
|
+
* Only site-relative paths are accepted. `//evil.com` and `/\evil.com` are
|
|
277
|
+
* rejected because browsers resolve both as protocol-relative *absolute* URLs —
|
|
278
|
+
* without this check, anything able to write one key of `sessionStorage` could
|
|
279
|
+
* turn the post-authorize restore into an open redirect.
|
|
280
|
+
*/
|
|
281
|
+
function isSafeReturnPath(value: string): boolean {
|
|
282
|
+
if (!value.startsWith('/')) return false;
|
|
283
|
+
if (value.startsWith('//') || value.startsWith('/\\')) return false;
|
|
284
|
+
if (value.length > 2048) return false;
|
|
285
|
+
return true;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Remember the page the user was on before a full-page authorize redirect.
|
|
290
|
+
*
|
|
291
|
+
* Silent cross-origin restore navigates the whole tab to the IdP and back, and
|
|
292
|
+
* `redirect_uri` must be a registered apex origin, so the browser returns to
|
|
293
|
+
* `/` no matter which page the visit started on. Without this, every deep link
|
|
294
|
+
* — a shared URL, a search result, an ad landing page — silently became the
|
|
295
|
+
* home page for signed-out visitors on their first navigation in a tab.
|
|
296
|
+
*
|
|
297
|
+
* Stored next to the PKCE handshake it belongs to, and cleared by
|
|
298
|
+
* {@link clearOAuthHandshake} so a stale entry can never be applied to an
|
|
299
|
+
* unrelated later navigation.
|
|
300
|
+
*/
|
|
301
|
+
export function persistOAuthReturnPath(path: string): void {
|
|
302
|
+
const store = (globalThis as { sessionStorage?: Storage }).sessionStorage;
|
|
303
|
+
if (!store || !isSafeReturnPath(path)) return;
|
|
304
|
+
try {
|
|
305
|
+
store.setItem(OXY_OAUTH_RETURN_PATH_STORAGE_KEY, path);
|
|
306
|
+
} catch (error) {
|
|
307
|
+
// Non-fatal: the round trip still completes, it just lands on the origin.
|
|
308
|
+
logger.warn(
|
|
309
|
+
'Could not persist OAuth return path to sessionStorage',
|
|
310
|
+
{ component: 'oauthPkce' },
|
|
311
|
+
error,
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Read and remove the persisted return path, or `null` when there is none or
|
|
318
|
+
* it failed validation.
|
|
319
|
+
*
|
|
320
|
+
* Single-use by design: it is consumed on the first return so a leftover value
|
|
321
|
+
* cannot redirect a later navigation in the same tab.
|
|
322
|
+
*/
|
|
323
|
+
export function consumeOAuthReturnPath(): string | null {
|
|
324
|
+
const store = (globalThis as { sessionStorage?: Storage }).sessionStorage;
|
|
325
|
+
if (!store) return null;
|
|
326
|
+
try {
|
|
327
|
+
const value = store.getItem(OXY_OAUTH_RETURN_PATH_STORAGE_KEY);
|
|
328
|
+
store.removeItem(OXY_OAUTH_RETURN_PATH_STORAGE_KEY);
|
|
329
|
+
if (!value || !isSafeReturnPath(value)) return null;
|
|
330
|
+
return value;
|
|
331
|
+
} catch {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/** Persist the OAuth handshake for a full-page redirect return (web only). */
|
|
337
|
+
export function persistOAuthHandshake(
|
|
338
|
+
state: string,
|
|
339
|
+
codeVerifier: string,
|
|
340
|
+
redirectUri?: string,
|
|
341
|
+
): boolean {
|
|
342
|
+
const store = (globalThis as { sessionStorage?: Storage }).sessionStorage;
|
|
343
|
+
try {
|
|
344
|
+
if (!store) throw new Error('sessionStorage is unavailable');
|
|
345
|
+
store.setItem(OXY_OAUTH_STATE_STORAGE_KEY, state);
|
|
346
|
+
store.setItem(OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY, codeVerifier);
|
|
347
|
+
if (redirectUri) {
|
|
348
|
+
store.setItem(
|
|
349
|
+
OXY_OAUTH_REDIRECT_URI_STORAGE_KEY,
|
|
350
|
+
canonicalizeOAuthRedirectUri(redirectUri),
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
return true;
|
|
354
|
+
} catch (error) {
|
|
355
|
+
logger.warn(
|
|
356
|
+
'Could not persist OAuth handshake to sessionStorage',
|
|
357
|
+
{ component: 'oauthPkce' },
|
|
358
|
+
error,
|
|
359
|
+
);
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/** Read the persisted OAuth handshake, or `null` when absent. */
|
|
365
|
+
export function readOAuthHandshake(): {
|
|
366
|
+
state: string;
|
|
367
|
+
codeVerifier: string;
|
|
368
|
+
redirectUri?: string;
|
|
369
|
+
} | null {
|
|
370
|
+
const store = (globalThis as { sessionStorage?: Storage }).sessionStorage;
|
|
371
|
+
if (!store) return null;
|
|
372
|
+
const state = store.getItem(OXY_OAUTH_STATE_STORAGE_KEY);
|
|
373
|
+
const codeVerifier = store.getItem(OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY);
|
|
374
|
+
if (!state || !codeVerifier) return null;
|
|
375
|
+
const redirectUri = store.getItem(OXY_OAUTH_REDIRECT_URI_STORAGE_KEY) ?? undefined;
|
|
376
|
+
return redirectUri ? { state, codeVerifier, redirectUri } : { state, codeVerifier };
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/** Drop persisted OAuth handshake keys after a successful or aborted return. */
|
|
380
|
+
export function clearOAuthHandshake(): void {
|
|
381
|
+
const store = (globalThis as { sessionStorage?: Storage }).sessionStorage;
|
|
382
|
+
try {
|
|
383
|
+
store?.removeItem(OXY_OAUTH_STATE_STORAGE_KEY);
|
|
384
|
+
store?.removeItem(OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY);
|
|
385
|
+
store?.removeItem(OXY_OAUTH_REDIRECT_URI_STORAGE_KEY);
|
|
386
|
+
// The return path belongs to this handshake. Leaving it behind would let a
|
|
387
|
+
// later, unrelated navigation in the same tab be redirected by it.
|
|
388
|
+
store?.removeItem(OXY_OAUTH_RETURN_PATH_STORAGE_KEY);
|
|
389
|
+
} catch {
|
|
390
|
+
// Best-effort cleanup only.
|
|
391
|
+
}
|
|
392
|
+
}
|