@oxy.so/core 1.18.0 → 3.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/README.md +13 -7
- package/dist/cjs/HttpService.js +189 -112
- package/dist/cjs/OxyServices.errors.js +48 -1
- package/dist/cjs/OxyServices.js +263 -60
- package/dist/cjs/api/accounts.js +231 -0
- package/dist/cjs/api/agency.js +102 -0
- package/dist/cjs/api/appData.js +45 -0
- package/dist/cjs/api/apps.js +197 -0
- package/dist/cjs/api/assets.js +329 -0
- package/dist/cjs/api/auth.js +900 -0
- package/dist/cjs/api/billing.js +44 -0
- package/dist/cjs/api/civic.js +289 -0
- package/dist/cjs/api/contacts.js +16 -0
- package/dist/cjs/api/devices.js +351 -0
- package/dist/cjs/api/follows.js +240 -0
- package/dist/cjs/api/identity.js +569 -0
- package/dist/cjs/api/linkedAccounts.js +48 -0
- package/dist/cjs/api/nodes.js +75 -0
- package/dist/cjs/api/notifications.js +75 -0
- package/dist/cjs/api/privacy.js +105 -0
- package/dist/cjs/api/reputation.js +74 -0
- package/dist/cjs/api/session.js +177 -0
- package/dist/cjs/api/store.js +199 -0
- package/dist/cjs/api/topics.js +66 -0
- package/dist/cjs/api/users.js +309 -0
- package/dist/cjs/boot/sessionColdBoot.js +4 -4
- package/dist/cjs/civic/index.js +16 -0
- package/dist/cjs/civic/payloads.js +156 -0
- package/dist/cjs/client/context.js +2 -0
- package/dist/cjs/crypto/identityLink.js +5 -5
- package/dist/cjs/crypto/index.js +44 -0
- package/dist/cjs/crypto/internal.js +37 -0
- package/dist/cjs/crypto/keyManager.js +2 -2
- package/dist/cjs/crypto/polyfill.js +9 -80
- package/dist/cjs/crypto/recoveryPhrase.js +1 -0
- package/dist/cjs/crypto/registrationPow.js +1 -1
- package/dist/cjs/crypto/signatureService.js +1 -0
- package/dist/cjs/i18n/accountCategoryLabels.js +3 -3
- package/dist/cjs/i18n/accountRoleLabels.js +7 -7
- package/dist/cjs/i18n/index.js +130 -52
- package/dist/cjs/i18n/locales/{ar-SA.json → ar-SA.js} +134 -52
- package/dist/cjs/i18n/locales/{ca-ES.json → ca-ES.js} +134 -52
- package/dist/{esm/i18n/locales/de-DE.json → cjs/i18n/locales/de-DE.js} +136 -54
- package/dist/{esm/i18n/locales/en-US.json → cjs/i18n/locales/en-US.js} +163 -245
- package/dist/{esm/i18n/locales/es-ES.json → cjs/i18n/locales/es-ES.js} +163 -245
- package/dist/cjs/i18n/locales/{fr-FR.json → fr-FR.js} +134 -52
- package/dist/cjs/i18n/locales/{it-IT.json → it-IT.js} +134 -52
- package/dist/{esm/i18n/locales/ja-JP.json → cjs/i18n/locales/ja-JP.js} +134 -52
- package/dist/cjs/i18n/locales/{ko-KR.json → ko-KR.js} +134 -52
- package/dist/{esm/i18n/locales/pt-PT.json → cjs/i18n/locales/pt-PT.js} +136 -54
- package/dist/{esm/i18n/locales/zh-CN.json → cjs/i18n/locales/zh-CN.js} +135 -53
- package/dist/cjs/i18n/reputationCategoryLabels.js +2 -2
- package/dist/cjs/i18n/trustTierLabels.js +2 -2
- package/dist/cjs/index.js +55 -263
- package/dist/cjs/inference/index.js +21 -0
- package/dist/cjs/server/OxyServer.js +521 -0
- package/dist/cjs/server/auth.js +2 -2
- package/dist/cjs/server/index.js +23 -6
- package/dist/cjs/server/middleware.js +1154 -0
- package/dist/cjs/server/namespaces.js +214 -0
- package/dist/cjs/server/rateLimit.js +2 -2
- package/dist/cjs/server/requesterAssertion.js +5 -5
- package/dist/cjs/server/securityHeaders.js +3 -3
- package/dist/cjs/server/traffic.js +7 -6
- package/dist/cjs/server/trafficNodeHttp.js +9 -3
- package/dist/cjs/server/userInvalidation.js +5 -5
- package/dist/cjs/session/accountDialogController.js +23 -23
- package/dist/cjs/session/authStateStore.js +2 -2
- package/dist/cjs/session/identitySession.js +3 -3
- package/dist/cjs/session/index.js +131 -0
- package/dist/cjs/session/refresh.js +37 -30
- package/dist/cjs/session/sessionClientHost.js +5 -5
- package/dist/cjs/session/sharedDeviceCredential.js +3 -2
- package/dist/cjs/shared/utils/networkUtils.js +2 -2
- package/dist/cjs/utils/accountCacheSweep.js +11 -15
- package/dist/cjs/utils/asyncUtils.js +22 -163
- package/dist/cjs/utils/authHelpers.js +2 -2
- package/dist/cjs/utils/authWebUrl.js +3 -4
- package/dist/cjs/utils/avatarUtils.js +2 -2
- package/dist/cjs/utils/cache.js +32 -2
- package/dist/cjs/utils/cacheKey.js +5 -10
- package/dist/cjs/utils/deviceManager.js +17 -16
- package/dist/cjs/utils/edgeRegion.js +2 -3
- package/dist/cjs/utils/identityCacheSweep.js +6 -10
- package/dist/cjs/utils/oxyServiceEnvironment.js +3 -7
- package/dist/cjs/utils/requestUtils.js +40 -33
- package/dist/cjs/utils/sessionUtils.js +0 -2
- package/dist/cjs/utils/tokenClaims.js +34 -0
- package/dist/esm/HttpService.js +190 -113
- package/dist/esm/OxyServices.errors.js +45 -0
- package/dist/esm/OxyServices.js +262 -57
- package/dist/esm/api/accounts.js +218 -0
- package/dist/esm/api/agency.js +95 -0
- package/dist/esm/api/appData.js +40 -0
- package/dist/esm/api/apps.js +191 -0
- package/dist/esm/api/assets.js +324 -0
- package/dist/esm/api/auth.js +858 -0
- package/dist/esm/api/billing.js +40 -0
- package/dist/esm/api/civic.js +250 -0
- package/dist/esm/api/contacts.js +12 -0
- package/dist/esm/api/devices.js +346 -0
- package/dist/esm/api/follows.js +236 -0
- package/dist/esm/api/identity.js +527 -0
- package/dist/esm/api/linkedAccounts.js +44 -0
- package/dist/esm/api/nodes.js +71 -0
- package/dist/esm/api/notifications.js +71 -0
- package/dist/esm/api/privacy.js +101 -0
- package/dist/esm/api/reputation.js +70 -0
- package/dist/esm/api/session.js +173 -0
- package/dist/esm/api/store.js +192 -0
- package/dist/esm/api/topics.js +62 -0
- package/dist/esm/api/users.js +272 -0
- package/dist/esm/boot/sessionColdBoot.js +4 -4
- package/dist/esm/civic/index.js +10 -0
- package/dist/esm/civic/payloads.js +118 -0
- package/dist/esm/client/context.js +1 -0
- package/dist/esm/crypto/identityLink.js +5 -5
- package/dist/esm/crypto/index.js +24 -0
- package/dist/esm/crypto/internal.js +19 -0
- package/dist/esm/crypto/keyManager.js +2 -2
- package/dist/esm/crypto/polyfill.js +10 -81
- package/dist/esm/crypto/recoveryPhrase.js +1 -0
- package/dist/esm/crypto/registrationPow.js +1 -1
- package/dist/esm/crypto/signatureService.js +1 -0
- package/dist/esm/i18n/accountCategoryLabels.js +2 -2
- package/dist/esm/i18n/accountRoleLabels.js +1 -1
- package/dist/esm/i18n/index.js +93 -52
- package/dist/esm/i18n/locales/{ar-SA.json → ar-SA.js} +132 -52
- package/dist/esm/i18n/locales/{ca-ES.json → ca-ES.js} +132 -52
- package/dist/{cjs/i18n/locales/de-DE.json → esm/i18n/locales/de-DE.js} +134 -54
- package/dist/{cjs/i18n/locales/en-US.json → esm/i18n/locales/en-US.js} +161 -245
- package/dist/{cjs/i18n/locales/es-ES.json → esm/i18n/locales/es-ES.js} +161 -245
- package/dist/esm/i18n/locales/{fr-FR.json → fr-FR.js} +132 -52
- package/dist/esm/i18n/locales/{it-IT.json → it-IT.js} +132 -52
- package/dist/{cjs/i18n/locales/ja-JP.json → esm/i18n/locales/ja-JP.js} +132 -52
- package/dist/esm/i18n/locales/{ko-KR.json → ko-KR.js} +132 -52
- package/dist/{cjs/i18n/locales/pt-PT.json → esm/i18n/locales/pt-PT.js} +134 -54
- package/dist/{cjs/i18n/locales/zh-CN.json → esm/i18n/locales/zh-CN.js} +133 -53
- package/dist/esm/i18n/reputationCategoryLabels.js +1 -1
- package/dist/esm/i18n/trustTierLabels.js +1 -1
- package/dist/esm/index.js +30 -166
- package/dist/esm/inference/index.js +13 -0
- package/dist/esm/server/OxyServer.js +516 -0
- package/dist/esm/server/auth.js +2 -2
- package/dist/esm/server/index.js +10 -5
- package/dist/esm/server/middleware.js +1147 -0
- package/dist/esm/server/namespaces.js +206 -0
- package/dist/esm/server/rateLimit.js +2 -2
- package/dist/esm/server/requesterAssertion.js +5 -5
- package/dist/esm/server/securityHeaders.js +3 -3
- package/dist/esm/server/traffic.js +7 -6
- package/dist/esm/server/trafficNodeHttp.js +9 -3
- package/dist/esm/server/userInvalidation.js +5 -5
- package/dist/esm/session/accountDialogController.js +23 -23
- package/dist/esm/session/authStateStore.js +2 -2
- package/dist/esm/session/identitySession.js +3 -3
- package/dist/esm/session/index.js +82 -0
- package/dist/esm/session/refresh.js +37 -30
- package/dist/esm/session/sessionClientHost.js +5 -5
- package/dist/esm/session/sharedDeviceCredential.js +3 -2
- package/dist/esm/shared/utils/networkUtils.js +2 -2
- package/dist/esm/utils/accountCacheSweep.js +11 -15
- package/dist/esm/utils/asyncUtils.js +22 -151
- package/dist/esm/utils/authHelpers.js +2 -2
- package/dist/esm/utils/authWebUrl.js +3 -4
- package/dist/esm/utils/avatarUtils.js +2 -2
- package/dist/esm/utils/cache.js +32 -2
- package/dist/esm/utils/cacheKey.js +5 -10
- package/dist/esm/utils/deviceManager.js +17 -16
- package/dist/esm/utils/edgeRegion.js +1 -2
- package/dist/esm/utils/identityCacheSweep.js +6 -10
- package/dist/esm/utils/oxyServiceEnvironment.js +3 -7
- package/dist/esm/utils/requestUtils.js +40 -33
- package/dist/esm/utils/sessionUtils.js +0 -2
- package/dist/esm/utils/tokenClaims.js +31 -0
- package/dist/types/HttpService.d.ts +47 -9
- package/dist/types/OxyServices.d.ts +160 -131
- package/dist/types/OxyServices.errors.d.ts +42 -0
- package/dist/types/api/accounts.d.ts +415 -0
- package/dist/types/api/agency.d.ts +135 -0
- package/dist/types/api/appData.d.ts +27 -0
- package/dist/types/api/apps.d.ts +433 -0
- package/dist/types/api/assets.d.ts +174 -0
- package/dist/types/api/auth.d.ts +766 -0
- package/dist/types/api/billing.d.ts +113 -0
- package/dist/types/api/civic.d.ts +203 -0
- package/dist/types/api/contacts.d.ts +31 -0
- package/{src/mixins/OxyServices.deviceBoot.ts → dist/types/api/devices.d.ts} +130 -110
- package/dist/types/api/follows.d.ts +236 -0
- package/dist/types/api/identity.d.ts +275 -0
- package/dist/types/api/linkedAccounts.d.ts +48 -0
- package/dist/types/api/nodes.d.ts +102 -0
- package/dist/types/api/notifications.d.ts +77 -0
- package/dist/types/api/privacy.d.ts +43 -0
- package/dist/types/api/reputation.d.ts +50 -0
- package/dist/types/api/session.d.ts +85 -0
- package/dist/types/api/store.d.ts +278 -0
- package/dist/types/api/topics.d.ts +34 -0
- package/dist/types/api/users.d.ts +134 -0
- package/dist/types/boot/sessionColdBoot.d.ts +1 -1
- package/dist/types/civic/index.d.ts +10 -0
- package/dist/types/civic/payloads.d.ts +83 -0
- package/dist/types/client/context.d.ts +33 -0
- package/dist/types/crypto/identityLink.d.ts +5 -5
- package/dist/types/crypto/index.d.ts +24 -0
- package/dist/types/crypto/internal.d.ts +19 -0
- package/dist/types/crypto/polyfill.d.ts +6 -9
- package/dist/types/crypto/registrationPow.d.ts +1 -1
- package/dist/types/i18n/accountCategoryLabels.d.ts +1 -1
- package/dist/types/i18n/accountRoleLabels.d.ts +1 -1
- package/dist/types/i18n/index.d.ts +20 -1
- package/dist/types/i18n/locales/ar-SA.d.ts +386 -0
- package/dist/types/i18n/locales/ca-ES.d.ts +386 -0
- package/dist/types/i18n/locales/de-DE.d.ts +386 -0
- package/dist/types/i18n/locales/en-US.d.ts +2034 -0
- package/dist/types/i18n/locales/es-ES.d.ts +2034 -0
- package/dist/types/i18n/locales/fr-FR.d.ts +386 -0
- package/dist/types/i18n/locales/it-IT.d.ts +386 -0
- package/dist/types/i18n/locales/ja-JP.d.ts +386 -0
- package/dist/types/i18n/locales/ko-KR.d.ts +386 -0
- package/dist/types/i18n/locales/pt-PT.d.ts +386 -0
- package/dist/types/i18n/locales/zh-CN.d.ts +386 -0
- package/dist/types/index.d.ts +46 -95
- package/dist/types/inference/index.d.ts +22 -0
- package/dist/types/models/interfaces.d.ts +1 -1
- package/dist/types/server/OxyServer.d.ts +183 -0
- package/dist/types/server/auth.d.ts +10 -7
- package/dist/types/server/index.d.ts +10 -4
- package/dist/types/server/middleware.d.ts +291 -0
- package/dist/types/server/namespaces.d.ts +98 -0
- package/dist/types/server/rateLimit.d.ts +7 -5
- package/dist/types/server/requesterAssertion.d.ts +14 -11
- package/dist/types/server/securityHeaders.d.ts +4 -5
- package/dist/types/server/userInvalidation.d.ts +10 -8
- package/dist/types/session/accountDelegationAccess.d.ts +1 -1
- package/dist/types/session/accountDialogController.d.ts +8 -8
- package/dist/types/session/accountSwitchTargets.d.ts +1 -1
- package/dist/types/session/authStateStore.d.ts +7 -6
- package/dist/types/session/index.d.ts +40 -0
- package/dist/types/utils/accountCacheSweep.d.ts +7 -10
- package/dist/types/utils/apiUtils.d.ts +3 -3
- package/dist/types/utils/asyncUtils.d.ts +11 -58
- package/dist/types/utils/authWebUrl.d.ts +3 -4
- package/dist/types/utils/avatarUtils.d.ts +5 -6
- package/dist/types/utils/cache.d.ts +11 -3
- package/dist/types/utils/commonsApproval.d.ts +1 -1
- package/dist/types/utils/edgeRegion.d.ts +1 -2
- package/dist/types/utils/identityCacheSweep.d.ts +6 -4
- package/dist/types/utils/oauthPkce.d.ts +2 -2
- package/dist/types/utils/oxyServiceEnvironment.d.ts +3 -7
- package/dist/types/utils/requestUtils.d.ts +18 -14
- package/dist/types/utils/tokenClaims.d.ts +9 -0
- package/package.json +68 -7
- package/src/HttpService.ts +201 -132
- package/src/OxyServices.errors.ts +73 -0
- package/src/OxyServices.ts +289 -164
- package/src/api/accounts.ts +579 -0
- package/src/api/agency.ts +277 -0
- package/src/api/appData.ts +63 -0
- package/src/api/apps.ts +596 -0
- package/src/api/assets.ts +416 -0
- package/src/api/auth.ts +1457 -0
- package/src/api/billing.ts +144 -0
- package/src/api/civic.ts +428 -0
- package/src/api/contacts.ts +41 -0
- package/src/api/devices.ts +438 -0
- package/src/api/follows.ts +414 -0
- package/src/api/identity.ts +727 -0
- package/src/api/linkedAccounts.ts +78 -0
- package/src/api/nodes.ts +169 -0
- package/src/api/notifications.ts +135 -0
- package/src/api/privacy.ts +120 -0
- package/src/api/reputation.ts +134 -0
- package/src/api/session.ts +210 -0
- package/src/api/store.ts +435 -0
- package/src/api/topics.ts +83 -0
- package/src/api/users.ts +357 -0
- package/src/boot/sessionColdBoot.ts +4 -4
- package/src/civic/index.ts +12 -0
- package/src/civic/payloads.ts +175 -0
- package/src/client/context.ts +34 -0
- package/src/crypto/identityLink.ts +5 -5
- package/src/crypto/index.ts +39 -0
- package/src/crypto/internal.ts +20 -0
- package/src/crypto/keyManager.ts +2 -2
- package/src/crypto/polyfill.ts +9 -89
- package/src/crypto/recoveryPhrase.ts +1 -0
- package/src/crypto/registrationPow.ts +1 -1
- package/src/crypto/signatureService.ts +1 -0
- package/src/i18n/accountCategoryLabels.ts +2 -2
- package/src/i18n/accountRoleLabels.ts +2 -2
- package/src/i18n/index.ts +105 -54
- package/src/i18n/locales/{ar-SA.json → ar-SA.ts} +132 -52
- package/src/i18n/locales/{ca-ES.json → ca-ES.ts} +132 -52
- package/{dist/cjs/i18n/locales/locales/de-DE.json → src/i18n/locales/de-DE.ts} +134 -54
- package/src/i18n/locales/{en-US.json → en-US.ts} +161 -245
- package/{dist/cjs/i18n/locales/locales/es-ES.json → src/i18n/locales/es-ES.ts} +161 -245
- package/src/i18n/locales/{fr-FR.json → fr-FR.ts} +132 -52
- package/src/i18n/locales/{it-IT.json → it-IT.ts} +132 -52
- package/{dist/cjs/i18n/locales/locales/ja-JP.json → src/i18n/locales/ja-JP.ts} +132 -52
- package/src/i18n/locales/{ko-KR.json → ko-KR.ts} +132 -52
- package/src/i18n/locales/{pt-PT.json → pt-PT.ts} +134 -54
- package/src/i18n/locales/{zh-CN.json → zh-CN.ts} +133 -53
- package/src/i18n/reputationCategoryLabels.ts +1 -1
- package/src/i18n/trustTierLabels.ts +1 -1
- package/src/index.ts +170 -501
- package/src/inference/index.ts +48 -0
- package/src/models/interfaces.ts +1 -1
- package/src/server/OxyServer.ts +637 -0
- package/src/server/auth.ts +12 -8
- package/src/server/index.ts +28 -5
- package/src/server/middleware.ts +1520 -0
- package/src/server/namespaces.ts +257 -0
- package/src/server/rateLimit.ts +10 -9
- package/src/server/requesterAssertion.ts +13 -10
- package/src/server/securityHeaders.ts +7 -8
- package/src/server/traffic.ts +6 -6
- package/src/server/trafficNodeHttp.ts +7 -3
- package/src/server/userInvalidation.ts +9 -9
- package/src/session/accountDelegationAccess.ts +1 -1
- package/src/session/accountDialogController.ts +24 -24
- package/src/session/accountSwitchTargets.ts +1 -1
- package/src/session/authStateStore.ts +9 -8
- package/src/session/identitySession.ts +3 -3
- package/src/session/index.ts +210 -0
- package/src/session/refresh.ts +36 -30
- package/src/session/sessionClientHost.ts +5 -5
- package/src/session/sharedDeviceCredential.ts +3 -2
- package/src/shared/utils/networkUtils.ts +2 -2
- package/src/utils/accountCacheSweep.ts +11 -15
- package/src/utils/apiUtils.ts +3 -3
- package/src/utils/asyncUtils.ts +38 -235
- package/src/utils/authHelpers.ts +2 -2
- package/src/utils/authWebUrl.ts +3 -4
- package/src/utils/avatarUtils.ts +4 -7
- package/src/utils/cache.ts +36 -5
- package/src/utils/cacheKey.ts +5 -9
- package/src/utils/commonsApproval.ts +1 -1
- package/src/utils/deviceManager.ts +17 -16
- package/src/utils/edgeRegion.ts +1 -2
- package/src/utils/identityCacheSweep.ts +7 -12
- package/src/utils/oauthPkce.ts +2 -2
- package/src/utils/oxyServiceEnvironment.ts +3 -7
- package/src/utils/requestUtils.ts +48 -44
- package/src/utils/sessionUtils.ts +0 -2
- package/src/utils/tokenClaims.ts +38 -0
- package/dist/cjs/.tsbuildinfo +0 -1
- package/dist/cjs/OxyServices.base.js +0 -392
- package/dist/cjs/i18n/locales/locales/ar-SA.json +0 -306
- package/dist/cjs/i18n/locales/locales/ca-ES.json +0 -306
- package/dist/cjs/i18n/locales/locales/en-US.json +0 -2127
- package/dist/cjs/i18n/locales/locales/fr-FR.json +0 -306
- package/dist/cjs/i18n/locales/locales/it-IT.json +0 -306
- package/dist/cjs/i18n/locales/locales/ko-KR.json +0 -306
- package/dist/cjs/i18n/locales/locales/pt-PT.json +0 -306
- package/dist/cjs/i18n/locales/locales/zh-CN.json +0 -306
- package/dist/cjs/mixins/OxyServices.accounts.js +0 -555
- package/dist/cjs/mixins/OxyServices.agency.js +0 -154
- package/dist/cjs/mixins/OxyServices.analytics.js +0 -49
- package/dist/cjs/mixins/OxyServices.appData.js +0 -108
- package/dist/cjs/mixins/OxyServices.assets.js +0 -854
- package/dist/cjs/mixins/OxyServices.auth.js +0 -1440
- package/dist/cjs/mixins/OxyServices.chains.js +0 -73
- package/dist/cjs/mixins/OxyServices.civic.js +0 -611
- package/dist/cjs/mixins/OxyServices.connectedApps.js +0 -95
- package/dist/cjs/mixins/OxyServices.contacts.js +0 -50
- package/dist/cjs/mixins/OxyServices.deviceBoot.js +0 -191
- package/dist/cjs/mixins/OxyServices.devices.js +0 -116
- package/dist/cjs/mixins/OxyServices.features.js +0 -335
- package/dist/cjs/mixins/OxyServices.followGraph.js +0 -221
- package/dist/cjs/mixins/OxyServices.identity.js +0 -562
- package/dist/cjs/mixins/OxyServices.identityBackup.js +0 -172
- package/dist/cjs/mixins/OxyServices.inference.js +0 -59
- package/dist/cjs/mixins/OxyServices.language.js +0 -125
- package/dist/cjs/mixins/OxyServices.linkedAccounts.js +0 -79
- package/dist/cjs/mixins/OxyServices.location.js +0 -43
- package/dist/cjs/mixins/OxyServices.nodes.js +0 -175
- package/dist/cjs/mixins/OxyServices.notifications.js +0 -63
- package/dist/cjs/mixins/OxyServices.payment.js +0 -158
- package/dist/cjs/mixins/OxyServices.privacy.js +0 -199
- package/dist/cjs/mixins/OxyServices.reputation.js +0 -289
- package/dist/cjs/mixins/OxyServices.security.js +0 -89
- package/dist/cjs/mixins/OxyServices.store.js +0 -266
- package/dist/cjs/mixins/OxyServices.topics.js +0 -137
- package/dist/cjs/mixins/OxyServices.user.js +0 -954
- package/dist/cjs/mixins/OxyServices.utility.js +0 -1402
- package/dist/cjs/mixins/index.js +0 -136
- package/dist/cjs/mixins/mixinHelpers.js +0 -53
- package/dist/cjs/utils/webauthnOrigin.js +0 -51
- package/dist/esm/.tsbuildinfo +0 -1
- package/dist/esm/OxyServices.base.js +0 -388
- package/dist/esm/i18n/locales/locales/ar-SA.json +0 -306
- package/dist/esm/i18n/locales/locales/ca-ES.json +0 -306
- package/dist/esm/i18n/locales/locales/de-DE.json +0 -306
- package/dist/esm/i18n/locales/locales/en-US.json +0 -2127
- package/dist/esm/i18n/locales/locales/es-ES.json +0 -2127
- package/dist/esm/i18n/locales/locales/fr-FR.json +0 -306
- package/dist/esm/i18n/locales/locales/it-IT.json +0 -306
- package/dist/esm/i18n/locales/locales/ja-JP.json +0 -306
- package/dist/esm/i18n/locales/locales/ko-KR.json +0 -306
- package/dist/esm/i18n/locales/locales/pt-PT.json +0 -306
- package/dist/esm/i18n/locales/locales/zh-CN.json +0 -306
- package/dist/esm/mixins/OxyServices.accounts.js +0 -543
- package/dist/esm/mixins/OxyServices.agency.js +0 -151
- package/dist/esm/mixins/OxyServices.analytics.js +0 -46
- package/dist/esm/mixins/OxyServices.appData.js +0 -103
- package/dist/esm/mixins/OxyServices.assets.js +0 -851
- package/dist/esm/mixins/OxyServices.auth.js +0 -1400
- package/dist/esm/mixins/OxyServices.chains.js +0 -70
- package/dist/esm/mixins/OxyServices.civic.js +0 -605
- package/dist/esm/mixins/OxyServices.connectedApps.js +0 -92
- package/dist/esm/mixins/OxyServices.contacts.js +0 -47
- package/dist/esm/mixins/OxyServices.deviceBoot.js +0 -186
- package/dist/esm/mixins/OxyServices.devices.js +0 -113
- package/dist/esm/mixins/OxyServices.features.js +0 -332
- package/dist/esm/mixins/OxyServices.followGraph.js +0 -218
- package/dist/esm/mixins/OxyServices.identity.js +0 -558
- package/dist/esm/mixins/OxyServices.identityBackup.js +0 -169
- package/dist/esm/mixins/OxyServices.inference.js +0 -56
- package/dist/esm/mixins/OxyServices.language.js +0 -122
- package/dist/esm/mixins/OxyServices.linkedAccounts.js +0 -76
- package/dist/esm/mixins/OxyServices.location.js +0 -40
- package/dist/esm/mixins/OxyServices.nodes.js +0 -172
- package/dist/esm/mixins/OxyServices.notifications.js +0 -60
- package/dist/esm/mixins/OxyServices.payment.js +0 -155
- package/dist/esm/mixins/OxyServices.privacy.js +0 -196
- package/dist/esm/mixins/OxyServices.reputation.js +0 -286
- package/dist/esm/mixins/OxyServices.security.js +0 -86
- package/dist/esm/mixins/OxyServices.store.js +0 -263
- package/dist/esm/mixins/OxyServices.topics.js +0 -134
- package/dist/esm/mixins/OxyServices.user.js +0 -951
- package/dist/esm/mixins/OxyServices.utility.js +0 -1397
- package/dist/esm/mixins/index.js +0 -133
- package/dist/esm/mixins/mixinHelpers.js +0 -48
- package/dist/esm/utils/webauthnOrigin.js +0 -48
- package/dist/types/.tsbuildinfo +0 -1
- package/dist/types/OxyServices.base.d.ts +0 -189
- package/dist/types/mixins/OxyServices.accounts.d.ts +0 -868
- package/dist/types/mixins/OxyServices.agency.d.ts +0 -176
- package/dist/types/mixins/OxyServices.analytics.d.ts +0 -72
- package/dist/types/mixins/OxyServices.appData.d.ts +0 -108
- package/dist/types/mixins/OxyServices.assets.d.ts +0 -371
- package/dist/types/mixins/OxyServices.auth.d.ts +0 -965
- package/dist/types/mixins/OxyServices.chains.d.ts +0 -156
- package/dist/types/mixins/OxyServices.civic.d.ts +0 -520
- package/dist/types/mixins/OxyServices.connectedApps.d.ts +0 -184
- package/dist/types/mixins/OxyServices.contacts.d.ts +0 -100
- package/dist/types/mixins/OxyServices.deviceBoot.d.ts +0 -185
- package/dist/types/mixins/OxyServices.devices.d.ts +0 -103
- package/dist/types/mixins/OxyServices.features.d.ts +0 -250
- package/dist/types/mixins/OxyServices.followGraph.d.ts +0 -223
- package/dist/types/mixins/OxyServices.identity.d.ts +0 -337
- package/dist/types/mixins/OxyServices.identityBackup.d.ts +0 -129
- package/dist/types/mixins/OxyServices.inference.d.ts +0 -95
- package/dist/types/mixins/OxyServices.language.d.ts +0 -87
- package/dist/types/mixins/OxyServices.linkedAccounts.d.ts +0 -107
- package/dist/types/mixins/OxyServices.location.d.ts +0 -70
- package/dist/types/mixins/OxyServices.nodes.d.ts +0 -198
- package/dist/types/mixins/OxyServices.notifications.d.ts +0 -143
- package/dist/types/mixins/OxyServices.payment.d.ts +0 -117
- package/dist/types/mixins/OxyServices.privacy.d.ts +0 -146
- package/dist/types/mixins/OxyServices.reputation.d.ts +0 -202
- package/dist/types/mixins/OxyServices.security.d.ts +0 -84
- package/dist/types/mixins/OxyServices.store.d.ts +0 -334
- package/dist/types/mixins/OxyServices.topics.d.ts +0 -110
- package/dist/types/mixins/OxyServices.user.d.ts +0 -600
- package/dist/types/mixins/OxyServices.utility.d.ts +0 -526
- package/dist/types/mixins/index.d.ts +0 -87
- package/dist/types/mixins/mixinHelpers.d.ts +0 -31
- package/dist/types/utils/webauthnOrigin.d.ts +0 -32
- package/src/OxyServices.base.ts +0 -480
- package/src/__tests__/authSocket.test.ts +0 -96
- package/src/__tests__/fixEsmImports.test.ts +0 -132
- package/src/__tests__/httpServiceAbort.test.ts +0 -308
- package/src/__tests__/httpServiceActivityId.test.ts +0 -49
- package/src/__tests__/httpServiceAuthSelfAwait.test.ts +0 -108
- package/src/__tests__/httpServiceCache.test.ts +0 -318
- package/src/__tests__/httpServiceFormEncoded.test.ts +0 -139
- package/src/__tests__/httpServiceQueueDeadlock.test.ts +0 -128
- package/src/__tests__/httpServiceRateLimitedRefresh.test.ts +0 -160
- package/src/__tests__/httpServiceUnwrapEnvelope.test.ts +0 -115
- package/src/__tests__/httpServiceWrites.test.ts +0 -175
- package/src/__tests__/inSessionRefresh.test.ts +0 -313
- package/src/__tests__/linkedClient.test.ts +0 -363
- package/src/__tests__/nearExpiryMintStorm.test.ts +0 -165
- package/src/__tests__/parseHttpErrorBody.test.ts +0 -116
- package/src/__tests__/publicInterfaceParity.test.ts +0 -75
- package/src/__tests__/serverValueImportsDeclared.test.ts +0 -120
- package/src/__tests__/serviceTokenWorkloadFallback.test.ts +0 -85
- package/src/__tests__/userIdentity.test.ts +0 -143
- package/src/boot/__tests__/sessionColdBoot.identity.test.ts +0 -393
- package/src/boot/__tests__/sessionColdBoot.sharedDevice.test.ts +0 -325
- package/src/boot/__tests__/sessionColdBoot.test.ts +0 -570
- package/src/crypto/__tests__/backupMaterial.test.ts +0 -86
- package/src/crypto/__tests__/cryptoPrimitives.test.ts +0 -226
- package/src/crypto/__tests__/identityLink.test.ts +0 -18
- package/src/crypto/__tests__/identityMocks.ts +0 -125
- package/src/crypto/__tests__/identityProof.test.ts +0 -81
- package/src/crypto/__tests__/keyManager.atomicity.test.ts +0 -272
- package/src/crypto/__tests__/keyManager.cacheSafety.test.ts +0 -175
- package/src/crypto/__tests__/keyManager.deviceBackup.test.ts +0 -310
- package/src/crypto/__tests__/keyManager.identityStatus.test.ts +0 -217
- package/src/crypto/__tests__/keyManager.recoveryLadder.test.ts +0 -179
- package/src/crypto/__tests__/keyManager.recoveryMnemonic.test.ts +0 -138
- package/src/crypto/__tests__/keyManager.sharedSlotSync.test.ts +0 -150
- package/src/crypto/__tests__/keyManager.storageMigration.test.ts +0 -227
- package/src/crypto/__tests__/keyManager.test.ts +0 -329
- package/src/crypto/__tests__/polyfill.test.ts +0 -109
- package/src/crypto/__tests__/polyfillOrder.test.ts +0 -114
- package/src/crypto/__tests__/recoveryPhrase.test.ts +0 -61
- package/src/crypto/__tests__/registrationPow.test.ts +0 -61
- package/src/crypto/__tests__/scopedSeed.test.ts +0 -126
- package/src/crypto/__tests__/signChallengeShared.test.ts +0 -63
- package/src/crypto/__tests__/signedRecord.test.ts +0 -230
- package/src/i18n/__tests__/accountCategoryLabels.test.ts +0 -62
- package/src/i18n/__tests__/accountRoleLabels.test.ts +0 -54
- package/src/i18n/__tests__/reputationCategoryLabels.test.ts +0 -56
- package/src/i18n/__tests__/trustTierLabels.test.ts +0 -47
- package/src/i18n/locales/de-DE.json +0 -306
- package/src/i18n/locales/es-ES.json +0 -2127
- package/src/i18n/locales/ja-JP.json +0 -306
- package/src/inference/__tests__/OxyInferenceClient.test.ts +0 -899
- package/src/logger/__tests__/logger.test.ts +0 -207
- package/src/mixins/OxyServices.accounts.ts +0 -1330
- package/src/mixins/OxyServices.agency.ts +0 -353
- package/src/mixins/OxyServices.analytics.ts +0 -53
- package/src/mixins/OxyServices.appData.ts +0 -158
- package/src/mixins/OxyServices.assets.ts +0 -964
- package/src/mixins/OxyServices.auth.ts +0 -2028
- package/src/mixins/OxyServices.chains.ts +0 -134
- package/src/mixins/OxyServices.civic.ts +0 -945
- package/src/mixins/OxyServices.connectedApps.ts +0 -212
- package/src/mixins/OxyServices.contacts.ts +0 -73
- package/src/mixins/OxyServices.devices.ts +0 -120
- package/src/mixins/OxyServices.features.ts +0 -454
- package/src/mixins/OxyServices.followGraph.ts +0 -293
- package/src/mixins/OxyServices.identity.ts +0 -787
- package/src/mixins/OxyServices.identityBackup.ts +0 -250
- package/src/mixins/OxyServices.inference.ts +0 -57
- package/src/mixins/OxyServices.language.ts +0 -144
- package/src/mixins/OxyServices.linkedAccounts.ts +0 -129
- package/src/mixins/OxyServices.location.ts +0 -46
- package/src/mixins/OxyServices.nodes.ts +0 -337
- package/src/mixins/OxyServices.notifications.ts +0 -142
- package/src/mixins/OxyServices.payment.ts +0 -163
- package/src/mixins/OxyServices.privacy.ts +0 -219
- package/src/mixins/OxyServices.reputation.ts +0 -436
- package/src/mixins/OxyServices.security.ts +0 -110
- package/src/mixins/OxyServices.store.ts +0 -585
- package/src/mixins/OxyServices.topics.ts +0 -169
- package/src/mixins/OxyServices.user.ts +0 -1208
- package/src/mixins/OxyServices.utility.ts +0 -1803
- package/src/mixins/__tests__/OxyServices.civic.test.ts +0 -1095
- package/src/mixins/__tests__/OxyServices.deviceBoot.test.ts +0 -124
- package/src/mixins/__tests__/OxyServices.identity.test.ts +0 -301
- package/src/mixins/__tests__/OxyServices.nodes.test.ts +0 -341
- package/src/mixins/__tests__/OxyServices.rotateKey.test.ts +0 -323
- package/src/mixins/__tests__/OxyServices.serviceAssetMetadata.test.ts +0 -192
- package/src/mixins/__tests__/OxyServices.serviceAssetMetadataBySha.test.ts +0 -135
- package/src/mixins/__tests__/OxyServices.serviceLinkedDownloadUrl.test.ts +0 -139
- package/src/mixins/__tests__/accountEvents.test.ts +0 -158
- package/src/mixins/__tests__/accounts.test.ts +0 -727
- package/src/mixins/__tests__/agency.test.ts +0 -120
- package/src/mixins/__tests__/appData.test.ts +0 -203
- package/src/mixins/__tests__/assetCredentials.test.ts +0 -47
- package/src/mixins/__tests__/assetUpload.test.ts +0 -191
- package/src/mixins/__tests__/authRefusalObservability.test.ts +0 -396
- package/src/mixins/__tests__/backgroundCredential.httpIntegration.test.ts +0 -130
- package/src/mixins/__tests__/chains.test.ts +0 -113
- package/src/mixins/__tests__/commonsSignIn.test.ts +0 -1147
- package/src/mixins/__tests__/connectedApps.test.ts +0 -146
- package/src/mixins/__tests__/deviceTokenMintPinned.test.ts +0 -113
- package/src/mixins/__tests__/discoveryErrorHandling.test.ts +0 -265
- package/src/mixins/__tests__/followCacheInvalidation.test.ts +0 -173
- package/src/mixins/__tests__/followGraph.test.ts +0 -147
- package/src/mixins/__tests__/followGraphPagination.test.ts +0 -271
- package/src/mixins/__tests__/getFileDownloadUrl.test.ts +0 -346
- package/src/mixins/__tests__/getFollowStatuses.test.ts +0 -95
- package/src/mixins/__tests__/getUsersByIds.test.ts +0 -176
- package/src/mixins/__tests__/identityBackup.test.ts +0 -315
- package/src/mixins/__tests__/identityLink.test.ts +0 -89
- package/src/mixins/__tests__/identityWriteCacheInvalidation.test.ts +0 -407
- package/src/mixins/__tests__/inferenceFactory.test.ts +0 -58
- package/src/mixins/__tests__/linkedAccounts.test.ts +0 -109
- package/src/mixins/__tests__/notifications.read.test.ts +0 -82
- package/src/mixins/__tests__/onTokensChanged.test.ts +0 -129
- package/src/mixins/__tests__/preSessionSkipAuth.test.ts +0 -284
- package/src/mixins/__tests__/privacyCacheInvalidation.test.ts +0 -155
- package/src/mixins/__tests__/reputation.test.ts +0 -522
- package/src/mixins/__tests__/serviceAuth.test.ts +0 -889
- package/src/mixins/__tests__/serviceTokenJwks.test.ts +0 -208
- package/src/mixins/__tests__/store.test.ts +0 -304
- package/src/mixins/__tests__/topics.test.ts +0 -155
- package/src/mixins/__tests__/userReadCacheBypass.test.ts +0 -129
- package/src/mixins/__tests__/userTokenAuth.test.ts +0 -746
- package/src/mixins/__tests__/verifyChallenge.test.ts +0 -161
- package/src/mixins/__tests__/verifyServiceActingAs.test.ts +0 -167
- package/src/mixins/__tests__/webauthnAuth.test.ts +0 -279
- package/src/mixins/index.ts +0 -203
- package/src/mixins/mixinHelpers.ts +0 -69
- package/src/server/__tests__/auth.test.ts +0 -78
- package/src/server/__tests__/capabilityTicket.test.ts +0 -111
- package/src/server/__tests__/cors.socket.test.ts +0 -225
- package/src/server/__tests__/cors.test.ts +0 -158
- package/src/server/__tests__/rateLimit.test.ts +0 -257
- package/src/server/__tests__/requesterAssertion.test.ts +0 -359
- package/src/server/__tests__/safeFetch.test.ts +0 -232
- package/src/server/__tests__/securityHeaders.test.ts +0 -387
- package/src/server/__tests__/serviceTokenAttribution.test.ts +0 -396
- package/src/server/__tests__/traffic.test.ts +0 -180
- package/src/server/__tests__/trafficNodeHttp.test.ts +0 -38
- package/src/server/__tests__/userInvalidation.test.ts +0 -203
- package/src/server/__tests__/verifySecret.test.ts +0 -40
- package/src/server/__tests__/workloadIdentity.client.test.ts +0 -16
- package/src/server/__tests__/workloadIdentity.test.ts +0 -128
- package/src/session/__tests__/SessionClient.additive.test.ts +0 -153
- package/src/session/__tests__/SessionClient.broadcastChannel.test.ts +0 -114
- package/src/session/__tests__/SessionClient.diagnostics.test.ts +0 -91
- package/src/session/__tests__/SessionClient.directory.test.ts +0 -688
- package/src/session/__tests__/SessionClient.httpIntegration.test.ts +0 -65
- package/src/session/__tests__/SessionClient.identityPin.test.ts +0 -175
- package/src/session/__tests__/SessionClient.rest.test.ts +0 -119
- package/src/session/__tests__/SessionClient.serverEvents.test.ts +0 -72
- package/src/session/__tests__/SessionClient.socket.test.ts +0 -202
- package/src/session/__tests__/SessionClient.socketFactory.test.ts +0 -81
- package/src/session/__tests__/SessionClient.state.test.ts +0 -78
- package/src/session/__tests__/SessionClient.switchTokenOrder.test.ts +0 -170
- package/src/session/__tests__/accountDelegationAccess.test.ts +0 -104
- package/src/session/__tests__/accountDialogController.test.ts +0 -2502
- package/src/session/__tests__/accountDialogShape.test.ts +0 -120
- package/src/session/__tests__/accountSwitchTargets.test.ts +0 -137
- package/src/session/__tests__/authStateStore.test.ts +0 -451
- package/src/session/__tests__/deviceDirectory.test.ts +0 -422
- package/src/session/__tests__/deviceSwitcherRows.test.ts +0 -223
- package/src/session/__tests__/identityPin.test.ts +0 -131
- package/src/session/__tests__/identitySession.test.ts +0 -194
- package/src/session/__tests__/projectSessionState.test.ts +0 -105
- package/src/session/__tests__/refresh.identityPin.test.ts +0 -307
- package/src/session/__tests__/refresh.test.ts +0 -606
- package/src/session/__tests__/sessionIntegration.test.ts +0 -209
- package/src/session/__tests__/sharedDeviceCredential.test.ts +0 -300
- package/src/utils/__tests__/accountUtils.test.ts +0 -151
- package/src/utils/__tests__/activityId.test.ts +0 -45
- package/src/utils/__tests__/asyncUtils.test.ts +0 -279
- package/src/utils/__tests__/authWebUrl.test.ts +0 -15
- package/src/utils/__tests__/cacheKey.test.ts +0 -0
- package/src/utils/__tests__/cacheLifecycle.test.ts +0 -65
- package/src/utils/__tests__/coldBoot.test.ts +0 -496
- package/src/utils/__tests__/commonsApproval.test.ts +0 -60
- package/src/utils/__tests__/identityCacheSweep.test.ts +0 -151
- package/src/utils/__tests__/languageUtils.test.ts +0 -244
- package/src/utils/__tests__/oauthPkce.test.ts +0 -303
- package/src/utils/__tests__/officialOrigins.test.ts +0 -31
- package/src/utils/__tests__/oxyServiceEnvironment.test.ts +0 -7
- package/src/utils/__tests__/profileLinks.test.ts +0 -180
- package/src/utils/__tests__/redactUrl.test.ts +0 -33
- package/src/utils/__tests__/registrableApex.test.ts +0 -62
- package/src/utils/__tests__/requestUtilsAbort.test.ts +0 -155
- package/src/utils/__tests__/textNormalization.test.ts +0 -205
- package/src/utils/__tests__/validationUtils.test.ts +0 -703
- package/src/utils/__tests__/webauthnOrigin.test.ts +0 -83
- package/src/utils/webauthnOrigin.ts +0 -52
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@oxy.so/core/inference` — the Oxy inference client.
|
|
3
|
+
*
|
|
4
|
+
* Its own subpath so an app that never calls inference never bundles it.
|
|
5
|
+
*
|
|
6
|
+
* ```ts
|
|
7
|
+
* import { createInferenceClient } from '@oxy.so/core/inference';
|
|
8
|
+
* const inference = createInferenceClient(oxy); // the session's bearer
|
|
9
|
+
* ```
|
|
10
|
+
*
|
|
11
|
+
* A server holding an Oxy API key builds `new OxyInferenceClient({ credential })`
|
|
12
|
+
* directly — one surface, two credential lanes. See `docs/inference/sdk.md`.
|
|
13
|
+
*/
|
|
14
|
+
import type { OxyServices } from '../OxyServices';
|
|
15
|
+
import { OxyInferenceClient, type OxyInferenceClientOptions } from './OxyInferenceClient';
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
OXY_INFERENCE_BASE_URL,
|
|
19
|
+
OxyInferenceClient,
|
|
20
|
+
OxyInferenceError,
|
|
21
|
+
OxyInferenceProtocolError,
|
|
22
|
+
} from './OxyInferenceClient';
|
|
23
|
+
export type {
|
|
24
|
+
OxyGenerationReceipt,
|
|
25
|
+
OxyInferenceClientOptions,
|
|
26
|
+
OxyInferenceCredential,
|
|
27
|
+
OxyInferenceFetch,
|
|
28
|
+
OxyInferenceRequestOptions,
|
|
29
|
+
OxyInferenceResponse,
|
|
30
|
+
OxyResponsesRequest,
|
|
31
|
+
OxySpeechRequest,
|
|
32
|
+
OxySpeechResponse,
|
|
33
|
+
} from './OxyInferenceClient';
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* An inference client bound to `oxy`'s session: it calls the same API origin
|
|
37
|
+
* with the session's current bearer on every request.
|
|
38
|
+
*/
|
|
39
|
+
export function createInferenceClient(
|
|
40
|
+
oxy: OxyServices,
|
|
41
|
+
options: Omit<OxyInferenceClientOptions, 'baseURL' | 'credential'> = {},
|
|
42
|
+
): OxyInferenceClient {
|
|
43
|
+
return new OxyInferenceClient({
|
|
44
|
+
...options,
|
|
45
|
+
baseURL: oxy.baseURL,
|
|
46
|
+
credential: () => oxy.session.accessToken,
|
|
47
|
+
});
|
|
48
|
+
}
|
package/src/models/interfaces.ts
CHANGED
|
@@ -830,7 +830,7 @@ export interface SecurityActivity {
|
|
|
830
830
|
userId: string;
|
|
831
831
|
eventType: SecurityEventType;
|
|
832
832
|
eventDescription: string;
|
|
833
|
-
metadata?: Record<string,
|
|
833
|
+
metadata?: Record<string, unknown>;
|
|
834
834
|
userAgent?: string;
|
|
835
835
|
deviceId?: string;
|
|
836
836
|
timestamp: string;
|
|
@@ -0,0 +1,637 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `OxyServer` — the Oxy client for a backend.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* import { OxyServer } from '@oxy.so/core/server';
|
|
6
|
+
*
|
|
7
|
+
* const oxy = new OxyServer({
|
|
8
|
+
* baseURL: 'https://api.oxy.so',
|
|
9
|
+
* serviceAuth: { apiKey: process.env.OXY_SERVICE_API_KEY!, apiSecret: process.env.OXY_SERVICE_API_SECRET! },
|
|
10
|
+
* });
|
|
11
|
+
*
|
|
12
|
+
* app.use('/api', oxy.middleware.auth());
|
|
13
|
+
* io.use(oxy.middleware.socket());
|
|
14
|
+
* app.use('/internal', oxy.middleware.service(), oxy.middleware.requireScope('files:read'));
|
|
15
|
+
*
|
|
16
|
+
* const people = await oxy.users.getMany(ids); // service lane when available
|
|
17
|
+
* const meta = await oxy.assets.metadataByIds(fileIds); // service-only
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* Everything `OxyServices` has, plus the service-token lane (a key pair from
|
|
21
|
+
* `serviceAuth` / `configureServiceAuth`, or — with neither — the process's
|
|
22
|
+
* attested workload identity, ADR 0026), the Express / Socket.IO middleware,
|
|
23
|
+
* account events, and the service-only methods of `assets`, `notifications`,
|
|
24
|
+
* `linkedAccounts`, `agency` and `reputation`.
|
|
25
|
+
*
|
|
26
|
+
* Node only.
|
|
27
|
+
*/
|
|
28
|
+
import { jwtDecode } from 'jwt-decode';
|
|
29
|
+
import { loadNodeCrypto } from '@oxy.so/protocol';
|
|
30
|
+
import { OxyServices, type OxyConfig } from '../OxyServices';
|
|
31
|
+
import type { HttpMethod, ServiceLane } from '../client/context';
|
|
32
|
+
import type { RequestOptions } from '../HttpService';
|
|
33
|
+
import { logger } from '../logger';
|
|
34
|
+
import { canAttestWorkloadIdentity, requestWorkloadServiceToken } from './workloadIdentity';
|
|
35
|
+
import {
|
|
36
|
+
createOxyMiddleware,
|
|
37
|
+
OXY_ACCOUNT_DELETED_EVENT_URI,
|
|
38
|
+
OXY_JWT_ISSUER,
|
|
39
|
+
OxyAccountEventError,
|
|
40
|
+
parseJsonSegment,
|
|
41
|
+
resolveServiceTokenPublicKey,
|
|
42
|
+
type OxyAccountEvent,
|
|
43
|
+
type OxyAccountEventFeedPage,
|
|
44
|
+
type OxyMiddleware,
|
|
45
|
+
type ServiceActingAsVerification,
|
|
46
|
+
type ServiceTokenJwksCache,
|
|
47
|
+
type ServiceTokenPublicJwk,
|
|
48
|
+
type VerifyAccountEventOptions,
|
|
49
|
+
} from './middleware';
|
|
50
|
+
import {
|
|
51
|
+
ServerAgencyApi,
|
|
52
|
+
ServerAssetsApi,
|
|
53
|
+
ServerLinkedAccountsApi,
|
|
54
|
+
ServerNotificationsApi,
|
|
55
|
+
ServerReputationApi,
|
|
56
|
+
} from './namespaces';
|
|
57
|
+
|
|
58
|
+
export interface OxyServerConfig extends OxyConfig {
|
|
59
|
+
/** This service's application credential. Omit to use workload identity (ADR 0026). */
|
|
60
|
+
serviceAuth?: { apiKey: string; apiSecret: string };
|
|
61
|
+
/**
|
|
62
|
+
* How this backend identifies itself on requests that carry no user session.
|
|
63
|
+
*
|
|
64
|
+
* - `'never'` (default): such requests go out anonymous.
|
|
65
|
+
* - `'when-anonymous'`: they carry this process's service token instead —
|
|
66
|
+
* from `serviceAuth`, or, with none, from workload attestation (ADR 0026).
|
|
67
|
+
* A user session, when there is one, still wins; `skipAuth` requests stay
|
|
68
|
+
* unauthenticated; a process that can produce no service token (a local
|
|
69
|
+
* checkout) keeps sending them anonymous.
|
|
70
|
+
*
|
|
71
|
+
* Why a server wants it: oxy-api charges anonymous traffic to the SOURCE
|
|
72
|
+
* ADDRESS (`rl:general`, and a +500 ms `slowDown` per request past 100 in 15
|
|
73
|
+
* minutes), and a fleet leaves through one NAT address, so every anonymous
|
|
74
|
+
* read a backend makes shares — and past the threshold pays for — one budget.
|
|
75
|
+
* A first-party service token is exempt and charged to its own application.
|
|
76
|
+
* Measured from Mention's task (2026-09-25): `GET /users/:id` 543–575 ms
|
|
77
|
+
* anonymous past the threshold, `POST /users/by-ids` 19–24 ms with the
|
|
78
|
+
* service token.
|
|
79
|
+
*/
|
|
80
|
+
serviceIdentity?: 'never' | 'when-anonymous';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* How long a failed service-token mint sends session-less requests anonymous
|
|
85
|
+
* before the next attempt, under `serviceIdentity: 'when-anonymous'`.
|
|
86
|
+
*/
|
|
87
|
+
export const ANONYMOUS_SERVICE_TOKEN_RETRY_MS = 30_000;
|
|
88
|
+
|
|
89
|
+
/** `POST /auth/service-token`. */
|
|
90
|
+
export interface ServiceTokenResponse {
|
|
91
|
+
token: string;
|
|
92
|
+
expiresIn: number;
|
|
93
|
+
appName: string;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Sentinel error raised when a service token is asked for with a known apiKey
|
|
98
|
+
* but a non-matching secret: credential drift in the caller, or a cross-tenant
|
|
99
|
+
* cache lookup attempt. Surface as a 401-equivalent.
|
|
100
|
+
*/
|
|
101
|
+
export class ServiceCredentialMismatchError extends Error {
|
|
102
|
+
constructor() {
|
|
103
|
+
super('Service credential mismatch: provided secret does not match the secret stored for this apiKey');
|
|
104
|
+
this.name = 'ServiceCredentialMismatchError';
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* One cache entry per SHA-256(apiKey) → issued token + the secret that produced
|
|
110
|
+
* it, kept as a Buffer for a constant-time compare on every hit, so an attacker
|
|
111
|
+
* who learned a peer's apiKey cannot extract its cached token by guessing.
|
|
112
|
+
*/
|
|
113
|
+
interface ServiceTokenCacheEntry {
|
|
114
|
+
token: string;
|
|
115
|
+
/** Expiry as ms since epoch. */
|
|
116
|
+
expiresAt: number;
|
|
117
|
+
secretBuf: Buffer;
|
|
118
|
+
/** In-flight mint (deduplicates concurrent callers). */
|
|
119
|
+
pending: Promise<string> | null;
|
|
120
|
+
/** The raw apiKey, so `invalidateServiceToken(apiKey)` stays synchronous. Never logged. */
|
|
121
|
+
apiKey: string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const WORKLOAD_CACHE_KEY = 'workload-identity';
|
|
125
|
+
/** A token is reused until 60s before expiry (clock drift). */
|
|
126
|
+
const TOKEN_REUSE_MARGIN_MS = 60_000;
|
|
127
|
+
|
|
128
|
+
/** Bound on remembered `appId:userId` delegation answers. */
|
|
129
|
+
const ACTING_AS_CACHE_MAX = 1000;
|
|
130
|
+
/** A positive grant is reused for 5 minutes — the revocation latency window. */
|
|
131
|
+
const ACTING_AS_GRANT_TTL_MS = 5 * 60 * 1000;
|
|
132
|
+
/** A negative answer (or a failed lookup) is reused for 1 minute. */
|
|
133
|
+
const ACTING_AS_DENIAL_TTL_MS = 60 * 1000;
|
|
134
|
+
|
|
135
|
+
export class OxyServer extends OxyServices {
|
|
136
|
+
private readonly serviceTokens = new Map<string, ServiceTokenCacheEntry>();
|
|
137
|
+
private serviceApiKey: string | null = null;
|
|
138
|
+
private serviceApiSecret: string | null = null;
|
|
139
|
+
private readonly actingAs = new Map<string, { result: ServiceActingAsVerification | null; expiresAt: number }>();
|
|
140
|
+
private readonly actingAsPending = new Map<string, Promise<ServiceActingAsVerification | null>>();
|
|
141
|
+
/** Public keys only; never private material. */
|
|
142
|
+
private readonly jwksCache: ServiceTokenJwksCache = { keys: new Map(), expiresAt: 0, lastAttemptAt: 0 };
|
|
143
|
+
/**
|
|
144
|
+
* Before this instant a request without a user session does not try to mint
|
|
145
|
+
* a service token and goes out anonymous. Set after a failed mint.
|
|
146
|
+
*/
|
|
147
|
+
private anonymousServiceTokenRetryAt = 0;
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Express and Socket.IO middleware.
|
|
151
|
+
*
|
|
152
|
+
* - `auth(options)` — Express: resolve the caller (user session or service
|
|
153
|
+
* token) onto `req.userId` / `req.user` / `req.serviceApp`.
|
|
154
|
+
* - `socket(options)` — Socket.IO: the same for a handshake.
|
|
155
|
+
* - `service(options)` — Express: service tokens only.
|
|
156
|
+
* - `requireScope(scope)` — Express, after `auth`/`service`: the service
|
|
157
|
+
* token (and its delegation, if any) must hold `scope`.
|
|
158
|
+
*/
|
|
159
|
+
readonly middleware: OxyMiddleware;
|
|
160
|
+
|
|
161
|
+
constructor(config: OxyServerConfig) {
|
|
162
|
+
super(config);
|
|
163
|
+
if (config.serviceAuth) {
|
|
164
|
+
this.configureServiceAuth(config.serviceAuth.apiKey, config.serviceAuth.apiSecret);
|
|
165
|
+
}
|
|
166
|
+
const server = this;
|
|
167
|
+
const serviceLane: ServiceLane = {
|
|
168
|
+
get available() {
|
|
169
|
+
return server.canMintServiceToken();
|
|
170
|
+
},
|
|
171
|
+
request: (...args) => this.serviceRequest(...args),
|
|
172
|
+
};
|
|
173
|
+
this.context.service = serviceLane;
|
|
174
|
+
if (config.serviceIdentity === 'when-anonymous') {
|
|
175
|
+
this.http.setAnonymousAuthProvider(() => this.serviceTokenForAnonymousRequest());
|
|
176
|
+
}
|
|
177
|
+
this.middleware = createOxyMiddleware({
|
|
178
|
+
get baseURL() {
|
|
179
|
+
return server.baseURL;
|
|
180
|
+
},
|
|
181
|
+
jwksCache: this.jwksCache,
|
|
182
|
+
validateSession: (sessionId, options) => this.session.validate(sessionId, options),
|
|
183
|
+
verifyActingAs: (appId, userId) => this.verifyActingAs(appId, userId),
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ── Server-side namespaces ───────────────────────────────────────────────
|
|
188
|
+
|
|
189
|
+
private _serverAssets?: ServerAssetsApi;
|
|
190
|
+
private _serverNotifications?: ServerNotificationsApi;
|
|
191
|
+
private _serverLinkedAccounts?: ServerLinkedAccountsApi;
|
|
192
|
+
private _serverAgency?: ServerAgencyApi;
|
|
193
|
+
private _serverReputation?: ServerReputationApi;
|
|
194
|
+
|
|
195
|
+
/** Files, plus the service-only metadata and linked-URL lookups. */
|
|
196
|
+
override get assets(): ServerAssetsApi {
|
|
197
|
+
if (!this._serverAssets) this._serverAssets = new ServerAssetsApi(this.context);
|
|
198
|
+
return this._serverAssets;
|
|
199
|
+
}
|
|
200
|
+
/** The inbox and push tokens, plus `create`. */
|
|
201
|
+
override get notifications(): ServerNotificationsApi {
|
|
202
|
+
if (!this._serverNotifications) this._serverNotifications = new ServerNotificationsApi(this.context);
|
|
203
|
+
return this._serverNotifications;
|
|
204
|
+
}
|
|
205
|
+
/** Linked external accounts, plus `forUser`. */
|
|
206
|
+
override get linkedAccounts(): ServerLinkedAccountsApi {
|
|
207
|
+
if (!this._serverLinkedAccounts) this._serverLinkedAccounts = new ServerLinkedAccountsApi(this.context);
|
|
208
|
+
return this._serverLinkedAccounts;
|
|
209
|
+
}
|
|
210
|
+
/** Delegated capabilities, plus `introspectRequesterAssertion`. */
|
|
211
|
+
override get agency(): ServerAgencyApi {
|
|
212
|
+
if (!this._serverAgency) this._serverAgency = new ServerAgencyApi(this.context);
|
|
213
|
+
return this._serverAgency;
|
|
214
|
+
}
|
|
215
|
+
/** Reputation reads, plus `award`. */
|
|
216
|
+
override get reputation(): ServerReputationApi {
|
|
217
|
+
if (!this._serverReputation) this._serverReputation = new ServerReputationApi(this.context);
|
|
218
|
+
return this._serverReputation;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// ── Service token ────────────────────────────────────────────────────────
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Set this service's application credential. Each `(apiKey, apiSecret)` pair
|
|
225
|
+
* is cached independently, so a multi-tenant host switching credentials
|
|
226
|
+
* cannot leak one tenant's token to another.
|
|
227
|
+
*/
|
|
228
|
+
configureServiceAuth(apiKey: string, apiSecret: string): void {
|
|
229
|
+
this.serviceApiKey = apiKey;
|
|
230
|
+
this.serviceApiSecret = apiSecret;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* A service token (short-lived, cached and refreshed per credential pair;
|
|
235
|
+
* concurrent callers share one mint).
|
|
236
|
+
*
|
|
237
|
+
* With no credential configured or passed, the token comes from the process's
|
|
238
|
+
* attested workload identity (ADR 0026); with neither, this throws.
|
|
239
|
+
*
|
|
240
|
+
* If the cache already holds a token for `apiKey` but `apiSecret` does not
|
|
241
|
+
* constant-time match the secret that produced it, this throws
|
|
242
|
+
* {@link ServiceCredentialMismatchError} rather than return the cached token.
|
|
243
|
+
*/
|
|
244
|
+
async serviceToken(apiKey?: string, apiSecret?: string): Promise<string> {
|
|
245
|
+
const key = apiKey || this.serviceApiKey;
|
|
246
|
+
const secret = apiSecret || this.serviceApiSecret;
|
|
247
|
+
|
|
248
|
+
if (!key || !secret) {
|
|
249
|
+
// No credential — prove what this process IS instead. The fallback, not the
|
|
250
|
+
// preference: a deployment that still has a credential keeps using it.
|
|
251
|
+
if (canAttestWorkloadIdentity()) {
|
|
252
|
+
return this.workloadServiceToken();
|
|
253
|
+
}
|
|
254
|
+
throw new Error('Service credentials not provided. Pass serviceAuth, call configureServiceAuth(), or pass apiKey and apiSecret.');
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// The apiKey is the credential's PUBLIC id (`oxy_dk_…`, the OAuth
|
|
258
|
+
// client_id), so it keys the in-memory cache as is; the secret never does.
|
|
259
|
+
const cacheKey = key;
|
|
260
|
+
const now = Date.now();
|
|
261
|
+
const providedSecretBuf = Buffer.from(secret, 'utf8');
|
|
262
|
+
|
|
263
|
+
let entry = this.serviceTokens.get(cacheKey);
|
|
264
|
+
|
|
265
|
+
if (entry) {
|
|
266
|
+
// Verify the secret on every hit, fresh token or not. Constant-time, and
|
|
267
|
+
// run on equal-length inputs even when the lengths differ.
|
|
268
|
+
const nodeCrypto = await loadNodeCrypto();
|
|
269
|
+
const stored = entry.secretBuf;
|
|
270
|
+
const lengthMatch = stored.length === providedSecretBuf.length;
|
|
271
|
+
const compareBuf = lengthMatch ? providedSecretBuf : Buffer.alloc(stored.length);
|
|
272
|
+
const equal = nodeCrypto.timingSafeEqual(stored, compareBuf);
|
|
273
|
+
if (!lengthMatch || !equal) {
|
|
274
|
+
logger.warn('[oxy.auth] Service token cache hit with mismatched secret', {
|
|
275
|
+
component: 'auth',
|
|
276
|
+
method: 'serviceToken',
|
|
277
|
+
});
|
|
278
|
+
throw new ServiceCredentialMismatchError();
|
|
279
|
+
}
|
|
280
|
+
if (entry.token && entry.expiresAt > now + TOKEN_REUSE_MARGIN_MS) {
|
|
281
|
+
return entry.token;
|
|
282
|
+
}
|
|
283
|
+
if (entry.pending) {
|
|
284
|
+
return entry.pending;
|
|
285
|
+
}
|
|
286
|
+
} else {
|
|
287
|
+
// Seed an empty entry so concurrent callers serialize on one promise.
|
|
288
|
+
entry = { token: '', expiresAt: 0, secretBuf: providedSecretBuf, pending: null, apiKey: key };
|
|
289
|
+
this.serviceTokens.set(cacheKey, entry);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const pending = this.mintServiceToken(key, secret, cacheKey, providedSecretBuf);
|
|
293
|
+
entry.pending = pending;
|
|
294
|
+
try {
|
|
295
|
+
return await pending;
|
|
296
|
+
} catch (error) {
|
|
297
|
+
// Never keep an entry that never held a token: a wrong first secret must
|
|
298
|
+
// not make the right one fail locally as a mismatch later.
|
|
299
|
+
const failed = this.serviceTokens.get(cacheKey);
|
|
300
|
+
if (failed?.pending === pending && !failed.token) {
|
|
301
|
+
this.serviceTokens.delete(cacheKey);
|
|
302
|
+
}
|
|
303
|
+
throw error;
|
|
304
|
+
} finally {
|
|
305
|
+
const settled = this.serviceTokens.get(cacheKey);
|
|
306
|
+
if (settled?.pending === pending) settled.pending = null;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Forget cached service token(s) so the next `serviceToken()` mints anew —
|
|
312
|
+
* the way back from a 401 after a credential was revoked or rotated.
|
|
313
|
+
*
|
|
314
|
+
* `apiKey` clears that credential's entry; with none, the configured
|
|
315
|
+
* credential's; with neither, every entry. Synchronous.
|
|
316
|
+
*/
|
|
317
|
+
invalidateServiceToken(apiKey?: string): void {
|
|
318
|
+
const targetKey = apiKey ?? this.serviceApiKey;
|
|
319
|
+
if (!targetKey) {
|
|
320
|
+
this.serviceTokens.clear();
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
this.serviceTokens.delete(targetKey);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* One request with this service's token. `actAs` sends `X-Oxy-User-Id`: the
|
|
328
|
+
* request is on behalf of that user (the API checks the delegation grant).
|
|
329
|
+
* Never cached unless `cache` says so. Rejects with `OxyApiError`.
|
|
330
|
+
*/
|
|
331
|
+
async serviceRequest<T>(
|
|
332
|
+
method: HttpMethod,
|
|
333
|
+
url: string,
|
|
334
|
+
data?: unknown,
|
|
335
|
+
options: RequestOptions & { actAs?: string } = {},
|
|
336
|
+
): Promise<T> {
|
|
337
|
+
const { actAs, headers, ...rest } = options;
|
|
338
|
+
const token = await this.serviceToken();
|
|
339
|
+
const merged: Record<string, string> = { ...headers, Authorization: `Bearer ${token}` };
|
|
340
|
+
if (actAs) merged['X-Oxy-User-Id'] = actAs;
|
|
341
|
+
return this.request<T>(method, url, data, { cache: false, ...rest, headers: merged });
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// ── Delegation and account events ────────────────────────────────────────
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Whether service app `appId` holds an active delegation grant to act for
|
|
348
|
+
* `userId`; the grant's scopes, or `null`. Used by `middleware.auth()` for
|
|
349
|
+
* `X-Oxy-User-Id`.
|
|
350
|
+
*
|
|
351
|
+
* Answers are cached per `appId:userId` (a grant for 5 minutes — the
|
|
352
|
+
* revocation window; a denial or failed lookup for 1 minute), bounded to the
|
|
353
|
+
* 1000 most recent pairs, and concurrent lookups of one pair share a single
|
|
354
|
+
* request.
|
|
355
|
+
*/
|
|
356
|
+
async verifyActingAs(appId: string, userId: string): Promise<ServiceActingAsVerification | null> {
|
|
357
|
+
const cacheKey = `${appId}:${userId}`;
|
|
358
|
+
const cached = this.actingAs.get(cacheKey);
|
|
359
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
360
|
+
// Refresh recency for the LRU bound.
|
|
361
|
+
this.actingAs.delete(cacheKey);
|
|
362
|
+
this.actingAs.set(cacheKey, cached);
|
|
363
|
+
return cached.result;
|
|
364
|
+
}
|
|
365
|
+
const inflight = this.actingAsPending.get(cacheKey);
|
|
366
|
+
if (inflight) return inflight;
|
|
367
|
+
|
|
368
|
+
const lookup = this.lookupActingAs(appId, userId).finally(() => {
|
|
369
|
+
this.actingAsPending.delete(cacheKey);
|
|
370
|
+
});
|
|
371
|
+
this.actingAsPending.set(cacheKey, lookup);
|
|
372
|
+
return lookup;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Account events addressed to this application (Oxy account deletions every
|
|
377
|
+
* relying application must honour, OxyHQ/Mention#1169).
|
|
378
|
+
*/
|
|
379
|
+
readonly accountEvents = {
|
|
380
|
+
/**
|
|
381
|
+
* Verify an account event token — a webhook body, or a feed entry's `token`
|
|
382
|
+
* — and return the event. Throws {@link OxyAccountEventError} unless it is
|
|
383
|
+
* an EdDSA `secevent+jwt` from Oxy's key set, issued by `oxy-auth`,
|
|
384
|
+
* addressed to this application, carrying exactly one known event. Be
|
|
385
|
+
* idempotent on `eventId`: Oxy delivers at least once.
|
|
386
|
+
*/
|
|
387
|
+
verify: (token: string, options: VerifyAccountEventOptions = {}): Promise<OxyAccountEvent> =>
|
|
388
|
+
this.verifyAccountEvent(token, options),
|
|
389
|
+
/**
|
|
390
|
+
* One page of account events for this application, oldest first — the pull
|
|
391
|
+
* feed behind the webhook. Verify each entry's `token` before acting on it.
|
|
392
|
+
*/
|
|
393
|
+
list: async (options: { after?: string; limit?: number } = {}): Promise<OxyAccountEventFeedPage> => {
|
|
394
|
+
const query: Record<string, string> = {};
|
|
395
|
+
if (options.after) query.after = options.after;
|
|
396
|
+
if (options.limit !== undefined) query.limit = String(options.limit);
|
|
397
|
+
return this.serviceRequest<OxyAccountEventFeedPage>('GET', '/account-events', query);
|
|
398
|
+
},
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
// ── Internals ────────────────────────────────────────────────────────────
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* The bearer for a request with no user session, under `serviceIdentity:
|
|
405
|
+
* 'when-anonymous'`: this process's service token, or `null` to send the
|
|
406
|
+
* request anonymous.
|
|
407
|
+
*
|
|
408
|
+
* `null`, never a throw, whenever there is no token to offer:
|
|
409
|
+
* - no key pair and no attestable workload (a local checkout);
|
|
410
|
+
* - the mint failed. The request still goes out, anonymous, and so does every
|
|
411
|
+
* request for the next {@link ANONYMOUS_SERVICE_TOKEN_RETRY_MS}, so an Oxy
|
|
412
|
+
* refusing attestation is asked once per window rather than once per read.
|
|
413
|
+
*
|
|
414
|
+
* The token comes from {@link serviceToken}: cached and single-flight, so
|
|
415
|
+
* concurrent reads share one mint.
|
|
416
|
+
*/
|
|
417
|
+
private async serviceTokenForAnonymousRequest(): Promise<string | null> {
|
|
418
|
+
if (Date.now() < this.anonymousServiceTokenRetryAt) return null;
|
|
419
|
+
if (!this.canMintServiceToken()) return null;
|
|
420
|
+
try {
|
|
421
|
+
return await this.serviceToken();
|
|
422
|
+
} catch (error) {
|
|
423
|
+
this.anonymousServiceTokenRetryAt = Date.now() + ANONYMOUS_SERVICE_TOKEN_RETRY_MS;
|
|
424
|
+
logger.warn('[oxy.auth] No service token for a request without a session; sending it anonymous', {
|
|
425
|
+
component: 'auth',
|
|
426
|
+
method: 'serviceTokenForAnonymousRequest',
|
|
427
|
+
retryInMs: ANONYMOUS_SERVICE_TOKEN_RETRY_MS,
|
|
428
|
+
error: error instanceof Error ? error.message : String(error),
|
|
429
|
+
});
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
private canMintServiceToken(): boolean {
|
|
435
|
+
return Boolean(this.serviceApiKey && this.serviceApiSecret) || canAttestWorkloadIdentity();
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
private async mintServiceToken(key: string, secret: string, cacheKey: string, secretBuf: Buffer): Promise<string> {
|
|
439
|
+
const response = await this.request<ServiceTokenResponse>(
|
|
440
|
+
'POST',
|
|
441
|
+
'/auth/service-token',
|
|
442
|
+
{ apiKey: key, apiSecret: secret },
|
|
443
|
+
{ cache: false, retry: false, skipAuth: true },
|
|
444
|
+
);
|
|
445
|
+
const expiresAt = Date.now() + response.expiresIn * 1000;
|
|
446
|
+
const entry = this.serviceTokens.get(cacheKey);
|
|
447
|
+
if (entry) {
|
|
448
|
+
entry.token = response.token;
|
|
449
|
+
entry.expiresAt = expiresAt;
|
|
450
|
+
entry.secretBuf = secretBuf;
|
|
451
|
+
} else {
|
|
452
|
+
this.serviceTokens.set(cacheKey, { token: response.token, expiresAt, secretBuf, pending: null, apiKey: key });
|
|
453
|
+
}
|
|
454
|
+
return response.token;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/** A token obtained by attestation, cached like a credential's (one identity per process). */
|
|
458
|
+
private async workloadServiceToken(): Promise<string> {
|
|
459
|
+
const entry = this.serviceTokens.get(WORKLOAD_CACHE_KEY);
|
|
460
|
+
if (entry?.token && entry.expiresAt > Date.now() + TOKEN_REUSE_MARGIN_MS) return entry.token;
|
|
461
|
+
if (entry?.pending) return entry.pending;
|
|
462
|
+
|
|
463
|
+
const seeded = entry ?? { token: '', expiresAt: 0, secretBuf: Buffer.alloc(0), pending: null, apiKey: WORKLOAD_CACHE_KEY };
|
|
464
|
+
this.serviceTokens.set(WORKLOAD_CACHE_KEY, seeded);
|
|
465
|
+
|
|
466
|
+
const pending = (async () => {
|
|
467
|
+
const granted = await requestWorkloadServiceToken({ baseUrl: this.baseURL });
|
|
468
|
+
const current = this.serviceTokens.get(WORKLOAD_CACHE_KEY);
|
|
469
|
+
if (current) {
|
|
470
|
+
current.token = granted.token;
|
|
471
|
+
current.expiresAt = Date.now() + granted.expiresIn * 1000;
|
|
472
|
+
}
|
|
473
|
+
return granted.token;
|
|
474
|
+
})();
|
|
475
|
+
seeded.pending = pending;
|
|
476
|
+
|
|
477
|
+
try {
|
|
478
|
+
return await pending;
|
|
479
|
+
} catch (error) {
|
|
480
|
+
const failed = this.serviceTokens.get(WORKLOAD_CACHE_KEY);
|
|
481
|
+
if (failed?.pending === pending && !failed.token) this.serviceTokens.delete(WORKLOAD_CACHE_KEY);
|
|
482
|
+
throw error;
|
|
483
|
+
} finally {
|
|
484
|
+
const settled = this.serviceTokens.get(WORKLOAD_CACHE_KEY);
|
|
485
|
+
if (settled?.pending === pending) settled.pending = null;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
private async lookupActingAs(appId: string, userId: string): Promise<ServiceActingAsVerification | null> {
|
|
490
|
+
try {
|
|
491
|
+
// The verify endpoint admits only a platform-trusted caller, so this
|
|
492
|
+
// carries the VERIFIER's own service token. No retry and a short timeout:
|
|
493
|
+
// it runs inside request-handling middleware, and a retry loop would
|
|
494
|
+
// multiply the latency of every delegated request. A verifier with no
|
|
495
|
+
// credential throws here and is refused below — a host that cannot prove
|
|
496
|
+
// who it is has no business learning who delegated to whom.
|
|
497
|
+
const serviceToken = await this.serviceToken();
|
|
498
|
+
const result = await this.request<ServiceActingAsVerification>(
|
|
499
|
+
'GET',
|
|
500
|
+
'/internal/service-acting-as/verify',
|
|
501
|
+
{ appId, userId },
|
|
502
|
+
{ cache: false, retry: false, timeout: 5000, headers: { Authorization: `Bearer ${serviceToken}` } },
|
|
503
|
+
);
|
|
504
|
+
const verified: ServiceActingAsVerification | null = result?.authorized
|
|
505
|
+
? { authorized: true, scopes: Array.isArray(result.scopes) ? result.scopes : [] }
|
|
506
|
+
: null;
|
|
507
|
+
this.rememberActingAs(`${appId}:${userId}`, verified, ACTING_AS_GRANT_TTL_MS);
|
|
508
|
+
return verified;
|
|
509
|
+
} catch (error) {
|
|
510
|
+
logger.warn('[oxy.auth] verifyActingAs lookup failed — caching negative result', {
|
|
511
|
+
component: 'auth',
|
|
512
|
+
method: 'verifyActingAs',
|
|
513
|
+
appId,
|
|
514
|
+
userId,
|
|
515
|
+
}, error);
|
|
516
|
+
this.rememberActingAs(`${appId}:${userId}`, null, ACTING_AS_DENIAL_TTL_MS);
|
|
517
|
+
return null;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
private rememberActingAs(cacheKey: string, result: ServiceActingAsVerification | null, ttlMs: number): void {
|
|
522
|
+
this.actingAs.delete(cacheKey);
|
|
523
|
+
this.actingAs.set(cacheKey, { result, expiresAt: Date.now() + ttlMs });
|
|
524
|
+
if (this.actingAs.size > ACTING_AS_CACHE_MAX) {
|
|
525
|
+
const oldest = this.actingAs.keys().next().value;
|
|
526
|
+
if (oldest !== undefined) this.actingAs.delete(oldest);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
private async verifyAccountEvent(token: string, options: VerifyAccountEventOptions): Promise<OxyAccountEvent> {
|
|
531
|
+
if (typeof token !== 'string' || token.length === 0 || token.length > 16 * 1024) {
|
|
532
|
+
throw new OxyAccountEventError('Account event token is missing or oversized');
|
|
533
|
+
}
|
|
534
|
+
const parts = token.split('.');
|
|
535
|
+
if (parts.length !== 3 || parts.some((part) => part.length === 0)) {
|
|
536
|
+
throw new OxyAccountEventError('Account event token is not a compact JWS');
|
|
537
|
+
}
|
|
538
|
+
const [headerB64, payloadB64, signatureB64] = parts as [string, string, string];
|
|
539
|
+
let header: Record<string, unknown>;
|
|
540
|
+
let payload: Record<string, unknown>;
|
|
541
|
+
try {
|
|
542
|
+
header = parseJsonSegment(headerB64);
|
|
543
|
+
payload = parseJsonSegment(payloadB64);
|
|
544
|
+
} catch {
|
|
545
|
+
throw new OxyAccountEventError('Account event token is malformed');
|
|
546
|
+
}
|
|
547
|
+
if (
|
|
548
|
+
Object.keys(header).length !== 3
|
|
549
|
+
|| header.alg !== 'EdDSA'
|
|
550
|
+
|| header.typ !== 'secevent+jwt'
|
|
551
|
+
|| typeof header.kid !== 'string'
|
|
552
|
+
|| !/^[A-Za-z0-9._-]{1,128}$/.test(header.kid)
|
|
553
|
+
) {
|
|
554
|
+
throw new OxyAccountEventError('Account event token header is not supported');
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
const nodeCrypto = await loadNodeCrypto();
|
|
558
|
+
let jwk: ServiceTokenPublicJwk;
|
|
559
|
+
try {
|
|
560
|
+
jwk = await resolveServiceTokenPublicKey(
|
|
561
|
+
header.kid,
|
|
562
|
+
options.jwksUrl ?? new URL('/.well-known/jwks.json', this.baseURL).toString(),
|
|
563
|
+
this.jwksCache,
|
|
564
|
+
);
|
|
565
|
+
} catch (error) {
|
|
566
|
+
throw new OxyAccountEventError(
|
|
567
|
+
error instanceof Error ? error.message.replace('Service token', 'Account event') : 'Signing key is unavailable',
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
const signature = Buffer.from(signatureB64, 'base64url');
|
|
571
|
+
if (signature.toString('base64url') !== signatureB64 || signature.length !== 64) {
|
|
572
|
+
throw new OxyAccountEventError('Account event token signature is malformed');
|
|
573
|
+
}
|
|
574
|
+
let verified = false;
|
|
575
|
+
try {
|
|
576
|
+
const publicKey = nodeCrypto.createPublicKey({ key: jwk as unknown as import('node:crypto').JsonWebKey, format: 'jwk' });
|
|
577
|
+
verified = nodeCrypto.verify(null, Buffer.from(`${headerB64}.${payloadB64}`), publicKey, signature);
|
|
578
|
+
} catch {
|
|
579
|
+
verified = false;
|
|
580
|
+
}
|
|
581
|
+
if (!verified) throw new OxyAccountEventError('Account event token signature is invalid');
|
|
582
|
+
|
|
583
|
+
if (payload.iss !== OXY_JWT_ISSUER) {
|
|
584
|
+
throw new OxyAccountEventError('Account event token issuer is not Oxy');
|
|
585
|
+
}
|
|
586
|
+
const audience = options.audience ?? await this.configuredServiceAppId();
|
|
587
|
+
if (typeof payload.aud !== 'string' || payload.aud !== audience) {
|
|
588
|
+
throw new OxyAccountEventError('Account event token is addressed to another application');
|
|
589
|
+
}
|
|
590
|
+
if (typeof payload.jti !== 'string' || payload.jti.length === 0 || payload.jti.length > 128) {
|
|
591
|
+
throw new OxyAccountEventError('Account event token has no event id');
|
|
592
|
+
}
|
|
593
|
+
if (typeof payload.iat !== 'number' || !Number.isFinite(payload.iat)) {
|
|
594
|
+
throw new OxyAccountEventError('Account event token has no issue time');
|
|
595
|
+
}
|
|
596
|
+
const events = payload.events;
|
|
597
|
+
if (typeof events !== 'object' || events === null || Array.isArray(events)) {
|
|
598
|
+
throw new OxyAccountEventError('Account event token carries no events');
|
|
599
|
+
}
|
|
600
|
+
const entries = Object.entries(events as Record<string, unknown>);
|
|
601
|
+
const deleted = (events as Record<string, unknown>)[OXY_ACCOUNT_DELETED_EVENT_URI];
|
|
602
|
+
if (entries.length !== 1 || typeof deleted !== 'object' || deleted === null || Array.isArray(deleted)) {
|
|
603
|
+
throw new OxyAccountEventError('Account event token carries an unknown event');
|
|
604
|
+
}
|
|
605
|
+
const body = deleted as Record<string, unknown>;
|
|
606
|
+
if (typeof body.userId !== 'string' || body.userId.length === 0 || body.userId.length > 128) {
|
|
607
|
+
throw new OxyAccountEventError('Account event names no account');
|
|
608
|
+
}
|
|
609
|
+
if (typeof body.occurredAt !== 'string' || Number.isNaN(Date.parse(body.occurredAt))) {
|
|
610
|
+
throw new OxyAccountEventError('Account event has no occurrence time');
|
|
611
|
+
}
|
|
612
|
+
if (body.username !== undefined && body.username !== null && typeof body.username !== 'string') {
|
|
613
|
+
throw new OxyAccountEventError('Account event username is malformed');
|
|
614
|
+
}
|
|
615
|
+
return {
|
|
616
|
+
eventId: payload.jti,
|
|
617
|
+
type: 'account.deleted',
|
|
618
|
+
userId: body.userId,
|
|
619
|
+
username: typeof body.username === 'string' ? body.username : null,
|
|
620
|
+
occurredAt: body.occurredAt,
|
|
621
|
+
retained: body.retained === true,
|
|
622
|
+
applicationId: payload.aud,
|
|
623
|
+
issuedAt: payload.iat,
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/** The `appId` claim of this service's own token. */
|
|
628
|
+
private async configuredServiceAppId(): Promise<string> {
|
|
629
|
+
const serviceToken = await this.serviceToken();
|
|
630
|
+
const appId = jwtDecode<{ appId?: unknown }>(serviceToken).appId;
|
|
631
|
+
if (typeof appId !== 'string' || appId.length === 0) {
|
|
632
|
+
throw new OxyAccountEventError('No audience given and the service credential names no application');
|
|
633
|
+
}
|
|
634
|
+
return appId;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|