@oxy.so/core 1.12.1 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/i18n/locales/en-US.json +11 -1
- package/dist/cjs/i18n/locales/es-ES.json +11 -1
- package/dist/cjs/i18n/locales/locales/en-US.json +11 -1
- package/dist/cjs/i18n/locales/locales/es-ES.json +11 -1
- package/dist/cjs/index.js +9 -6
- package/dist/cjs/mixins/OxyServices.utility.js +140 -0
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/i18n/locales/en-US.json +11 -1
- package/dist/esm/i18n/locales/es-ES.json +11 -1
- package/dist/esm/i18n/locales/locales/en-US.json +11 -1
- package/dist/esm/i18n/locales/locales/es-ES.json +11 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/mixins/OxyServices.utility.js +138 -0
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/mixins/OxyServices.utility.d.ts +84 -1
- package/dist/types/server/requesterAssertion.d.ts +2 -2
- package/package.json +1 -1
- package/src/i18n/locales/en-US.json +11 -1
- package/src/i18n/locales/es-ES.json +11 -1
- package/src/index.ts +7 -0
- package/src/mixins/OxyServices.utility.ts +208 -0
- package/src/mixins/__tests__/accountEvents.test.ts +158 -0
|
@@ -1934,7 +1934,17 @@
|
|
|
1934
1934
|
"warning": "This action cannot be undone. Your account and all associated data will be permanently deleted.",
|
|
1935
1935
|
"confirmLabel": "Type \"{{username}}\" to confirm",
|
|
1936
1936
|
"confirm": "Delete Forever",
|
|
1937
|
-
"error": "Failed to delete account"
|
|
1937
|
+
"error": "Failed to delete account",
|
|
1938
|
+
"handoff": {
|
|
1939
|
+
"commonsTitle": "Delete your account in Oxy Commons",
|
|
1940
|
+
"commonsMessage": "Your identity key is kept by Oxy Commons on this device, and deleting your account needs it. Commons will open on Settings > Delete account to finish.",
|
|
1941
|
+
"openCommons": "Open Oxy Commons",
|
|
1942
|
+
"commonsOpenFailed": "Couldn't open Oxy Commons. Open it yourself and go to Settings > Delete account.",
|
|
1943
|
+
"elsewhereTitle": "Delete your account where your identity is",
|
|
1944
|
+
"elsewhereMessage": "Deleting your account needs your identity key, and it isn't on this device. Open Oxy Commons on the device that holds your identity and go to Settings > Delete account, or open {{site}} in a browser that holds it.",
|
|
1945
|
+
"gotIt": "Got it",
|
|
1946
|
+
"identityUnreadable": "Couldn't read this device's identity storage. Unlock your device and try again."
|
|
1947
|
+
}
|
|
1938
1948
|
},
|
|
1939
1949
|
"manageAccount": {
|
|
1940
1950
|
"title": "Manage your Oxy Account",
|
|
@@ -1934,7 +1934,17 @@
|
|
|
1934
1934
|
"warning": "Esta acción no se puede deshacer. Tu cuenta y todos los datos asociados se eliminarán de forma permanente.",
|
|
1935
1935
|
"confirmLabel": "Escribe \"{{username}}\" para confirmar",
|
|
1936
1936
|
"confirm": "Eliminar para siempre",
|
|
1937
|
-
"error": "No se pudo eliminar la cuenta"
|
|
1937
|
+
"error": "No se pudo eliminar la cuenta",
|
|
1938
|
+
"handoff": {
|
|
1939
|
+
"commonsTitle": "Elimina tu cuenta en Oxy Commons",
|
|
1940
|
+
"commonsMessage": "Tu clave de identidad la guarda Oxy Commons en este dispositivo, y eliminar tu cuenta la necesita. Commons se abrirá en Ajustes > Eliminar cuenta para terminar.",
|
|
1941
|
+
"openCommons": "Abrir Oxy Commons",
|
|
1942
|
+
"commonsOpenFailed": "No se pudo abrir Oxy Commons. Ábrelo tú y ve a Ajustes > Eliminar cuenta.",
|
|
1943
|
+
"elsewhereTitle": "Elimina tu cuenta donde está tu identidad",
|
|
1944
|
+
"elsewhereMessage": "Eliminar tu cuenta necesita tu clave de identidad, y no está en este dispositivo. Abre Oxy Commons en el dispositivo que guarda tu identidad y ve a Ajustes > Eliminar cuenta, o abre {{site}} en un navegador que la guarde.",
|
|
1945
|
+
"gotIt": "Entendido",
|
|
1946
|
+
"identityUnreadable": "No se pudo leer el almacén de identidad de este dispositivo. Desbloquea el dispositivo e inténtalo de nuevo."
|
|
1947
|
+
}
|
|
1938
1948
|
},
|
|
1939
1949
|
"manageAccount": {
|
|
1940
1950
|
"title": "Gestiona tu cuenta de Oxy",
|
|
@@ -1934,7 +1934,17 @@
|
|
|
1934
1934
|
"warning": "This action cannot be undone. Your account and all associated data will be permanently deleted.",
|
|
1935
1935
|
"confirmLabel": "Type \"{{username}}\" to confirm",
|
|
1936
1936
|
"confirm": "Delete Forever",
|
|
1937
|
-
"error": "Failed to delete account"
|
|
1937
|
+
"error": "Failed to delete account",
|
|
1938
|
+
"handoff": {
|
|
1939
|
+
"commonsTitle": "Delete your account in Oxy Commons",
|
|
1940
|
+
"commonsMessage": "Your identity key is kept by Oxy Commons on this device, and deleting your account needs it. Commons will open on Settings > Delete account to finish.",
|
|
1941
|
+
"openCommons": "Open Oxy Commons",
|
|
1942
|
+
"commonsOpenFailed": "Couldn't open Oxy Commons. Open it yourself and go to Settings > Delete account.",
|
|
1943
|
+
"elsewhereTitle": "Delete your account where your identity is",
|
|
1944
|
+
"elsewhereMessage": "Deleting your account needs your identity key, and it isn't on this device. Open Oxy Commons on the device that holds your identity and go to Settings > Delete account, or open {{site}} in a browser that holds it.",
|
|
1945
|
+
"gotIt": "Got it",
|
|
1946
|
+
"identityUnreadable": "Couldn't read this device's identity storage. Unlock your device and try again."
|
|
1947
|
+
}
|
|
1938
1948
|
},
|
|
1939
1949
|
"manageAccount": {
|
|
1940
1950
|
"title": "Manage your Oxy Account",
|
|
@@ -1934,7 +1934,17 @@
|
|
|
1934
1934
|
"warning": "Esta acción no se puede deshacer. Tu cuenta y todos los datos asociados se eliminarán de forma permanente.",
|
|
1935
1935
|
"confirmLabel": "Escribe \"{{username}}\" para confirmar",
|
|
1936
1936
|
"confirm": "Eliminar para siempre",
|
|
1937
|
-
"error": "No se pudo eliminar la cuenta"
|
|
1937
|
+
"error": "No se pudo eliminar la cuenta",
|
|
1938
|
+
"handoff": {
|
|
1939
|
+
"commonsTitle": "Elimina tu cuenta en Oxy Commons",
|
|
1940
|
+
"commonsMessage": "Tu clave de identidad la guarda Oxy Commons en este dispositivo, y eliminar tu cuenta la necesita. Commons se abrirá en Ajustes > Eliminar cuenta para terminar.",
|
|
1941
|
+
"openCommons": "Abrir Oxy Commons",
|
|
1942
|
+
"commonsOpenFailed": "No se pudo abrir Oxy Commons. Ábrelo tú y ve a Ajustes > Eliminar cuenta.",
|
|
1943
|
+
"elsewhereTitle": "Elimina tu cuenta donde está tu identidad",
|
|
1944
|
+
"elsewhereMessage": "Eliminar tu cuenta necesita tu clave de identidad, y no está en este dispositivo. Abre Oxy Commons en el dispositivo que guarda tu identidad y ve a Ajustes > Eliminar cuenta, o abre {{site}} en un navegador que la guarde.",
|
|
1945
|
+
"gotIt": "Entendido",
|
|
1946
|
+
"identityUnreadable": "No se pudo leer el almacén de identidad de este dispositivo. Desbloquea el dispositivo e inténtalo de nuevo."
|
|
1947
|
+
}
|
|
1938
1948
|
},
|
|
1939
1949
|
"manageAccount": {
|
|
1940
1950
|
"title": "Gestiona tu cuenta de Oxy",
|
package/dist/cjs/index.js
CHANGED
|
@@ -18,12 +18,12 @@
|
|
|
18
18
|
* If a symbol does not appear here, it is NOT part of the public API.
|
|
19
19
|
*/
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
exports.
|
|
22
|
-
exports.
|
|
23
|
-
exports.
|
|
24
|
-
exports.
|
|
25
|
-
exports.
|
|
26
|
-
exports.packageInfo = exports.runSessionColdBoot = exports.OXY_INFERENCE_BASE_URL = exports.OxyInferenceProtocolError = exports.OxyInferenceError = exports.OxyInferenceClient = exports.TOKEN_REFRESH_LEAD_MS = exports.startTokenRefreshScheduler = exports.installAuthRefreshHandler = exports.createAuthRefreshHandler = exports.refreshDeviceSecretArm = exports.refreshPersistedSession = exports.AccountNotOnDeviceError = exports.establishIdentitySession = exports.resolveIdentityPin = exports.IDENTITY_PIN_STORAGE_KEY = exports.identityPinMatches = exports.createMemoryIdentityPinStore = exports.createNativeIdentityPinStore = exports.createWebIdentityPinStore = exports.readLocalDeviceCredential = exports.publishProvenDeviceCredential = exports.normalizeSharedDeviceSessionRead = exports.decideSharedDevicePublish = void 0;
|
|
21
|
+
exports.encryptAead = exports.hkdfSha256 = exports.RecoveryPhraseService = exports.SignatureService = exports.updateIdentityMarker = exports.readIdentityMarker = exports.IdentityUnavailableError = exports.IdentityPersistError = exports.IdentityAlreadyExistsError = exports.KeyManager = exports.sessionsArraysEqual = exports.normalizeAndSortSessions = exports.mergeSessions = exports.authenticatedApiCall = exports.withAuthErrorHandling = exports.isAuthenticationError = exports.ensureValidToken = exports.AuthenticationFailedError = exports.SessionSyncRequiredError = exports.verifyPublicCardAttestation = exports.parseAttestPayload = exports.parseIdPayload = exports.buildUserDid = exports.kindAcceptsAccountCategories = exports.isSelectableAccountCategoryId = exports.SELECTABLE_ACCOUNT_CATEGORY_IDS = exports.MAX_ACCOUNT_CATEGORIES = exports.ACCOUNT_CATEGORY_IDS = exports.normalizeProfileLinks = exports.getNormalizedUserHandle = exports.getCanonicalUserHandle = exports.normalizeUserIdentityOrNull = exports.normalizeUserIdentity = exports.getNormalizedUserId = exports.OxyAppDataIdentifierError = exports.OXY_ACCOUNT_DELETED_EVENT_URI = exports.OxyAccountEventError = exports.commonsDeliveryPlatform = exports.pushTargetsFromDelivery = exports.selectCommonsDelivery = exports.parseCommonsApprovalExpiresAt = exports.getCommonsApprovalBlockingReason = exports.ServiceCredentialMismatchError = exports.oxyClient = exports.OXY_CLOUD_URL = exports.ServiceAssetMetadataError = exports.OxyAuthenticationTimeoutError = exports.OxyAuthenticationError = exports.AssetUrlResolutionError = exports.OxyServices = void 0;
|
|
22
|
+
exports.getLanguageName = exports.getLanguageMetadata = exports.isSupportedLocale = exports.normalizeLocale = exports.getBaseLanguage = exports.FALLBACK_LOCALE = exports.SUPPORTED_LANGUAGES = exports.TopicSource = exports.TopicType = exports.SECURITY_EVENT_SEVERITY_MAP = exports.DeviceManager = exports.verifyMoveReceipt = exports.verifyMoveCommitment = exports.signMoveReceipt = exports.sealIdentityForMove = exports.parseMoveQrPayload = exports.openMovedIdentity = exports.generateMoveEphemeralKeyPair = exports.digestMoveCiphertext = exports.deriveMoveSas = exports.deriveMoveKey = exports.createMoveCommitment = exports.buildMoveQrPayload = exports.signIdentityProof = exports.digestIdentityPayload = exports.wipeOpenedIdentity = exports.wipeBytes = exports.unwrapDataKey = exports.unlockWebIdentity = exports.sealWebIdentity = exports.removeWrap = exports.parseRecoveryMaterial = exports.openWebIdentity = exports.normalizeMnemonic = exports.markWrapVerified = exports.isUsablePrfOutput = exports.generateWebIdentity = exports.generateDataKey = exports.deriveKeyEncryptionKey = exports.deriveIdentityFromRecoveryMaterial = exports.deriveIdentityFromPrivateKey = exports.deriveIdentityFromMnemonic = exports.addWrap = exports.WEB_IDENTITY_PRF_OUTPUT_LENGTH = exports.WebIdentityUnlockError = exports.WEB_IDENTITY_PRF_INPUT = exports.deriveSharedSecret = exports.AEAD_NONCE_LENGTH = exports.AEAD_KEY_LENGTH = exports.decryptAead = void 0;
|
|
23
|
+
exports.buildSearchParams = exports.buildQueryParams = exports.trustTierLabel = exports.reputationCategoryLabel = exports.accountRoleLabel = exports.accountCategoryLabel = exports.translate = exports.withRetry = exports.delay = exports.shouldAllowRequest = exports.recordSuccess = exports.recordFailure = exports.calculateBackoffInterval = exports.createCircuitBreakerState = exports.DEFAULT_CIRCUIT_BREAKER_CONFIG = exports.isRetryableError = exports.isNetworkError = exports.isServerError = exports.isRateLimitError = exports.isNotFoundError = exports.isForbiddenError = exports.isUnauthorizedError = exports.isAlreadyRegisteredError = exports.getErrorMessage = exports.getErrorStatus = exports.HttpStatus = exports.getSystemColorScheme = exports.systemPrefersDarkMode = exports.getOppositeTheme = exports.normalizeColorScheme = exports.normalizeTheme = exports.getContrastTextColor = exports.isLightColor = exports.withOpacity = exports.rgbToHex = exports.hexToRgb = exports.lightenColor = exports.darkenColor = exports.isWebBrowser = exports.isAndroid = exports.isIOS = exports.isNative = exports.isWeb = exports.setPlatformOS = exports.getPlatformOS = exports.coerceToSupportedLocale = exports.getPrimaryLanguage = exports.getUserLanguages = exports.isRTLLocale = exports.getNativeLanguageName = void 0;
|
|
24
|
+
exports.formatPublicKeyHandle = exports.getAccountFallbackHandle = exports.getAccountDisplayName = exports.createQuickAccount = exports.buildAccountsArray = exports.updateAvatarVisibility = exports.isDev = exports.consoleSink = exports.resetLoggerConfig = exports.getLoggerConfig = exports.configureLogger = exports.createLogger = exports.logger = exports.normalizeMultilineText = exports.normalizeInlineText = exports.validateAndSanitizeUserInput = exports.isValidObjectId = exports.sanitizeHTML = exports.sanitizeString = exports.isValidFileType = exports.isValidFileSize = exports.isValidDate = exports.isValidURL = exports.isValidUUID = exports.isValidObject = exports.isValidArray = exports.isRequiredBoolean = exports.isRequiredNumber = exports.isRequiredString = exports.DISPLAY_NAME_UNFLANKED_SEPARATOR_SOURCE = exports.DISPLAY_NAME_ORPHANED_MARK_SOURCE = exports.DISPLAY_NAME_DISALLOWED_SOURCE = exports.DISPLAY_NAME_ALLOWED_SCRIPTS = exports.isValidDisplayName = exports.isValidPassword = exports.isValidEmail = exports.DISPLAY_NAME_INVALID_MESSAGE = exports.MAX_DISPLAY_NAME_LENGTH = exports.PASSWORD_REGEX = exports.EMAIL_REGEX = exports.retryAsync = exports.validateRequiredFields = exports.parseHttpErrorBody = exports.isHttpRequestError = exports.handleHttpError = exports.createApiError = exports.ErrorCodes = exports.safeJsonParse = exports.buildPaginationParams = exports.buildUrl = void 0;
|
|
25
|
+
exports.AUTH_STATE_STORAGE_KEY = exports.createMemoryAuthStateStore = exports.createNativeAuthStateStore = exports.createWebAuthStateStore = exports.createAccountDialogController = exports.AccountDialogController = exports.resolveAccountDelegationAccess = exports.canSwitchIntoAccount = exports.isSwitchTargetAccount = exports.showsPrincipalHeaders = exports.buildSwitcherRows = exports.resolveDeviceContext = exports.resolveActiveContext = exports.projectDevicePrincipals = exports.directoryHandle = exports.directoryDisplayName = exports.canActivateContext = exports.accountIdsOf = exports.activeUserOf = exports.activeSessionIdOf = exports.deviceStateToClientSessions = exports.createSessionClient = exports.createSessionClientHost = exports.SessionClient = exports.isAllowedDeviceJoinOrigin = exports.isOfficialWebOrigin = exports.isLoopbackOrigin = exports.consumeOAuthReturnPath = exports.persistOAuthReturnPath = exports.clearOAuthHandshake = exports.readOAuthHandshake = exports.persistOAuthHandshake = exports.canonicalizeOAuthRedirectUri = exports.normalizeOAuthRedirectUri = exports.OXY_OAUTH_RETURN_PATH_STORAGE_KEY = exports.OXY_OAUTH_REDIRECT_URI_STORAGE_KEY = exports.OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY = exports.OXY_OAUTH_STATE_STORAGE_KEY = exports.OXY_AUTHORIZE_URL = exports.DEFAULT_OAUTH_SCOPE = exports.generatePkcePair = exports.generateOAuthState = exports.computeCodeChallenge = exports.buildOAuthAuthorizeUrl = exports.runColdBoot = exports.isOxyRpOrigin = exports.IDENTITY_WEB_ORIGIN = exports.CENTRAL_IDP_APEX = exports.registrableApex = exports.getAccountColor = void 0;
|
|
26
|
+
exports.packageInfo = exports.runSessionColdBoot = exports.OXY_INFERENCE_BASE_URL = exports.OxyInferenceProtocolError = exports.OxyInferenceError = exports.OxyInferenceClient = exports.TOKEN_REFRESH_LEAD_MS = exports.startTokenRefreshScheduler = exports.installAuthRefreshHandler = exports.createAuthRefreshHandler = exports.refreshDeviceSecretArm = exports.refreshPersistedSession = exports.AccountNotOnDeviceError = exports.establishIdentitySession = exports.resolveIdentityPin = exports.IDENTITY_PIN_STORAGE_KEY = exports.identityPinMatches = exports.createMemoryIdentityPinStore = exports.createNativeIdentityPinStore = exports.createWebIdentityPinStore = exports.readLocalDeviceCredential = exports.publishProvenDeviceCredential = exports.normalizeSharedDeviceSessionRead = exports.decideSharedDevicePublish = exports.decideSharedDeviceJoin = exports.createSharedMirroringAuthStateStore = void 0;
|
|
27
27
|
// Ensure crypto polyfills are loaded before anything else. This MUST stay the
|
|
28
28
|
// first import: `@noble/hashes` captures `globalThis.crypto` when it is
|
|
29
29
|
// evaluated, so the shim has to exist before any module below reaches it (see
|
|
@@ -55,6 +55,9 @@ var commonsDelivery_1 = require("./utils/commonsDelivery");
|
|
|
55
55
|
Object.defineProperty(exports, "selectCommonsDelivery", { enumerable: true, get: function () { return commonsDelivery_1.selectCommonsDelivery; } });
|
|
56
56
|
Object.defineProperty(exports, "pushTargetsFromDelivery", { enumerable: true, get: function () { return commonsDelivery_1.pushTargetsFromDelivery; } });
|
|
57
57
|
Object.defineProperty(exports, "commonsDeliveryPlatform", { enumerable: true, get: function () { return commonsDelivery_1.commonsDeliveryPlatform; } });
|
|
58
|
+
var OxyServices_utility_1 = require("./mixins/OxyServices.utility");
|
|
59
|
+
Object.defineProperty(exports, "OxyAccountEventError", { enumerable: true, get: function () { return OxyServices_utility_1.OxyAccountEventError; } });
|
|
60
|
+
Object.defineProperty(exports, "OXY_ACCOUNT_DELETED_EVENT_URI", { enumerable: true, get: function () { return OxyServices_utility_1.OXY_ACCOUNT_DELETED_EVENT_URI; } });
|
|
58
61
|
var OxyServices_appData_1 = require("./mixins/OxyServices.appData");
|
|
59
62
|
Object.defineProperty(exports, "OxyAppDataIdentifierError", { enumerable: true, get: function () { return OxyServices_appData_1.OxyAppDataIdentifierError; } });
|
|
60
63
|
// ---------------------------------------------------------------------------
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OxyAccountEventError = exports.OXY_ACCOUNT_DELETED_EVENT_URI = void 0;
|
|
3
4
|
exports.OxyServicesUtilityMixin = OxyServicesUtilityMixin;
|
|
4
5
|
/**
|
|
5
6
|
* Utility Methods Mixin
|
|
@@ -10,6 +11,16 @@ const jwt_decode_1 = require("jwt-decode");
|
|
|
10
11
|
const protocol_1 = require("@oxy.so/protocol");
|
|
11
12
|
const logger_1 = require("../logger");
|
|
12
13
|
const oxyServiceEnvironment_1 = require("../utils/oxyServiceEnvironment");
|
|
14
|
+
/** The SET `events` member Oxy uses for an account deletion. */
|
|
15
|
+
exports.OXY_ACCOUNT_DELETED_EVENT_URI = 'https://oxy.so/events/account.deleted';
|
|
16
|
+
/** Why an account event token was refused. `message` never contains the token. */
|
|
17
|
+
class OxyAccountEventError extends Error {
|
|
18
|
+
constructor(message) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.name = 'OxyAccountEventError';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.OxyAccountEventError = OxyAccountEventError;
|
|
13
24
|
/**
|
|
14
25
|
* Expected JWT audience for tokens issued by the Oxy auth service.
|
|
15
26
|
*/
|
|
@@ -136,6 +147,135 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
136
147
|
return null;
|
|
137
148
|
}
|
|
138
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* Verify an account event token from Oxy — the body of an account-event
|
|
152
|
+
* webhook, or an entry's `token` from `listAccountEvents` — and return the
|
|
153
|
+
* event. Throws {@link OxyAccountEventError} unless the token is an
|
|
154
|
+
* EdDSA-signed `secevent+jwt` from Oxy's published key set, issued by
|
|
155
|
+
* `oxy-auth`, addressed to this application, carrying exactly one known
|
|
156
|
+
* event. A service token (`typ: JWT`) is refused here, as this token is
|
|
157
|
+
* refused as a service token.
|
|
158
|
+
*
|
|
159
|
+
* Node only (it needs `node:crypto`). The caller must still be idempotent on
|
|
160
|
+
* `eventId`: Oxy delivers at least once, by push and by pull.
|
|
161
|
+
*/
|
|
162
|
+
async verifyAccountEvent(token, options = {}) {
|
|
163
|
+
if (typeof token !== 'string' || token.length === 0 || token.length > 16 * 1024) {
|
|
164
|
+
throw new OxyAccountEventError('Account event token is missing or oversized');
|
|
165
|
+
}
|
|
166
|
+
const parts = token.split('.');
|
|
167
|
+
if (parts.length !== 3 || parts.some((part) => part.length === 0)) {
|
|
168
|
+
throw new OxyAccountEventError('Account event token is not a compact JWS');
|
|
169
|
+
}
|
|
170
|
+
const [headerB64, payloadB64, signatureB64] = parts;
|
|
171
|
+
let header;
|
|
172
|
+
let payload;
|
|
173
|
+
try {
|
|
174
|
+
header = parseJsonSegment(headerB64);
|
|
175
|
+
payload = parseJsonSegment(payloadB64);
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
throw new OxyAccountEventError('Account event token is malformed');
|
|
179
|
+
}
|
|
180
|
+
if (Object.keys(header).length !== 3
|
|
181
|
+
|| header.alg !== 'EdDSA'
|
|
182
|
+
|| header.typ !== 'secevent+jwt'
|
|
183
|
+
|| typeof header.kid !== 'string'
|
|
184
|
+
|| !/^[A-Za-z0-9._-]{1,128}$/.test(header.kid)) {
|
|
185
|
+
throw new OxyAccountEventError('Account event token header is not supported');
|
|
186
|
+
}
|
|
187
|
+
const nodeCrypto = await (0, protocol_1.loadNodeCrypto)();
|
|
188
|
+
let jwk;
|
|
189
|
+
try {
|
|
190
|
+
jwk = await resolveServiceTokenPublicKey(header.kid, options.jwksUrl ?? new URL('/.well-known/jwks.json', this.getBaseURL()).toString(), this._serviceTokenJwksCache);
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
throw new OxyAccountEventError(error instanceof Error ? error.message.replace('Service token', 'Account event') : 'Signing key is unavailable');
|
|
194
|
+
}
|
|
195
|
+
const signature = Buffer.from(signatureB64, 'base64url');
|
|
196
|
+
if (signature.toString('base64url') !== signatureB64 || signature.length !== 64) {
|
|
197
|
+
throw new OxyAccountEventError('Account event token signature is malformed');
|
|
198
|
+
}
|
|
199
|
+
let verified = false;
|
|
200
|
+
try {
|
|
201
|
+
const publicKey = nodeCrypto.createPublicKey({ key: jwk, format: 'jwk' });
|
|
202
|
+
verified = nodeCrypto.verify(null, Buffer.from(`${headerB64}.${payloadB64}`), publicKey, signature);
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
verified = false;
|
|
206
|
+
}
|
|
207
|
+
if (!verified)
|
|
208
|
+
throw new OxyAccountEventError('Account event token signature is invalid');
|
|
209
|
+
if (payload.iss !== OXY_JWT_ISSUER) {
|
|
210
|
+
throw new OxyAccountEventError('Account event token issuer is not Oxy');
|
|
211
|
+
}
|
|
212
|
+
const audience = options.audience ?? await this._configuredServiceAppId();
|
|
213
|
+
if (typeof payload.aud !== 'string' || payload.aud !== audience) {
|
|
214
|
+
throw new OxyAccountEventError('Account event token is addressed to another application');
|
|
215
|
+
}
|
|
216
|
+
if (typeof payload.jti !== 'string' || payload.jti.length === 0 || payload.jti.length > 128) {
|
|
217
|
+
throw new OxyAccountEventError('Account event token has no event id');
|
|
218
|
+
}
|
|
219
|
+
if (typeof payload.iat !== 'number' || !Number.isFinite(payload.iat)) {
|
|
220
|
+
throw new OxyAccountEventError('Account event token has no issue time');
|
|
221
|
+
}
|
|
222
|
+
const events = payload.events;
|
|
223
|
+
if (typeof events !== 'object' || events === null || Array.isArray(events)) {
|
|
224
|
+
throw new OxyAccountEventError('Account event token carries no events');
|
|
225
|
+
}
|
|
226
|
+
const entries = Object.entries(events);
|
|
227
|
+
const deleted = events[exports.OXY_ACCOUNT_DELETED_EVENT_URI];
|
|
228
|
+
if (entries.length !== 1 || typeof deleted !== 'object' || deleted === null || Array.isArray(deleted)) {
|
|
229
|
+
throw new OxyAccountEventError('Account event token carries an unknown event');
|
|
230
|
+
}
|
|
231
|
+
const body = deleted;
|
|
232
|
+
if (typeof body.userId !== 'string' || body.userId.length === 0 || body.userId.length > 128) {
|
|
233
|
+
throw new OxyAccountEventError('Account event names no account');
|
|
234
|
+
}
|
|
235
|
+
if (typeof body.occurredAt !== 'string' || Number.isNaN(Date.parse(body.occurredAt))) {
|
|
236
|
+
throw new OxyAccountEventError('Account event has no occurrence time');
|
|
237
|
+
}
|
|
238
|
+
if (body.username !== undefined && body.username !== null && typeof body.username !== 'string') {
|
|
239
|
+
throw new OxyAccountEventError('Account event username is malformed');
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
eventId: payload.jti,
|
|
243
|
+
type: 'account.deleted',
|
|
244
|
+
userId: body.userId,
|
|
245
|
+
username: typeof body.username === 'string' ? body.username : null,
|
|
246
|
+
occurredAt: body.occurredAt,
|
|
247
|
+
retained: body.retained === true,
|
|
248
|
+
applicationId: payload.aud,
|
|
249
|
+
issuedAt: payload.iat,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* One page of account events addressed to this application, oldest first,
|
|
254
|
+
* from Oxy's pull feed — the reconciliation path behind the webhook. Needs
|
|
255
|
+
* the service credential (`configureServiceAuth`). Verify each entry's
|
|
256
|
+
* `token` with {@link verifyAccountEvent} before acting on it.
|
|
257
|
+
*/
|
|
258
|
+
async listAccountEvents(options = {}) {
|
|
259
|
+
const serviceToken = await this.getServiceToken();
|
|
260
|
+
const query = {};
|
|
261
|
+
if (options.after)
|
|
262
|
+
query.after = options.after;
|
|
263
|
+
if (options.limit !== undefined)
|
|
264
|
+
query.limit = String(options.limit);
|
|
265
|
+
return this.makeRequest('GET', '/account-events', query, {
|
|
266
|
+
cache: false,
|
|
267
|
+
headers: { Authorization: `Bearer ${serviceToken}` },
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
/** @internal The `appId` claim of this client's own service token. */
|
|
271
|
+
async _configuredServiceAppId() {
|
|
272
|
+
const serviceToken = await this.getServiceToken();
|
|
273
|
+
const appId = (0, jwt_decode_1.jwtDecode)(serviceToken).appId;
|
|
274
|
+
if (typeof appId !== 'string' || appId.length === 0) {
|
|
275
|
+
throw new OxyAccountEventError('No audience given and the service credential names no application');
|
|
276
|
+
}
|
|
277
|
+
return appId;
|
|
278
|
+
}
|
|
139
279
|
/**
|
|
140
280
|
* Express.js authentication middleware
|
|
141
281
|
*
|