@oxyhq/core 1.11.12 → 1.11.13
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/CrossDomainAuth.js +3 -1
- package/dist/cjs/HttpService.js +214 -33
- package/dist/cjs/OxyServices.base.js +9 -0
- package/dist/cjs/OxyServices.js +8 -3
- package/dist/cjs/crypto/index.js +3 -1
- package/dist/cjs/crypto/keyManager.js +476 -172
- package/dist/cjs/crypto/polyfill.js +14 -65
- package/dist/cjs/crypto/recoveryPhrase.js +30 -11
- package/dist/cjs/crypto/signatureService.js +25 -60
- package/dist/cjs/i18n/locales/en-US.json +46 -1
- package/dist/cjs/i18n/locales/es-ES.json +46 -1
- package/dist/cjs/i18n/locales/locales/en-US.json +46 -1
- package/dist/cjs/i18n/locales/locales/es-ES.json +46 -1
- package/dist/cjs/index.js +7 -2
- package/dist/cjs/mixins/OxyServices.assets.js +9 -4
- package/dist/cjs/mixins/OxyServices.auth.js +27 -0
- package/dist/cjs/mixins/OxyServices.contacts.js +50 -0
- package/dist/cjs/mixins/OxyServices.features.js +0 -11
- package/dist/cjs/mixins/OxyServices.fedcm.js +4 -3
- package/dist/cjs/mixins/OxyServices.language.js +5 -36
- package/dist/cjs/mixins/OxyServices.redirect.js +6 -2
- package/dist/cjs/mixins/OxyServices.security.js +13 -2
- package/dist/cjs/mixins/OxyServices.user.js +59 -38
- package/dist/cjs/mixins/OxyServices.utility.js +19 -43
- package/dist/cjs/mixins/index.js +11 -3
- package/dist/cjs/utils/accountUtils.js +71 -2
- package/dist/cjs/utils/deviceManager.js +5 -36
- package/dist/cjs/utils/platformCrypto.js +165 -0
- package/dist/cjs/utils/platformCrypto.native.js +123 -0
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/CrossDomainAuth.js +3 -1
- package/dist/esm/HttpService.js +215 -34
- package/dist/esm/OxyServices.base.js +9 -0
- package/dist/esm/OxyServices.js +8 -3
- package/dist/esm/crypto/index.js +1 -1
- package/dist/esm/crypto/keyManager.js +473 -138
- package/dist/esm/crypto/polyfill.js +14 -32
- package/dist/esm/crypto/recoveryPhrase.js +30 -11
- package/dist/esm/crypto/signatureService.js +25 -27
- package/dist/esm/i18n/locales/en-US.json +46 -1
- package/dist/esm/i18n/locales/es-ES.json +46 -1
- package/dist/esm/i18n/locales/locales/en-US.json +46 -1
- package/dist/esm/i18n/locales/locales/es-ES.json +46 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/mixins/OxyServices.assets.js +9 -4
- package/dist/esm/mixins/OxyServices.auth.js +27 -0
- package/dist/esm/mixins/OxyServices.contacts.js +47 -0
- package/dist/esm/mixins/OxyServices.features.js +0 -11
- package/dist/esm/mixins/OxyServices.fedcm.js +4 -3
- package/dist/esm/mixins/OxyServices.language.js +5 -3
- package/dist/esm/mixins/OxyServices.redirect.js +6 -2
- package/dist/esm/mixins/OxyServices.security.js +13 -2
- package/dist/esm/mixins/OxyServices.user.js +59 -38
- package/dist/esm/mixins/OxyServices.utility.js +19 -10
- package/dist/esm/mixins/index.js +11 -3
- package/dist/esm/utils/accountUtils.js +67 -1
- package/dist/esm/utils/deviceManager.js +5 -3
- package/dist/esm/utils/platformCrypto.js +125 -0
- package/dist/esm/utils/platformCrypto.native.js +80 -0
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/HttpService.d.ts +47 -3
- package/dist/types/OxyServices.base.d.ts +7 -0
- package/dist/types/OxyServices.d.ts +36 -3
- package/dist/types/crypto/index.d.ts +1 -1
- package/dist/types/crypto/keyManager.d.ts +110 -9
- package/dist/types/crypto/polyfill.d.ts +3 -1
- package/dist/types/crypto/recoveryPhrase.d.ts +31 -7
- package/dist/types/crypto/signatureService.d.ts +4 -0
- package/dist/types/index.d.ts +4 -3
- package/dist/types/mixins/OxyServices.analytics.d.ts +1 -0
- package/dist/types/mixins/OxyServices.assets.d.ts +6 -10
- package/dist/types/mixins/OxyServices.auth.d.ts +16 -0
- package/dist/types/mixins/OxyServices.contacts.d.ts +99 -0
- package/dist/types/mixins/OxyServices.developer.d.ts +1 -0
- package/dist/types/mixins/OxyServices.devices.d.ts +1 -0
- package/dist/types/mixins/OxyServices.features.d.ts +2 -7
- package/dist/types/mixins/OxyServices.fedcm.d.ts +1 -0
- package/dist/types/mixins/OxyServices.karma.d.ts +1 -0
- package/dist/types/mixins/OxyServices.language.d.ts +1 -0
- package/dist/types/mixins/OxyServices.location.d.ts +1 -0
- package/dist/types/mixins/OxyServices.managedAccounts.d.ts +1 -0
- package/dist/types/mixins/OxyServices.payment.d.ts +1 -0
- package/dist/types/mixins/OxyServices.popup.d.ts +1 -0
- package/dist/types/mixins/OxyServices.privacy.d.ts +1 -0
- package/dist/types/mixins/OxyServices.redirect.d.ts +1 -0
- package/dist/types/mixins/OxyServices.security.d.ts +1 -0
- package/dist/types/mixins/OxyServices.topics.d.ts +1 -0
- package/dist/types/mixins/OxyServices.user.d.ts +28 -11
- package/dist/types/mixins/OxyServices.utility.d.ts +1 -0
- package/dist/types/mixins/index.d.ts +52 -4
- package/dist/types/models/interfaces.d.ts +62 -3
- package/dist/types/utils/accountUtils.d.ts +41 -1
- package/dist/types/utils/platformCrypto.d.ts +87 -0
- package/dist/types/utils/platformCrypto.native.d.ts +54 -0
- package/package.json +28 -1
- package/src/CrossDomainAuth.ts +12 -10
- package/src/HttpService.ts +251 -40
- package/src/OxyServices.base.ts +10 -0
- package/src/OxyServices.ts +9 -4
- package/src/crypto/__tests__/keyManager.test.ts +336 -0
- package/src/crypto/index.ts +6 -1
- package/src/crypto/keyManager.ts +529 -151
- package/src/crypto/polyfill.ts +14 -34
- package/src/crypto/recoveryPhrase.ts +56 -17
- package/src/crypto/signatureService.ts +25 -30
- package/src/i18n/locales/en-US.json +46 -1
- package/src/i18n/locales/es-ES.json +46 -1
- package/src/index.ts +16 -3
- package/src/mixins/OxyServices.assets.ts +15 -11
- package/src/mixins/OxyServices.auth.ts +28 -0
- package/src/mixins/OxyServices.contacts.ts +73 -0
- package/src/mixins/OxyServices.features.ts +2 -12
- package/src/mixins/OxyServices.fedcm.ts +4 -3
- package/src/mixins/OxyServices.language.ts +6 -4
- package/src/mixins/OxyServices.redirect.ts +6 -2
- package/src/mixins/OxyServices.security.ts +18 -8
- package/src/mixins/OxyServices.user.ts +72 -49
- package/src/mixins/OxyServices.utility.ts +19 -10
- package/src/mixins/index.ts +58 -7
- package/src/models/interfaces.ts +65 -3
- package/src/utils/accountUtils.ts +82 -2
- package/src/utils/deviceManager.ts +7 -4
- package/src/utils/platformCrypto.native.ts +101 -0
- package/src/utils/platformCrypto.ts +145 -0
package/dist/cjs/index.js
CHANGED
|
@@ -29,8 +29,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
29
29
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
30
30
|
};
|
|
31
31
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
-
exports.
|
|
33
|
-
exports.createQuickAccount = exports.buildAccountsArray = exports.updateAvatarVisibility = exports.logPerformance = exports.logPayment = exports.logDevice = exports.logUser = exports.logSession = exports.logApi = exports.logAuth = exports.LogLevel = exports.logger = exports.retryAsync = exports.validateRequiredFields = exports.handleHttpError = exports.createApiError = exports.ErrorCodes = exports.packageInfo = exports.sessionsArraysEqual = exports.normalizeAndSortSessions = exports.mergeSessions = exports.authenticatedApiCall = exports.withAuthErrorHandling = exports.isAuthenticationError = exports.ensureValidToken = exports.AuthenticationFailedError = exports.SessionSyncRequiredError = exports.translate = exports.createDebugLogger = exports.debugError = exports.debugWarn = exports.debugLog = exports.isDev = exports.withRetry = exports.delay = exports.shouldAllowRequest = exports.recordSuccess = exports.recordFailure = exports.calculateBackoffInterval = exports.createCircuitBreakerState = void 0;
|
|
32
|
+
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.isAndroid = exports.isIOS = exports.isNative = exports.isWeb = exports.setPlatformOS = exports.getPlatformOS = exports.normalizeLanguageCode = exports.getNativeLanguageName = exports.getLanguageName = exports.getLanguageMetadata = exports.SUPPORTED_LANGUAGES = exports.DeviceManager = exports.TopicSource = exports.TopicType = exports.IdentityPersistError = exports.IdentityAlreadyExistsError = exports.RecoveryPhraseService = exports.SignatureService = exports.KeyManager = exports.createCrossDomainAuth = exports.CrossDomainAuth = exports.createAuthManager = exports.AuthManager = exports.oxyClient = exports.OXY_CLOUD_URL = exports.OxyAuthenticationTimeoutError = exports.OxyAuthenticationError = exports.OxyServices = void 0;
|
|
33
|
+
exports.formatPublicKeyHandle = exports.getAccountFallbackHandle = exports.getAccountDisplayName = exports.createQuickAccount = exports.buildAccountsArray = exports.updateAvatarVisibility = exports.logPerformance = exports.logPayment = exports.logDevice = exports.logUser = exports.logSession = exports.logApi = exports.logAuth = exports.LogLevel = exports.logger = exports.retryAsync = exports.validateRequiredFields = exports.handleHttpError = exports.createApiError = exports.ErrorCodes = exports.packageInfo = exports.sessionsArraysEqual = exports.normalizeAndSortSessions = exports.mergeSessions = exports.authenticatedApiCall = exports.withAuthErrorHandling = exports.isAuthenticationError = exports.ensureValidToken = exports.AuthenticationFailedError = exports.SessionSyncRequiredError = exports.translate = exports.createDebugLogger = exports.debugError = exports.debugWarn = exports.debugLog = exports.isDev = exports.withRetry = exports.delay = exports.shouldAllowRequest = exports.recordSuccess = exports.recordFailure = exports.calculateBackoffInterval = exports.createCircuitBreakerState = exports.DEFAULT_CIRCUIT_BREAKER_CONFIG = exports.isRetryableError = void 0;
|
|
34
34
|
// Ensure crypto polyfills are loaded before anything else
|
|
35
35
|
require("./crypto/polyfill");
|
|
36
36
|
// --- Core API Client ---
|
|
@@ -53,6 +53,8 @@ var crypto_1 = require("./crypto");
|
|
|
53
53
|
Object.defineProperty(exports, "KeyManager", { enumerable: true, get: function () { return crypto_1.KeyManager; } });
|
|
54
54
|
Object.defineProperty(exports, "SignatureService", { enumerable: true, get: function () { return crypto_1.SignatureService; } });
|
|
55
55
|
Object.defineProperty(exports, "RecoveryPhraseService", { enumerable: true, get: function () { return crypto_1.RecoveryPhraseService; } });
|
|
56
|
+
Object.defineProperty(exports, "IdentityAlreadyExistsError", { enumerable: true, get: function () { return crypto_1.IdentityAlreadyExistsError; } });
|
|
57
|
+
Object.defineProperty(exports, "IdentityPersistError", { enumerable: true, get: function () { return crypto_1.IdentityPersistError; } });
|
|
56
58
|
// --- Models & Types ---
|
|
57
59
|
__exportStar(require("./models/interfaces"), exports);
|
|
58
60
|
__exportStar(require("./models/session"), exports);
|
|
@@ -165,6 +167,9 @@ Object.defineProperty(exports, "updateAvatarVisibility", { enumerable: true, get
|
|
|
165
167
|
var accountUtils_1 = require("./utils/accountUtils");
|
|
166
168
|
Object.defineProperty(exports, "buildAccountsArray", { enumerable: true, get: function () { return accountUtils_1.buildAccountsArray; } });
|
|
167
169
|
Object.defineProperty(exports, "createQuickAccount", { enumerable: true, get: function () { return accountUtils_1.createQuickAccount; } });
|
|
170
|
+
Object.defineProperty(exports, "getAccountDisplayName", { enumerable: true, get: function () { return accountUtils_1.getAccountDisplayName; } });
|
|
171
|
+
Object.defineProperty(exports, "getAccountFallbackHandle", { enumerable: true, get: function () { return accountUtils_1.getAccountFallbackHandle; } });
|
|
172
|
+
Object.defineProperty(exports, "formatPublicKeyHandle", { enumerable: true, get: function () { return accountUtils_1.formatPublicKeyHandle; } });
|
|
168
173
|
// Default export
|
|
169
174
|
const OxyServices_3 = require("./OxyServices");
|
|
170
175
|
exports.default = OxyServices_3.OxyServices;
|
|
@@ -153,15 +153,20 @@ function OxyServicesAssetsMixin(Base) {
|
|
|
153
153
|
const fileSize = 'size' in file && file.size ? file.size : 0;
|
|
154
154
|
try {
|
|
155
155
|
const formData = new FormData();
|
|
156
|
-
if ('
|
|
157
|
-
// React Native file descriptor — RN's FormData handles {uri, type, name} natively
|
|
156
|
+
if (typeof File !== 'undefined' && file instanceof File) {
|
|
158
157
|
formData.append('file', file, fileName);
|
|
159
158
|
}
|
|
160
|
-
else if (file instanceof Blob) {
|
|
159
|
+
else if (typeof Blob !== 'undefined' && file instanceof Blob) {
|
|
160
|
+
formData.append('file', file, fileName);
|
|
161
|
+
}
|
|
162
|
+
else if ('uri' in file && typeof file.uri === 'string') {
|
|
163
|
+
// React Native file descriptor — RN's FormData handles {uri, type, name} natively.
|
|
164
|
+
// It reads the file from disk during the multipart request — no in-JS Blob
|
|
165
|
+
// conversion (which would fail on Hermes for ArrayBuffer-backed Blobs).
|
|
161
166
|
formData.append('file', file, fileName);
|
|
162
167
|
}
|
|
163
168
|
else {
|
|
164
|
-
|
|
169
|
+
throw new Error('Unsupported file input: expected File, Blob, or { uri, type?, name?, size? } descriptor');
|
|
165
170
|
}
|
|
166
171
|
if (visibility) {
|
|
167
172
|
formData.append('visibility', visibility);
|
|
@@ -10,6 +10,12 @@ function OxyServicesAuthMixin(Base) {
|
|
|
10
10
|
/** @internal */ this._serviceTokenExp = 0;
|
|
11
11
|
/** @internal */ this._serviceApiKey = null;
|
|
12
12
|
/** @internal */ this._serviceApiSecret = null;
|
|
13
|
+
/**
|
|
14
|
+
* In-flight promise for service token fetch. Used to deduplicate concurrent
|
|
15
|
+
* calls to getServiceToken() — pattern mirrors AuthManager.refreshToken().
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
18
|
+
this._serviceTokenPromise = null;
|
|
13
19
|
}
|
|
14
20
|
/**
|
|
15
21
|
* Configure service credentials for internal service-to-service communication.
|
|
@@ -30,6 +36,9 @@ function OxyServicesAuthMixin(Base) {
|
|
|
30
36
|
* Get a service token for internal service-to-service communication.
|
|
31
37
|
* Tokens are short-lived (1h) and automatically cached/refreshed.
|
|
32
38
|
*
|
|
39
|
+
* Concurrent callers share a single in-flight request to avoid hammering
|
|
40
|
+
* `/auth/service-token` when the cache is empty or expired.
|
|
41
|
+
*
|
|
33
42
|
* @param apiKey - DeveloperApp API key (optional if configureServiceAuth was called)
|
|
34
43
|
* @param apiSecret - DeveloperApp API secret (optional if configureServiceAuth was called)
|
|
35
44
|
*/
|
|
@@ -43,6 +52,24 @@ function OxyServicesAuthMixin(Base) {
|
|
|
43
52
|
if (this._serviceToken && this._serviceTokenExp > Date.now() + 60000) {
|
|
44
53
|
return this._serviceToken;
|
|
45
54
|
}
|
|
55
|
+
// If a fetch is already in-flight, share the same promise
|
|
56
|
+
if (this._serviceTokenPromise) {
|
|
57
|
+
return this._serviceTokenPromise;
|
|
58
|
+
}
|
|
59
|
+
this._serviceTokenPromise = this._doFetchServiceToken(key, secret);
|
|
60
|
+
try {
|
|
61
|
+
return await this._serviceTokenPromise;
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
this._serviceTokenPromise = null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Perform the actual /auth/service-token request and cache the result.
|
|
69
|
+
* Separated so getServiceToken() can deduplicate concurrent calls.
|
|
70
|
+
* @internal
|
|
71
|
+
*/
|
|
72
|
+
async _doFetchServiceToken(key, secret) {
|
|
46
73
|
const response = await this.makeRequest('POST', '/auth/service-token', { apiKey: key, apiSecret: secret }, { cache: false, retry: false });
|
|
47
74
|
this._serviceToken = response.token;
|
|
48
75
|
this._serviceTokenExp = Date.now() + response.expiresIn * 1000;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Contact Discovery Mixin
|
|
4
|
+
*
|
|
5
|
+
* Privacy-preserving discovery of which address-book contacts are on Oxy.
|
|
6
|
+
*
|
|
7
|
+
* The client hashes emails and phones locally before calling the API.
|
|
8
|
+
* The server responds with only Oxy user IDs and the hashes that matched,
|
|
9
|
+
* so the consumer can map each match back to the local contact that
|
|
10
|
+
* produced it.
|
|
11
|
+
*
|
|
12
|
+
* Hashing rules (must match the server `utils/contactHash.ts` exactly):
|
|
13
|
+
* - SHA-256, hex-encoded, lowercase
|
|
14
|
+
* - Email: `value.trim().toLowerCase()` then digest
|
|
15
|
+
* - Phone: trim → keep a single leading "+" → strip non-digits → prepend "+"
|
|
16
|
+
* if missing → digest
|
|
17
|
+
*
|
|
18
|
+
* Mobile clients can compute these digests with `expo-crypto`'s
|
|
19
|
+
* `digestStringAsync(SHA256, value, { encoding: HEX })`. Web clients should
|
|
20
|
+
* use `SubtleCrypto.digest('SHA-256', ...)`.
|
|
21
|
+
*/
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.OxyServicesContactsMixin = OxyServicesContactsMixin;
|
|
24
|
+
function OxyServicesContactsMixin(Base) {
|
|
25
|
+
return class extends Base {
|
|
26
|
+
constructor(...args) {
|
|
27
|
+
super(...args);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Discover which of the caller's contacts are on Oxy.
|
|
31
|
+
*
|
|
32
|
+
* @param hashedEmails - SHA-256 hex digests of normalized emails.
|
|
33
|
+
* @param hashedPhones - SHA-256 hex digests of normalized phone numbers.
|
|
34
|
+
* @returns Matches mapping each hashed identifier to the Oxy user ID it
|
|
35
|
+
* resolved to. Empty arrays are valid for either parameter, but at
|
|
36
|
+
* least one must be non-empty.
|
|
37
|
+
*
|
|
38
|
+
* The server enforces a 200-hash cap per channel per request — callers
|
|
39
|
+
* should batch larger address books client-side.
|
|
40
|
+
*/
|
|
41
|
+
async discoverContacts(hashedEmails, hashedPhones) {
|
|
42
|
+
try {
|
|
43
|
+
return await this.makeRequest('POST', '/contacts/discover', { hashedEmails, hashedPhones }, { cache: false });
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
throw this.handleError(error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -294,16 +294,5 @@ function OxyServicesFeaturesMixin(Base) {
|
|
|
294
294
|
throw this.handleError(error);
|
|
295
295
|
}
|
|
296
296
|
}
|
|
297
|
-
// ==================
|
|
298
|
-
// ACCOUNT
|
|
299
|
-
// ==================
|
|
300
|
-
/**
|
|
301
|
-
* Delete user account (requires password confirmation)
|
|
302
|
-
*/
|
|
303
|
-
async deleteAccount(password) {
|
|
304
|
-
return this.withAuthRetry(async () => {
|
|
305
|
-
await this.makeRequest('DELETE', '/account', { password }, { cache: false });
|
|
306
|
-
}, 'deleteAccount');
|
|
307
|
-
}
|
|
308
297
|
};
|
|
309
298
|
}
|
|
@@ -315,10 +315,11 @@ function OxyServicesFedCMMixin(Base) {
|
|
|
315
315
|
{
|
|
316
316
|
configURL: options.configURL,
|
|
317
317
|
clientId: options.clientId,
|
|
318
|
-
//
|
|
319
|
-
|
|
318
|
+
// Older browsers read `nonce` at the top level; Chrome 145+
|
|
319
|
+
// expects it inside `params`. Send both for full coverage.
|
|
320
|
+
nonce: options.nonce,
|
|
320
321
|
params: {
|
|
321
|
-
nonce: options.nonce,
|
|
322
|
+
nonce: options.nonce,
|
|
322
323
|
},
|
|
323
324
|
...(options.loginHint && { loginHint: options.loginHint }),
|
|
324
325
|
},
|
|
@@ -1,43 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
3
|
exports.OxyServicesLanguageMixin = OxyServicesLanguageMixin;
|
|
37
4
|
/**
|
|
38
5
|
* Language Methods Mixin
|
|
39
6
|
*/
|
|
40
7
|
const languageUtils_1 = require("../utils/languageUtils");
|
|
8
|
+
const platformCrypto_1 = require("../utils/platformCrypto");
|
|
41
9
|
const debugUtils_1 = require("../shared/utils/debugUtils");
|
|
42
10
|
function OxyServicesLanguageMixin(Base) {
|
|
43
11
|
return class extends Base {
|
|
@@ -51,9 +19,10 @@ function OxyServicesLanguageMixin(Base) {
|
|
|
51
19
|
const isReactNative = typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
|
|
52
20
|
if (isReactNative) {
|
|
53
21
|
try {
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
|
|
22
|
+
// `loadAsyncStorage` is per-platform: the RN variant statically imports
|
|
23
|
+
// @react-native-async-storage/async-storage, the default variant throws
|
|
24
|
+
// (never called outside RN because of the `isReactNative` gate above).
|
|
25
|
+
const asyncStorageModule = await (0, platformCrypto_1.loadAsyncStorage)();
|
|
57
26
|
const storage = asyncStorageModule.default;
|
|
58
27
|
return {
|
|
59
28
|
getItem: storage.getItem.bind(storage),
|
|
@@ -153,12 +153,16 @@ function OxyServicesRedirectAuthMixin(Base) {
|
|
|
153
153
|
// Store tokens
|
|
154
154
|
this.storeTokens(accessToken, sessionId);
|
|
155
155
|
this.httpService.setTokens(accessToken);
|
|
156
|
-
// Build session response (minimal
|
|
156
|
+
// Build session response (minimal — full user data is fetched separately
|
|
157
|
+
// by the caller via getCurrentUser() once tokens are stored).
|
|
157
158
|
const session = {
|
|
158
159
|
sessionId,
|
|
159
160
|
deviceId: '', // Not available in redirect flow
|
|
160
161
|
expiresAt: expiresAt || new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
|
|
161
|
-
user
|
|
162
|
+
// Placeholder user — caller MUST fetch real user data via getCurrentUser()
|
|
163
|
+
// before exposing this session to the application. The empty id signals
|
|
164
|
+
// that the user payload has not yet been populated.
|
|
165
|
+
user: { id: '', username: '' },
|
|
162
166
|
};
|
|
163
167
|
// Clean up URL (remove auth parameters)
|
|
164
168
|
this.cleanAuthCallbackUrl(url);
|
|
@@ -23,8 +23,19 @@ function OxyServicesSecurityMixin(Base) {
|
|
|
23
23
|
params.offset = offset;
|
|
24
24
|
if (eventType)
|
|
25
25
|
params.eventType = eventType;
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
// The API responds with the standard paginated envelope:
|
|
27
|
+
// { data: SecurityActivity[], pagination: { total, limit, offset, hasMore } }
|
|
28
|
+
// SecurityActivityResponse is the flattened shape consumers expect.
|
|
29
|
+
const raw = await this.makeRequest('GET', '/security/activity', params, { cache: false });
|
|
30
|
+
const requestedLimit = typeof params.limit === 'number' ? params.limit : 0;
|
|
31
|
+
const requestedOffset = typeof params.offset === 'number' ? params.offset : 0;
|
|
32
|
+
return {
|
|
33
|
+
data: raw.data ?? [],
|
|
34
|
+
total: raw.pagination?.total ?? raw.data?.length ?? 0,
|
|
35
|
+
limit: raw.pagination?.limit ?? requestedLimit,
|
|
36
|
+
offset: raw.pagination?.offset ?? requestedOffset,
|
|
37
|
+
hasMore: raw.pagination?.hasMore ?? false,
|
|
38
|
+
};
|
|
28
39
|
}
|
|
29
40
|
catch (error) {
|
|
30
41
|
throw this.handleError(error);
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.OxyServicesUserMixin = OxyServicesUserMixin;
|
|
4
4
|
const apiUtils_1 = require("../utils/apiUtils");
|
|
5
|
+
const keyManager_1 = require("../crypto/keyManager");
|
|
6
|
+
const signatureService_1 = require("../crypto/signatureService");
|
|
5
7
|
function OxyServicesUserMixin(Base) {
|
|
6
8
|
return class extends Base {
|
|
7
9
|
constructor(...args) {
|
|
@@ -44,37 +46,22 @@ function OxyServicesUserMixin(Base) {
|
|
|
44
46
|
cache: true,
|
|
45
47
|
cacheTTL: 2 * 60 * 1000, // 2 minutes cache
|
|
46
48
|
});
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (isSearchProfilesResponse(response)) {
|
|
52
|
-
const typedResponse = response;
|
|
53
|
-
const paginationInfo = typedResponse.pagination ?? {
|
|
54
|
-
total: typedResponse.data.length,
|
|
55
|
-
limit: pagination?.limit ?? typedResponse.data.length,
|
|
56
|
-
offset: pagination?.offset ?? 0,
|
|
57
|
-
hasMore: typedResponse.data.length === (pagination?.limit ?? typedResponse.data.length) &&
|
|
58
|
-
(pagination?.limit ?? typedResponse.data.length) > 0,
|
|
59
|
-
};
|
|
60
|
-
return {
|
|
61
|
-
data: typedResponse.data,
|
|
62
|
-
pagination: paginationInfo,
|
|
63
|
-
};
|
|
49
|
+
if (typeof response !== 'object' ||
|
|
50
|
+
response === null ||
|
|
51
|
+
!Array.isArray(response.data)) {
|
|
52
|
+
throw new Error('Unexpected search response format');
|
|
64
53
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
limit
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
// If response is unexpected, throw an error
|
|
77
|
-
throw new Error('Unexpected search response format');
|
|
54
|
+
const paginationInfo = response.pagination ?? {
|
|
55
|
+
total: response.data.length,
|
|
56
|
+
limit: pagination?.limit ?? response.data.length,
|
|
57
|
+
offset: pagination?.offset ?? 0,
|
|
58
|
+
hasMore: response.data.length === (pagination?.limit ?? response.data.length) &&
|
|
59
|
+
(pagination?.limit ?? response.data.length) > 0,
|
|
60
|
+
};
|
|
61
|
+
return {
|
|
62
|
+
data: response.data,
|
|
63
|
+
pagination: paginationInfo,
|
|
64
|
+
};
|
|
78
65
|
}
|
|
79
66
|
catch (error) {
|
|
80
67
|
throw this.handleError(error);
|
|
@@ -156,12 +143,31 @@ function OxyServicesUserMixin(Base) {
|
|
|
156
143
|
}, 'getCurrentUser');
|
|
157
144
|
}
|
|
158
145
|
/**
|
|
159
|
-
* Update user profile
|
|
160
|
-
*
|
|
146
|
+
* Update user profile.
|
|
147
|
+
*
|
|
148
|
+
* Invalidates the SDK-side response cache for every endpoint that
|
|
149
|
+
* returns the current user (`GET /users/me`, `GET /session/user/*`,
|
|
150
|
+
* `GET /users/<id>`, `GET /profiles/username/*`) so the next read
|
|
151
|
+
* doesn't return a stale snapshot. Without this, a follow-up
|
|
152
|
+
* `getUserBySession` call inside the 2-minute cache window can return
|
|
153
|
+
* the pre-update user — most visibly during onboarding, where it
|
|
154
|
+
* causes the username step to flicker back as if nothing was saved.
|
|
155
|
+
*
|
|
156
|
+
* TanStack Query handles offline queuing automatically.
|
|
161
157
|
*/
|
|
162
158
|
async updateProfile(updates) {
|
|
163
159
|
try {
|
|
164
|
-
|
|
160
|
+
const result = await this.makeRequest('PUT', '/users/me', updates, { cache: false });
|
|
161
|
+
// Bust every cached representation of the current user. We use a
|
|
162
|
+
// prefix sweep rather than an enumeration because the SDK never
|
|
163
|
+
// tracks the set of active session IDs centrally.
|
|
164
|
+
this.clearCacheByPrefix('GET:/session/user/');
|
|
165
|
+
this.clearCacheByPrefix('GET:/users/me');
|
|
166
|
+
this.clearCacheByPrefix('GET:/profiles/username/');
|
|
167
|
+
if (result?.id) {
|
|
168
|
+
this.clearCacheEntry(`GET:/users/${result.id}`);
|
|
169
|
+
}
|
|
170
|
+
return result;
|
|
165
171
|
}
|
|
166
172
|
catch (error) {
|
|
167
173
|
const errorAny = error;
|
|
@@ -245,14 +251,29 @@ function OxyServicesUserMixin(Base) {
|
|
|
245
251
|
}
|
|
246
252
|
}
|
|
247
253
|
/**
|
|
248
|
-
* Delete account permanently
|
|
249
|
-
*
|
|
250
|
-
*
|
|
254
|
+
* Delete account permanently.
|
|
255
|
+
*
|
|
256
|
+
* Signs `delete:{publicKey}:{timestamp}` with the locally-stored identity
|
|
257
|
+
* private key and submits the signature alongside the confirmation text
|
|
258
|
+
* (must equal the user's username). The signature is the cryptographic
|
|
259
|
+
* proof of ownership — only the device holding the private key can issue
|
|
260
|
+
* a valid signature, so no password is required.
|
|
261
|
+
*
|
|
262
|
+
* @param confirmText - Must equal the user's username (verified server-side)
|
|
263
|
+
* @throws If no identity is stored on this device, or signing fails
|
|
251
264
|
*/
|
|
252
|
-
async deleteAccount(
|
|
265
|
+
async deleteAccount(confirmText) {
|
|
253
266
|
try {
|
|
267
|
+
const publicKey = await keyManager_1.KeyManager.getPublicKey();
|
|
268
|
+
if (!publicKey) {
|
|
269
|
+
throw new Error('No identity found on this device. Account deletion requires the device that holds your identity key.');
|
|
270
|
+
}
|
|
271
|
+
const timestamp = Date.now();
|
|
272
|
+
const message = `delete:${publicKey}:${timestamp}`;
|
|
273
|
+
const signature = await signatureService_1.SignatureService.sign(message);
|
|
254
274
|
return await this.makeRequest('DELETE', '/users/me', {
|
|
255
|
-
|
|
275
|
+
signature,
|
|
276
|
+
timestamp,
|
|
256
277
|
confirmText,
|
|
257
278
|
}, { cache: false });
|
|
258
279
|
}
|
|
@@ -1,37 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
3
|
exports.OxyServicesUtilityMixin = OxyServicesUtilityMixin;
|
|
37
4
|
/**
|
|
@@ -41,6 +8,7 @@ exports.OxyServicesUtilityMixin = OxyServicesUtilityMixin;
|
|
|
41
8
|
* and Express.js authentication middleware
|
|
42
9
|
*/
|
|
43
10
|
const jwt_decode_1 = require("jwt-decode");
|
|
11
|
+
const platformCrypto_1 = require("../utils/platformCrypto");
|
|
44
12
|
const mixinHelpers_1 = require("./mixinHelpers");
|
|
45
13
|
function OxyServicesUtilityMixin(Base) {
|
|
46
14
|
return class extends Base {
|
|
@@ -254,9 +222,15 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
254
222
|
return onError(error);
|
|
255
223
|
return res.status(403).json(error);
|
|
256
224
|
}
|
|
257
|
-
// Verify JWT signature (not just decode)
|
|
225
|
+
// Verify JWT signature (not just decode).
|
|
226
|
+
// This middleware only runs on a Node Express server, but the file
|
|
227
|
+
// is bundled by Metro/Vite for RN/web consumers. `loadNodeCrypto`
|
|
228
|
+
// is per-platform: the RN variant throws (and is never called
|
|
229
|
+
// because service-token middleware is only mounted by Node hosts),
|
|
230
|
+
// so Metro never bundles a reference to Node's built-in.
|
|
258
231
|
try {
|
|
259
|
-
const
|
|
232
|
+
const nodeCrypto = await (0, platformCrypto_1.loadNodeCrypto)();
|
|
233
|
+
const { createHmac, timingSafeEqual } = nodeCrypto;
|
|
260
234
|
const [headerB64, payloadB64, signatureB64] = token.split('.');
|
|
261
235
|
if (!headerB64 || !payloadB64 || !signatureB64) {
|
|
262
236
|
throw new Error('Invalid token structure');
|
|
@@ -270,7 +244,6 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
270
244
|
// Timing-safe comparison
|
|
271
245
|
const sigBuf = Buffer.from(signatureB64);
|
|
272
246
|
const expectedBuf = Buffer.from(expectedSig);
|
|
273
|
-
const { timingSafeEqual } = await Promise.resolve().then(() => __importStar(require('crypto')));
|
|
274
247
|
if (sigBuf.length !== expectedBuf.length || !timingSafeEqual(sigBuf, expectedBuf)) {
|
|
275
248
|
throw new Error('Invalid signature');
|
|
276
249
|
}
|
|
@@ -295,8 +268,8 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
295
268
|
return onError(error);
|
|
296
269
|
return res.status(401).json(error);
|
|
297
270
|
}
|
|
298
|
-
// Check expiration
|
|
299
|
-
if (decoded.exp && decoded.exp
|
|
271
|
+
// Check expiration — reject tokens at exact expiry second (use <=)
|
|
272
|
+
if (decoded.exp && decoded.exp <= Math.floor(Date.now() / 1000)) {
|
|
300
273
|
if (optional) {
|
|
301
274
|
req.userId = null;
|
|
302
275
|
req.user = null;
|
|
@@ -351,7 +324,8 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
351
324
|
return res.status(401).json(error);
|
|
352
325
|
}
|
|
353
326
|
// Check token expiration locally first (fast path)
|
|
354
|
-
|
|
327
|
+
// Reject tokens at exact expiry second (use <=)
|
|
328
|
+
if (decoded.exp && decoded.exp <= Math.floor(Date.now() / 1000)) {
|
|
355
329
|
if (optional) {
|
|
356
330
|
req.userId = null;
|
|
357
331
|
req.user = null;
|
|
@@ -518,8 +492,8 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
518
492
|
if (!userId) {
|
|
519
493
|
return next(new Error('Invalid token payload'));
|
|
520
494
|
}
|
|
521
|
-
// Check expiration
|
|
522
|
-
if (decoded.exp && decoded.exp
|
|
495
|
+
// Check expiration — reject tokens at exact expiry second (use <=)
|
|
496
|
+
if (decoded.exp && decoded.exp <= Math.floor(Date.now() / 1000)) {
|
|
523
497
|
return next(new Error('Token expired'));
|
|
524
498
|
}
|
|
525
499
|
// Validate session if available
|
|
@@ -536,12 +510,14 @@ function OxyServicesUtilityMixin(Base) {
|
|
|
536
510
|
return next(new Error('Session validation failed'));
|
|
537
511
|
}
|
|
538
512
|
}
|
|
539
|
-
// Attach user data to socket
|
|
513
|
+
// Attach user data to socket. We expose BOTH `socket.data.userId`
|
|
514
|
+
// (the official Socket.IO data slot) and `socket.user` because
|
|
515
|
+
// every consumer in this ecosystem (Mention, Allo, api/server.ts)
|
|
516
|
+
// reads from `socket.user.id`.
|
|
540
517
|
socket.data = socket.data || {};
|
|
541
518
|
socket.data.userId = userId;
|
|
542
519
|
socket.data.sessionId = decoded.sessionId || null;
|
|
543
520
|
socket.data.token = token;
|
|
544
|
-
// Also set on socket.user for backward compatibility
|
|
545
521
|
socket.user = { id: userId, userId, sessionId: decoded.sessionId };
|
|
546
522
|
if (debug) {
|
|
547
523
|
console.log(`[oxy.authSocket] OK user=${userId}`);
|
package/dist/cjs/mixins/index.js
CHANGED
|
@@ -28,6 +28,7 @@ const OxyServices_utility_1 = require("./OxyServices.utility");
|
|
|
28
28
|
const OxyServices_features_1 = require("./OxyServices.features");
|
|
29
29
|
const OxyServices_topics_1 = require("./OxyServices.topics");
|
|
30
30
|
const OxyServices_managedAccounts_1 = require("./OxyServices.managedAccounts");
|
|
31
|
+
const OxyServices_contacts_1 = require("./OxyServices.contacts");
|
|
31
32
|
/**
|
|
32
33
|
* Mixin pipeline - applied in order from first to last.
|
|
33
34
|
*
|
|
@@ -66,6 +67,7 @@ const MIXIN_PIPELINE = [
|
|
|
66
67
|
OxyServices_features_1.OxyServicesFeaturesMixin,
|
|
67
68
|
OxyServices_topics_1.OxyServicesTopicsMixin,
|
|
68
69
|
OxyServices_managedAccounts_1.OxyServicesManagedAccountsMixin,
|
|
70
|
+
OxyServices_contacts_1.OxyServicesContactsMixin,
|
|
69
71
|
// Utility (last, can use all above)
|
|
70
72
|
OxyServices_utility_1.OxyServicesUtilityMixin,
|
|
71
73
|
];
|
|
@@ -74,10 +76,16 @@ exports.MIXIN_PIPELINE = MIXIN_PIPELINE;
|
|
|
74
76
|
* Composes all OxyServices mixins using a pipeline pattern.
|
|
75
77
|
*
|
|
76
78
|
* This is equivalent to the nested calls but more readable and maintainable.
|
|
77
|
-
* Adding a new mixin:
|
|
79
|
+
* Adding a new mixin: add it to MIXIN_PIPELINE at the appropriate position
|
|
80
|
+
* AND extend `AllMixinInstances` so its methods are visible to consumers.
|
|
78
81
|
*
|
|
79
|
-
*
|
|
82
|
+
* The cast through `unknown` carries the runtime augmentation chain into the
|
|
83
|
+
* static type system. `Array.reduce` cannot track each mixin's generic
|
|
84
|
+
* refinement, so we assert the final shape exposed by all mixins together.
|
|
85
|
+
*
|
|
86
|
+
* @returns The fully composed OxyServices constructor with all mixins applied
|
|
80
87
|
*/
|
|
81
88
|
function composeOxyServices() {
|
|
82
|
-
|
|
89
|
+
const composed = MIXIN_PIPELINE.reduce((Base, mixin) => mixin(Base), OxyServices_base_1.OxyServicesBase);
|
|
90
|
+
return composed;
|
|
83
91
|
}
|