@oxyhq/core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (277) hide show
  1. package/README.md +50 -0
  2. package/dist/cjs/AuthManager.js +361 -0
  3. package/dist/cjs/CrossDomainAuth.js +258 -0
  4. package/dist/cjs/HttpService.js +618 -0
  5. package/dist/cjs/OxyServices.base.js +263 -0
  6. package/dist/cjs/OxyServices.errors.js +22 -0
  7. package/dist/cjs/OxyServices.js +63 -0
  8. package/dist/cjs/constants/version.js +16 -0
  9. package/dist/cjs/crypto/index.js +20 -0
  10. package/dist/cjs/crypto/keyManager.js +887 -0
  11. package/dist/cjs/crypto/polyfill.js +64 -0
  12. package/dist/cjs/crypto/recoveryPhrase.js +169 -0
  13. package/dist/cjs/crypto/signatureService.js +296 -0
  14. package/dist/cjs/i18n/index.js +73 -0
  15. package/dist/cjs/i18n/locales/ar-SA.json +120 -0
  16. package/dist/cjs/i18n/locales/ca-ES.json +120 -0
  17. package/dist/cjs/i18n/locales/de-DE.json +120 -0
  18. package/dist/cjs/i18n/locales/en-US.json +956 -0
  19. package/dist/cjs/i18n/locales/es-ES.json +944 -0
  20. package/dist/cjs/i18n/locales/fr-FR.json +120 -0
  21. package/dist/cjs/i18n/locales/it-IT.json +120 -0
  22. package/dist/cjs/i18n/locales/ja-JP.json +119 -0
  23. package/dist/cjs/i18n/locales/ko-KR.json +120 -0
  24. package/dist/cjs/i18n/locales/locales/ar-SA.json +120 -0
  25. package/dist/cjs/i18n/locales/locales/ca-ES.json +120 -0
  26. package/dist/cjs/i18n/locales/locales/de-DE.json +120 -0
  27. package/dist/cjs/i18n/locales/locales/en-US.json +956 -0
  28. package/dist/cjs/i18n/locales/locales/es-ES.json +944 -0
  29. package/dist/cjs/i18n/locales/locales/fr-FR.json +120 -0
  30. package/dist/cjs/i18n/locales/locales/it-IT.json +120 -0
  31. package/dist/cjs/i18n/locales/locales/ja-JP.json +119 -0
  32. package/dist/cjs/i18n/locales/locales/ko-KR.json +120 -0
  33. package/dist/cjs/i18n/locales/locales/pt-PT.json +120 -0
  34. package/dist/cjs/i18n/locales/locales/zh-CN.json +120 -0
  35. package/dist/cjs/i18n/locales/pt-PT.json +120 -0
  36. package/dist/cjs/i18n/locales/zh-CN.json +120 -0
  37. package/dist/cjs/index.js +153 -0
  38. package/dist/cjs/mixins/OxyServices.analytics.js +49 -0
  39. package/dist/cjs/mixins/OxyServices.assets.js +380 -0
  40. package/dist/cjs/mixins/OxyServices.auth.js +259 -0
  41. package/dist/cjs/mixins/OxyServices.developer.js +97 -0
  42. package/dist/cjs/mixins/OxyServices.devices.js +116 -0
  43. package/dist/cjs/mixins/OxyServices.features.js +309 -0
  44. package/dist/cjs/mixins/OxyServices.fedcm.js +435 -0
  45. package/dist/cjs/mixins/OxyServices.karma.js +108 -0
  46. package/dist/cjs/mixins/OxyServices.language.js +154 -0
  47. package/dist/cjs/mixins/OxyServices.location.js +43 -0
  48. package/dist/cjs/mixins/OxyServices.payment.js +158 -0
  49. package/dist/cjs/mixins/OxyServices.popup.js +371 -0
  50. package/dist/cjs/mixins/OxyServices.privacy.js +162 -0
  51. package/dist/cjs/mixins/OxyServices.redirect.js +345 -0
  52. package/dist/cjs/mixins/OxyServices.security.js +81 -0
  53. package/dist/cjs/mixins/OxyServices.user.js +355 -0
  54. package/dist/cjs/mixins/OxyServices.utility.js +156 -0
  55. package/dist/cjs/mixins/index.js +79 -0
  56. package/dist/cjs/mixins/mixinHelpers.js +53 -0
  57. package/dist/cjs/models/interfaces.js +20 -0
  58. package/dist/cjs/models/session.js +2 -0
  59. package/dist/cjs/shared/index.js +70 -0
  60. package/dist/cjs/shared/utils/colorUtils.js +153 -0
  61. package/dist/cjs/shared/utils/debugUtils.js +73 -0
  62. package/dist/cjs/shared/utils/errorUtils.js +183 -0
  63. package/dist/cjs/shared/utils/index.js +49 -0
  64. package/dist/cjs/shared/utils/networkUtils.js +183 -0
  65. package/dist/cjs/shared/utils/themeUtils.js +106 -0
  66. package/dist/cjs/utils/apiUtils.js +61 -0
  67. package/dist/cjs/utils/asyncUtils.js +194 -0
  68. package/dist/cjs/utils/cache.js +226 -0
  69. package/dist/cjs/utils/deviceManager.js +205 -0
  70. package/dist/cjs/utils/errorUtils.js +154 -0
  71. package/dist/cjs/utils/index.js +26 -0
  72. package/dist/cjs/utils/languageUtils.js +165 -0
  73. package/dist/cjs/utils/loggerUtils.js +126 -0
  74. package/dist/cjs/utils/platform.js +144 -0
  75. package/dist/cjs/utils/requestUtils.js +209 -0
  76. package/dist/cjs/utils/sessionUtils.js +181 -0
  77. package/dist/cjs/utils/validationUtils.js +173 -0
  78. package/dist/esm/AuthManager.js +356 -0
  79. package/dist/esm/CrossDomainAuth.js +253 -0
  80. package/dist/esm/HttpService.js +614 -0
  81. package/dist/esm/OxyServices.base.js +259 -0
  82. package/dist/esm/OxyServices.errors.js +17 -0
  83. package/dist/esm/OxyServices.js +59 -0
  84. package/dist/esm/constants/version.js +13 -0
  85. package/dist/esm/crypto/index.js +13 -0
  86. package/dist/esm/crypto/keyManager.js +850 -0
  87. package/dist/esm/crypto/polyfill.js +61 -0
  88. package/dist/esm/crypto/recoveryPhrase.js +132 -0
  89. package/dist/esm/crypto/signatureService.js +259 -0
  90. package/dist/esm/i18n/index.js +69 -0
  91. package/dist/esm/i18n/locales/ar-SA.json +120 -0
  92. package/dist/esm/i18n/locales/ca-ES.json +120 -0
  93. package/dist/esm/i18n/locales/de-DE.json +120 -0
  94. package/dist/esm/i18n/locales/en-US.json +956 -0
  95. package/dist/esm/i18n/locales/es-ES.json +944 -0
  96. package/dist/esm/i18n/locales/fr-FR.json +120 -0
  97. package/dist/esm/i18n/locales/it-IT.json +120 -0
  98. package/dist/esm/i18n/locales/ja-JP.json +119 -0
  99. package/dist/esm/i18n/locales/ko-KR.json +120 -0
  100. package/dist/esm/i18n/locales/locales/ar-SA.json +120 -0
  101. package/dist/esm/i18n/locales/locales/ca-ES.json +120 -0
  102. package/dist/esm/i18n/locales/locales/de-DE.json +120 -0
  103. package/dist/esm/i18n/locales/locales/en-US.json +956 -0
  104. package/dist/esm/i18n/locales/locales/es-ES.json +944 -0
  105. package/dist/esm/i18n/locales/locales/fr-FR.json +120 -0
  106. package/dist/esm/i18n/locales/locales/it-IT.json +120 -0
  107. package/dist/esm/i18n/locales/locales/ja-JP.json +119 -0
  108. package/dist/esm/i18n/locales/locales/ko-KR.json +120 -0
  109. package/dist/esm/i18n/locales/locales/pt-PT.json +120 -0
  110. package/dist/esm/i18n/locales/locales/zh-CN.json +120 -0
  111. package/dist/esm/i18n/locales/pt-PT.json +120 -0
  112. package/dist/esm/i18n/locales/zh-CN.json +120 -0
  113. package/dist/esm/index.js +55 -0
  114. package/dist/esm/mixins/OxyServices.analytics.js +46 -0
  115. package/dist/esm/mixins/OxyServices.assets.js +377 -0
  116. package/dist/esm/mixins/OxyServices.auth.js +256 -0
  117. package/dist/esm/mixins/OxyServices.developer.js +94 -0
  118. package/dist/esm/mixins/OxyServices.devices.js +113 -0
  119. package/dist/esm/mixins/OxyServices.features.js +306 -0
  120. package/dist/esm/mixins/OxyServices.fedcm.js +433 -0
  121. package/dist/esm/mixins/OxyServices.karma.js +105 -0
  122. package/dist/esm/mixins/OxyServices.language.js +118 -0
  123. package/dist/esm/mixins/OxyServices.location.js +40 -0
  124. package/dist/esm/mixins/OxyServices.payment.js +155 -0
  125. package/dist/esm/mixins/OxyServices.popup.js +369 -0
  126. package/dist/esm/mixins/OxyServices.privacy.js +159 -0
  127. package/dist/esm/mixins/OxyServices.redirect.js +343 -0
  128. package/dist/esm/mixins/OxyServices.security.js +78 -0
  129. package/dist/esm/mixins/OxyServices.user.js +352 -0
  130. package/dist/esm/mixins/OxyServices.utility.js +153 -0
  131. package/dist/esm/mixins/index.js +76 -0
  132. package/dist/esm/mixins/mixinHelpers.js +48 -0
  133. package/dist/esm/models/interfaces.js +17 -0
  134. package/dist/esm/models/session.js +1 -0
  135. package/dist/esm/shared/index.js +31 -0
  136. package/dist/esm/shared/utils/colorUtils.js +143 -0
  137. package/dist/esm/shared/utils/debugUtils.js +65 -0
  138. package/dist/esm/shared/utils/errorUtils.js +170 -0
  139. package/dist/esm/shared/utils/index.js +15 -0
  140. package/dist/esm/shared/utils/networkUtils.js +173 -0
  141. package/dist/esm/shared/utils/themeUtils.js +98 -0
  142. package/dist/esm/utils/apiUtils.js +55 -0
  143. package/dist/esm/utils/asyncUtils.js +179 -0
  144. package/dist/esm/utils/cache.js +218 -0
  145. package/dist/esm/utils/deviceManager.js +168 -0
  146. package/dist/esm/utils/errorUtils.js +146 -0
  147. package/dist/esm/utils/index.js +7 -0
  148. package/dist/esm/utils/languageUtils.js +158 -0
  149. package/dist/esm/utils/loggerUtils.js +115 -0
  150. package/dist/esm/utils/platform.js +102 -0
  151. package/dist/esm/utils/requestUtils.js +203 -0
  152. package/dist/esm/utils/sessionUtils.js +171 -0
  153. package/dist/esm/utils/validationUtils.js +153 -0
  154. package/dist/types/AuthManager.d.ts +143 -0
  155. package/dist/types/CrossDomainAuth.d.ts +160 -0
  156. package/dist/types/HttpService.d.ts +163 -0
  157. package/dist/types/OxyServices.base.d.ts +126 -0
  158. package/dist/types/OxyServices.d.ts +81 -0
  159. package/dist/types/OxyServices.errors.d.ts +11 -0
  160. package/dist/types/constants/version.d.ts +13 -0
  161. package/dist/types/crypto/index.d.ts +11 -0
  162. package/dist/types/crypto/keyManager.d.ts +189 -0
  163. package/dist/types/crypto/polyfill.d.ts +11 -0
  164. package/dist/types/crypto/recoveryPhrase.d.ts +58 -0
  165. package/dist/types/crypto/signatureService.d.ts +86 -0
  166. package/dist/types/i18n/index.d.ts +3 -0
  167. package/dist/types/index.d.ts +50 -0
  168. package/dist/types/mixins/OxyServices.analytics.d.ts +66 -0
  169. package/dist/types/mixins/OxyServices.assets.d.ts +135 -0
  170. package/dist/types/mixins/OxyServices.auth.d.ts +186 -0
  171. package/dist/types/mixins/OxyServices.developer.d.ts +99 -0
  172. package/dist/types/mixins/OxyServices.devices.d.ts +96 -0
  173. package/dist/types/mixins/OxyServices.features.d.ts +228 -0
  174. package/dist/types/mixins/OxyServices.fedcm.d.ts +200 -0
  175. package/dist/types/mixins/OxyServices.karma.d.ts +85 -0
  176. package/dist/types/mixins/OxyServices.language.d.ts +81 -0
  177. package/dist/types/mixins/OxyServices.location.d.ts +64 -0
  178. package/dist/types/mixins/OxyServices.payment.d.ts +111 -0
  179. package/dist/types/mixins/OxyServices.popup.d.ts +205 -0
  180. package/dist/types/mixins/OxyServices.privacy.d.ts +122 -0
  181. package/dist/types/mixins/OxyServices.redirect.d.ts +245 -0
  182. package/dist/types/mixins/OxyServices.security.d.ts +78 -0
  183. package/dist/types/mixins/OxyServices.user.d.ts +182 -0
  184. package/dist/types/mixins/OxyServices.utility.d.ts +93 -0
  185. package/dist/types/mixins/index.d.ts +30 -0
  186. package/dist/types/mixins/mixinHelpers.d.ts +31 -0
  187. package/dist/types/models/interfaces.d.ts +415 -0
  188. package/dist/types/models/session.d.ts +27 -0
  189. package/dist/types/shared/index.d.ts +28 -0
  190. package/dist/types/shared/utils/colorUtils.d.ts +104 -0
  191. package/dist/types/shared/utils/debugUtils.d.ts +48 -0
  192. package/dist/types/shared/utils/errorUtils.d.ts +97 -0
  193. package/dist/types/shared/utils/index.d.ts +13 -0
  194. package/dist/types/shared/utils/networkUtils.d.ts +139 -0
  195. package/dist/types/shared/utils/themeUtils.d.ts +90 -0
  196. package/dist/types/utils/apiUtils.d.ts +53 -0
  197. package/dist/types/utils/asyncUtils.d.ts +58 -0
  198. package/dist/types/utils/cache.d.ts +127 -0
  199. package/dist/types/utils/deviceManager.d.ts +65 -0
  200. package/dist/types/utils/errorUtils.d.ts +46 -0
  201. package/dist/types/utils/index.d.ts +6 -0
  202. package/dist/types/utils/languageUtils.d.ts +37 -0
  203. package/dist/types/utils/loggerUtils.d.ts +48 -0
  204. package/dist/types/utils/platform.d.ts +40 -0
  205. package/dist/types/utils/requestUtils.d.ts +123 -0
  206. package/dist/types/utils/sessionUtils.d.ts +54 -0
  207. package/dist/types/utils/validationUtils.d.ts +85 -0
  208. package/package.json +84 -0
  209. package/src/AuthManager.ts +436 -0
  210. package/src/CrossDomainAuth.ts +307 -0
  211. package/src/HttpService.ts +752 -0
  212. package/src/OxyServices.base.ts +334 -0
  213. package/src/OxyServices.errors.ts +26 -0
  214. package/src/OxyServices.ts +129 -0
  215. package/src/constants/version.ts +15 -0
  216. package/src/crypto/index.ts +25 -0
  217. package/src/crypto/keyManager.ts +962 -0
  218. package/src/crypto/polyfill.ts +70 -0
  219. package/src/crypto/recoveryPhrase.ts +166 -0
  220. package/src/crypto/signatureService.ts +323 -0
  221. package/src/i18n/index.ts +75 -0
  222. package/src/i18n/locales/ar-SA.json +120 -0
  223. package/src/i18n/locales/ca-ES.json +120 -0
  224. package/src/i18n/locales/de-DE.json +120 -0
  225. package/src/i18n/locales/en-US.json +956 -0
  226. package/src/i18n/locales/es-ES.json +944 -0
  227. package/src/i18n/locales/fr-FR.json +120 -0
  228. package/src/i18n/locales/it-IT.json +120 -0
  229. package/src/i18n/locales/ja-JP.json +119 -0
  230. package/src/i18n/locales/ko-KR.json +120 -0
  231. package/src/i18n/locales/pt-PT.json +120 -0
  232. package/src/i18n/locales/zh-CN.json +120 -0
  233. package/src/index.ts +153 -0
  234. package/src/mixins/OxyServices.analytics.ts +53 -0
  235. package/src/mixins/OxyServices.assets.ts +412 -0
  236. package/src/mixins/OxyServices.auth.ts +358 -0
  237. package/src/mixins/OxyServices.developer.ts +114 -0
  238. package/src/mixins/OxyServices.devices.ts +119 -0
  239. package/src/mixins/OxyServices.features.ts +428 -0
  240. package/src/mixins/OxyServices.fedcm.ts +494 -0
  241. package/src/mixins/OxyServices.karma.ts +111 -0
  242. package/src/mixins/OxyServices.language.ts +127 -0
  243. package/src/mixins/OxyServices.location.ts +46 -0
  244. package/src/mixins/OxyServices.payment.ts +163 -0
  245. package/src/mixins/OxyServices.popup.ts +443 -0
  246. package/src/mixins/OxyServices.privacy.ts +182 -0
  247. package/src/mixins/OxyServices.redirect.ts +397 -0
  248. package/src/mixins/OxyServices.security.ts +103 -0
  249. package/src/mixins/OxyServices.user.ts +392 -0
  250. package/src/mixins/OxyServices.utility.ts +191 -0
  251. package/src/mixins/index.ts +91 -0
  252. package/src/mixins/mixinHelpers.ts +69 -0
  253. package/src/models/interfaces.ts +511 -0
  254. package/src/models/session.ts +30 -0
  255. package/src/shared/index.ts +82 -0
  256. package/src/shared/utils/colorUtils.ts +155 -0
  257. package/src/shared/utils/debugUtils.ts +73 -0
  258. package/src/shared/utils/errorUtils.ts +181 -0
  259. package/src/shared/utils/index.ts +59 -0
  260. package/src/shared/utils/networkUtils.ts +248 -0
  261. package/src/shared/utils/themeUtils.ts +115 -0
  262. package/src/types/bip39.d.ts +32 -0
  263. package/src/types/buffer.d.ts +97 -0
  264. package/src/types/color.d.ts +20 -0
  265. package/src/types/elliptic.d.ts +62 -0
  266. package/src/utils/apiUtils.ts +88 -0
  267. package/src/utils/asyncUtils.ts +252 -0
  268. package/src/utils/cache.ts +264 -0
  269. package/src/utils/deviceManager.ts +198 -0
  270. package/src/utils/errorUtils.ts +216 -0
  271. package/src/utils/index.ts +21 -0
  272. package/src/utils/languageUtils.ts +174 -0
  273. package/src/utils/loggerUtils.ts +153 -0
  274. package/src/utils/platform.ts +117 -0
  275. package/src/utils/requestUtils.ts +237 -0
  276. package/src/utils/sessionUtils.ts +206 -0
  277. package/src/utils/validationUtils.ts +174 -0
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Crypto Polyfills
3
+ *
4
+ * Ensures Buffer and crypto.getRandomValues are available
5
+ * across all platforms (Node.js, Browser, React Native).
6
+ *
7
+ * - Browser/Node.js: Uses native crypto
8
+ * - React Native: Falls back to expo-crypto if native crypto unavailable
9
+ */
10
+ import { Buffer } from 'buffer';
11
+ const getGlobalObject = () => {
12
+ if (typeof globalThis !== 'undefined')
13
+ return globalThis;
14
+ if (typeof global !== 'undefined')
15
+ return global;
16
+ if (typeof window !== 'undefined')
17
+ return window;
18
+ if (typeof self !== 'undefined')
19
+ return self;
20
+ return {};
21
+ };
22
+ const globalObject = getGlobalObject();
23
+ // Make Buffer available globally for libraries that depend on it
24
+ if (!globalObject.Buffer) {
25
+ globalObject.Buffer = Buffer;
26
+ }
27
+ // Cache for expo-crypto module (lazy loaded only in React Native)
28
+ let expoCryptoModule = null;
29
+ let expoCryptoLoadAttempted = false;
30
+ function getRandomBytesSync(byteCount) {
31
+ if (!expoCryptoLoadAttempted) {
32
+ expoCryptoLoadAttempted = true;
33
+ try {
34
+ expoCryptoModule = require('expo-crypto');
35
+ }
36
+ catch {
37
+ // expo-crypto not available — expected in non-RN environments
38
+ }
39
+ }
40
+ if (expoCryptoModule) {
41
+ return expoCryptoModule.getRandomBytes(byteCount);
42
+ }
43
+ throw new Error('No crypto.getRandomValues implementation available. ' +
44
+ 'In React Native, install expo-crypto.');
45
+ }
46
+ const cryptoPolyfill = {
47
+ getRandomValues(array) {
48
+ const bytes = getRandomBytesSync(array.byteLength);
49
+ const uint8View = new Uint8Array(array.buffer, array.byteOffset, array.byteLength);
50
+ uint8View.set(bytes);
51
+ return array;
52
+ },
53
+ };
54
+ // Only polyfill if crypto or crypto.getRandomValues is not available
55
+ if (typeof globalObject.crypto === 'undefined') {
56
+ globalObject.crypto = cryptoPolyfill;
57
+ }
58
+ else if (typeof globalObject.crypto.getRandomValues !== 'function') {
59
+ globalObject.crypto.getRandomValues = cryptoPolyfill.getRandomValues;
60
+ }
61
+ export { Buffer };
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Recovery Phrase Service - BIP39 Mnemonic Generation
3
+ *
4
+ * Handles generation and restoration of recovery phrases (mnemonic seeds)
5
+ * for backing up and restoring user identities.
6
+ *
7
+ * Note: This module requires the polyfill to be loaded first (done via crypto/index.ts)
8
+ */
9
+ import * as bip39 from 'bip39';
10
+ import { KeyManager } from './keyManager';
11
+ /**
12
+ * Convert Uint8Array or array-like to hexadecimal string
13
+ * Works in both Node.js and React Native without depending on Buffer
14
+ */
15
+ function toHex(data) {
16
+ // Convert to array of numbers if needed
17
+ const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);
18
+ return Array.from(bytes)
19
+ .map(b => b.toString(16).padStart(2, '0'))
20
+ .join('');
21
+ }
22
+ export class RecoveryPhraseService {
23
+ /**
24
+ * Generate a new identity with a recovery phrase
25
+ * Returns the mnemonic phrase (should only be shown once to the user)
26
+ */
27
+ static async generateIdentityWithRecovery() {
28
+ // Generate 128-bit entropy for 12-word mnemonic
29
+ const mnemonic = bip39.generateMnemonic(128);
30
+ // Derive private key from mnemonic
31
+ // Using the seed directly as the private key (simplified approach)
32
+ const seed = await bip39.mnemonicToSeed(mnemonic);
33
+ // Use first 32 bytes of seed as private key
34
+ const seedSlice = seed.subarray ? seed.subarray(0, 32) : seed.slice(0, 32);
35
+ const privateKeyHex = toHex(seedSlice);
36
+ // Import the derived key pair
37
+ const publicKey = await KeyManager.importKeyPair(privateKeyHex);
38
+ return {
39
+ phrase: mnemonic,
40
+ words: mnemonic.split(' '),
41
+ publicKey,
42
+ };
43
+ }
44
+ /**
45
+ * Generate a 24-word recovery phrase for higher security
46
+ */
47
+ static async generateIdentityWithRecovery24() {
48
+ // Generate 256-bit entropy for 24-word mnemonic
49
+ const mnemonic = bip39.generateMnemonic(256);
50
+ const seed = await bip39.mnemonicToSeed(mnemonic);
51
+ const seedSlice = seed.subarray ? seed.subarray(0, 32) : seed.slice(0, 32);
52
+ const privateKeyHex = toHex(seedSlice);
53
+ const publicKey = await KeyManager.importKeyPair(privateKeyHex);
54
+ return {
55
+ phrase: mnemonic,
56
+ words: mnemonic.split(' '),
57
+ publicKey,
58
+ };
59
+ }
60
+ /**
61
+ * Restore an identity from a recovery phrase
62
+ */
63
+ static async restoreFromPhrase(phrase) {
64
+ // Normalize and validate the phrase
65
+ const normalizedPhrase = phrase.trim().toLowerCase();
66
+ if (!bip39.validateMnemonic(normalizedPhrase)) {
67
+ throw new Error('Invalid recovery phrase. Please check the words and try again.');
68
+ }
69
+ // Derive the same private key from the mnemonic
70
+ const seed = await bip39.mnemonicToSeed(normalizedPhrase);
71
+ const seedSlice = seed.subarray ? seed.subarray(0, 32) : seed.slice(0, 32);
72
+ const privateKeyHex = toHex(seedSlice);
73
+ // Import and store the key pair
74
+ const publicKey = await KeyManager.importKeyPair(privateKeyHex);
75
+ return publicKey;
76
+ }
77
+ /**
78
+ * Validate a recovery phrase without importing it
79
+ */
80
+ static validatePhrase(phrase) {
81
+ const normalizedPhrase = phrase.trim().toLowerCase();
82
+ return bip39.validateMnemonic(normalizedPhrase);
83
+ }
84
+ /**
85
+ * Get the word list for autocomplete/validation
86
+ */
87
+ static getWordList() {
88
+ return bip39.wordlists.english;
89
+ }
90
+ /**
91
+ * Check if a word is valid in the BIP39 word list
92
+ */
93
+ static isValidWord(word) {
94
+ return bip39.wordlists.english.includes(word.toLowerCase());
95
+ }
96
+ /**
97
+ * Get suggestions for a partial word
98
+ */
99
+ static getSuggestions(partial, limit = 5) {
100
+ const lowerPartial = partial.toLowerCase();
101
+ return bip39.wordlists.english
102
+ .filter((word) => word.startsWith(lowerPartial))
103
+ .slice(0, limit);
104
+ }
105
+ /**
106
+ * Derive the public key from a phrase without storing
107
+ * Useful for verification before importing
108
+ */
109
+ static async derivePublicKeyFromPhrase(phrase) {
110
+ const normalizedPhrase = phrase.trim().toLowerCase();
111
+ if (!bip39.validateMnemonic(normalizedPhrase)) {
112
+ throw new Error('Invalid recovery phrase');
113
+ }
114
+ const seed = await bip39.mnemonicToSeed(normalizedPhrase);
115
+ const seedSlice = seed.subarray ? seed.subarray(0, 32) : seed.slice(0, 32);
116
+ const privateKeyHex = toHex(seedSlice);
117
+ return KeyManager.derivePublicKey(privateKeyHex);
118
+ }
119
+ /**
120
+ * Convert a phrase to its word array
121
+ */
122
+ static phraseToWords(phrase) {
123
+ return phrase.trim().toLowerCase().split(/\s+/);
124
+ }
125
+ /**
126
+ * Convert a word array to a phrase string
127
+ */
128
+ static wordsToPhrase(words) {
129
+ return words.map(w => w.toLowerCase().trim()).join(' ');
130
+ }
131
+ }
132
+ export default RecoveryPhraseService;
@@ -0,0 +1,259 @@
1
+ /**
2
+ * Signature Service - ECDSA Digital Signatures
3
+ *
4
+ * Handles signing and verification of messages using ECDSA secp256k1.
5
+ * Used for authenticating requests and proving identity ownership.
6
+ */
7
+ import { ec as EC } from 'elliptic';
8
+ import { KeyManager } from './keyManager';
9
+ // Lazy import for expo-crypto
10
+ let ExpoCrypto = null;
11
+ const ec = new EC('secp256k1');
12
+ /**
13
+ * Check if we're in a React Native environment
14
+ */
15
+ function isReactNative() {
16
+ return typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
17
+ }
18
+ /**
19
+ * Check if we're in a Node.js environment
20
+ */
21
+ function isNodeJS() {
22
+ return typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
23
+ }
24
+ /**
25
+ * Initialize expo-crypto module
26
+ */
27
+ async function initExpoCrypto() {
28
+ if (!ExpoCrypto) {
29
+ ExpoCrypto = await import('expo-crypto');
30
+ }
31
+ return ExpoCrypto;
32
+ }
33
+ /**
34
+ * Compute SHA-256 hash of a string
35
+ */
36
+ async function sha256(message) {
37
+ // In React Native, always use expo-crypto
38
+ if (isReactNative() || !isNodeJS()) {
39
+ const Crypto = await initExpoCrypto();
40
+ return Crypto.digestStringAsync(Crypto.CryptoDigestAlgorithm.SHA256, message);
41
+ }
42
+ // In Node.js, use Node's crypto module
43
+ // Use Function constructor to prevent Metro bundler from statically analyzing this require
44
+ // This ensures the require is only evaluated in Node.js runtime, not during Metro bundling
45
+ try {
46
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval
47
+ const getCrypto = new Function('return require("crypto")');
48
+ const crypto = getCrypto();
49
+ return crypto.createHash('sha256').update(message).digest('hex');
50
+ }
51
+ catch (error) {
52
+ // Fallback to expo-crypto if Node crypto fails
53
+ const Crypto = await initExpoCrypto();
54
+ return Crypto.digestStringAsync(Crypto.CryptoDigestAlgorithm.SHA256, message);
55
+ }
56
+ }
57
+ export class SignatureService {
58
+ /**
59
+ * Generate a random challenge string (for offline use)
60
+ * Uses expo-crypto in React Native, crypto.randomBytes in Node.js
61
+ */
62
+ static async generateChallenge() {
63
+ if (isReactNative() || !isNodeJS()) {
64
+ // Use expo-crypto for React Native (expo-random is deprecated)
65
+ const Crypto = await initExpoCrypto();
66
+ const randomBytes = await Crypto.getRandomBytesAsync(32);
67
+ return Array.from(randomBytes)
68
+ .map((b) => b.toString(16).padStart(2, '0'))
69
+ .join('');
70
+ }
71
+ // Node.js fallback
72
+ try {
73
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval
74
+ const getCrypto = new Function('return require("crypto")');
75
+ const crypto = getCrypto();
76
+ return crypto.randomBytes(32).toString('hex');
77
+ }
78
+ catch (error) {
79
+ // Fallback to expo-crypto if Node crypto fails
80
+ const Crypto = await initExpoCrypto();
81
+ const randomBytes = await Crypto.getRandomBytesAsync(32);
82
+ return Array.from(randomBytes)
83
+ .map((b) => b.toString(16).padStart(2, '0'))
84
+ .join('');
85
+ }
86
+ }
87
+ /**
88
+ * Hash a message using SHA-256
89
+ */
90
+ static async hashMessage(message) {
91
+ return sha256(message);
92
+ }
93
+ /**
94
+ * Sign a message using the stored private key
95
+ * Returns the signature in DER format (hex encoded)
96
+ */
97
+ static async sign(message) {
98
+ const keyPair = await KeyManager.getKeyPairObject();
99
+ if (!keyPair) {
100
+ throw new Error('No identity found. Please create or import an identity first.');
101
+ }
102
+ const messageHash = await sha256(message);
103
+ const signature = keyPair.sign(messageHash);
104
+ return signature.toDER('hex');
105
+ }
106
+ /**
107
+ * Sign a message with an explicit private key (without storing)
108
+ * Useful for one-time operations or testing
109
+ */
110
+ static async signWithKey(message, privateKey) {
111
+ const keyPair = ec.keyFromPrivate(privateKey);
112
+ const messageHash = await sha256(message);
113
+ const signature = keyPair.sign(messageHash);
114
+ return signature.toDER('hex');
115
+ }
116
+ /**
117
+ * Verify a signature against a message and public key
118
+ */
119
+ static async verify(message, signature, publicKey) {
120
+ try {
121
+ const key = ec.keyFromPublic(publicKey, 'hex');
122
+ const messageHash = await sha256(message);
123
+ return key.verify(messageHash, signature);
124
+ }
125
+ catch {
126
+ return false;
127
+ }
128
+ }
129
+ /**
130
+ * Synchronous verification (for Node.js backend)
131
+ * Uses crypto module directly for hashing
132
+ * Note: This method should only be used in Node.js environments
133
+ */
134
+ static verifySync(message, signature, publicKey) {
135
+ try {
136
+ if (!isNodeJS()) {
137
+ // In React Native, use async verify instead
138
+ throw new Error('verifySync should only be used in Node.js. Use verify() in React Native.');
139
+ }
140
+ // Use Function constructor to prevent Metro bundler from statically analyzing this require
141
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval
142
+ const getCrypto = new Function('return require("crypto")');
143
+ const crypto = getCrypto();
144
+ const key = ec.keyFromPublic(publicKey, 'hex');
145
+ const messageHash = crypto.createHash('sha256').update(message).digest('hex');
146
+ return key.verify(messageHash, signature);
147
+ }
148
+ catch {
149
+ return false;
150
+ }
151
+ }
152
+ /**
153
+ * Create a signed message object with metadata
154
+ */
155
+ static async createSignedMessage(message) {
156
+ const publicKey = await KeyManager.getPublicKey();
157
+ if (!publicKey) {
158
+ throw new Error('No identity found. Please create or import an identity first.');
159
+ }
160
+ const timestamp = Date.now();
161
+ const messageWithTimestamp = `${message}:${timestamp}`;
162
+ const signature = await SignatureService.sign(messageWithTimestamp);
163
+ return {
164
+ message,
165
+ signature,
166
+ publicKey,
167
+ timestamp,
168
+ };
169
+ }
170
+ /**
171
+ * Verify a signed message object
172
+ * Checks both signature validity and timestamp freshness
173
+ */
174
+ static async verifySignedMessage(signedMessage, maxAgeMs = 5 * 60 * 1000 // 5 minutes default
175
+ ) {
176
+ const { message, signature, publicKey, timestamp } = signedMessage;
177
+ // Check timestamp freshness
178
+ const now = Date.now();
179
+ if (now - timestamp > maxAgeMs) {
180
+ return false;
181
+ }
182
+ // Verify signature
183
+ const messageWithTimestamp = `${message}:${timestamp}`;
184
+ return SignatureService.verify(messageWithTimestamp, signature, publicKey);
185
+ }
186
+ /**
187
+ * Create a signed authentication challenge response
188
+ * Used for challenge-response authentication
189
+ */
190
+ static async signChallenge(challenge) {
191
+ const publicKey = await KeyManager.getPublicKey();
192
+ if (!publicKey) {
193
+ throw new Error('No identity found. Please create or import an identity first.');
194
+ }
195
+ const timestamp = Date.now();
196
+ const message = `auth:${publicKey}:${challenge}:${timestamp}`;
197
+ const signature = await SignatureService.sign(message);
198
+ return {
199
+ challenge: signature,
200
+ publicKey,
201
+ timestamp,
202
+ };
203
+ }
204
+ /**
205
+ * Verify a challenge response
206
+ */
207
+ static async verifyChallengeResponse(originalChallenge, response, maxAgeMs = 5 * 60 * 1000) {
208
+ const { challenge: signature, publicKey, timestamp } = response;
209
+ // Check timestamp freshness
210
+ const now = Date.now();
211
+ if (now - timestamp > maxAgeMs) {
212
+ return false;
213
+ }
214
+ const message = `auth:${publicKey}:${originalChallenge}:${timestamp}`;
215
+ return SignatureService.verify(message, signature, publicKey);
216
+ }
217
+ /**
218
+ * Create a registration signature
219
+ * Used when registering a new identity with the server
220
+ * Format matches server expectation: oxy:register:{publicKey}:{timestamp}
221
+ */
222
+ static async createRegistrationSignature() {
223
+ const publicKey = await KeyManager.getPublicKey();
224
+ if (!publicKey) {
225
+ throw new Error('No identity found. Please create or import an identity first.');
226
+ }
227
+ const timestamp = Date.now();
228
+ const message = `oxy:register:${publicKey}:${timestamp}`;
229
+ const signature = await SignatureService.sign(message);
230
+ return {
231
+ signature,
232
+ publicKey,
233
+ timestamp,
234
+ };
235
+ }
236
+ /**
237
+ * Sign arbitrary data for API requests
238
+ * Creates a canonical string representation and signs it
239
+ */
240
+ static async signRequestData(data) {
241
+ const publicKey = await KeyManager.getPublicKey();
242
+ if (!publicKey) {
243
+ throw new Error('No identity found. Please create or import an identity first.');
244
+ }
245
+ const timestamp = Date.now();
246
+ // Create canonical string representation
247
+ const sortedKeys = Object.keys(data).sort();
248
+ const canonicalParts = sortedKeys.map(key => `${key}:${JSON.stringify(data[key])}`);
249
+ const canonicalString = canonicalParts.join('|');
250
+ const message = `request:${publicKey}:${timestamp}:${canonicalString}`;
251
+ const signature = await SignatureService.sign(message);
252
+ return {
253
+ signature,
254
+ publicKey,
255
+ timestamp,
256
+ };
257
+ }
258
+ }
259
+ export default SignatureService;
@@ -0,0 +1,69 @@
1
+ // Use JSON locale files (RN Metro supports static requires reliably)
2
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
3
+ const enUS = require('./locales/en-US.json');
4
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
5
+ const esES = require('./locales/es-ES.json');
6
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
7
+ const caES = require('./locales/ca-ES.json');
8
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
9
+ const frFR = require('./locales/fr-FR.json');
10
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
11
+ const deDE = require('./locales/de-DE.json');
12
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
13
+ const itIT = require('./locales/it-IT.json');
14
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
15
+ const ptPT = require('./locales/pt-PT.json');
16
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
17
+ const jaJP = require('./locales/ja-JP.json');
18
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
19
+ const koKR = require('./locales/ko-KR.json');
20
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
21
+ const zhCN = require('./locales/zh-CN.json');
22
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
23
+ const arSA = require('./locales/ar-SA.json');
24
+ const DICTS = {
25
+ 'en': enUS,
26
+ 'en-US': enUS,
27
+ 'es': esES,
28
+ 'es-ES': esES,
29
+ 'ca': caES,
30
+ 'ca-ES': caES,
31
+ 'fr': frFR,
32
+ 'fr-FR': frFR,
33
+ 'de': deDE,
34
+ 'de-DE': deDE,
35
+ 'it': itIT,
36
+ 'it-IT': itIT,
37
+ 'pt': ptPT,
38
+ 'pt-PT': ptPT,
39
+ 'ja': jaJP,
40
+ 'ja-JP': jaJP,
41
+ 'ko': koKR,
42
+ 'ko-KR': koKR,
43
+ 'zh': zhCN,
44
+ 'zh-CN': zhCN,
45
+ 'ar': arSA,
46
+ 'ar-SA': arSA,
47
+ };
48
+ const FALLBACK = 'en-US';
49
+ function getNested(obj, path) {
50
+ return path.split('.').reduce((acc, key) => (acc && acc[key] != null ? acc[key] : undefined), obj);
51
+ }
52
+ export function translate(locale, key, vars) {
53
+ const lang = locale && DICTS[locale] ? locale : FALLBACK;
54
+ const dict = DICTS[lang] || DICTS[FALLBACK];
55
+ let val = getNested(dict, key);
56
+ if (typeof val !== 'string')
57
+ return key; // fallback to key if missing
58
+ if (vars) {
59
+ Object.keys(vars).forEach(k => {
60
+ const token = `{{${k}}}`;
61
+ val = val.replaceAll(token, String(vars[k]));
62
+ });
63
+ }
64
+ return val;
65
+ }
66
+ export function hasKey(locale, key) {
67
+ const lang = locale && DICTS[locale] ? locale : FALLBACK;
68
+ return getNested(DICTS[lang], key) != null || getNested(DICTS[FALLBACK], key) != null;
69
+ }
@@ -0,0 +1,120 @@
1
+ {
2
+ "signin": {
3
+ "title": "تسجيل الدخول",
4
+ "subtitle": "سجل الدخول للمتابعة",
5
+ "addAccountTitle": "إضافة حساب آخر",
6
+ "addAccountSubtitle": "تسجيل الدخول بحساب آخر",
7
+ "username": {
8
+ "label": "اسم المستخدم",
9
+ "placeholder": "أدخل اسم المستخدم",
10
+ "helper": "3-30 حرفًا، أحرف وأرقام فقط",
11
+ "required": "يرجى إدخال اسم المستخدم.",
12
+ "minLength": "يجب أن يكون اسم المستخدم 3 أحرف على الأقل."
13
+ },
14
+ "password": {
15
+ "label": "كلمة المرور",
16
+ "placeholder": "أدخل كلمة المرور",
17
+ "required": "يرجى إدخال كلمة المرور.",
18
+ "hint": "أدخل كلمة المرور لتسجيل الدخول"
19
+ },
20
+ "actions": {
21
+ "continue": "متابعة",
22
+ "back": "رجوع",
23
+ "signIn": "تسجيل الدخول",
24
+ "verify": "التحقق",
25
+ "openAccountSwitcher": "التبديل إلى حساب آخر",
26
+ "openAccountSwitcherSubtitle": "{{count}} حسابات أخرى متاحة",
27
+ "openAccountSwitcherSubtitle_singular": "حساب آخر واحد متاح",
28
+ "openAccountSwitcherSubtitle_zero": "راجع حساباتك المحفوظة",
29
+ "manageAccounts": "إدارة الحسابات المحفوظة",
30
+ "manageAccountsSubtitle": "مراجعة الجلسات أو إزالتها أو تسجيل الخروج",
31
+ "loadingOtherAccounts": "جاري تحميل الحسابات الأخرى…",
32
+ "switchAccountFailed": "لم نتمكن من التبديل بين الحسابات. يرجى المحاولة مرة أخرى."
33
+ },
34
+ "forgotPrompt": "نسيت كلمة المرور؟",
35
+ "security": {
36
+ "dataSecure": "بياناتك مشفرة وآمنة"
37
+ },
38
+ "currentlySignedInAs": "مسجل الدخول حاليًا كـ",
39
+ "alreadySignedInWith": "مسجل الدخول بالفعل بـ",
40
+ "alreadySignedIn": "مسجل الدخول بالفعل",
41
+ "alreadySignedInMessage": "هذا الحساب مسجل الدخول بالفعل. المتابعة بهذا الحساب؟",
42
+ "continueWithAccount": "متابعة",
43
+ "currentAccount": "الحالي",
44
+ "or": "أو",
45
+ "viewAllAccounts": "عرض {{count}} المزيد",
46
+ "status": {
47
+ "accountSwitched": "استخدام {{name}} الآن"
48
+ },
49
+ },
50
+ "signup": {
51
+ "welcome": {
52
+ "title": "مرحبًا بك في Oxy!",
53
+ "subtitle": "أنشئ حسابك في خطوات قليلة",
54
+ "haveAccount": "لديك حساب بالفعل؟",
55
+ "signInCta": "تسجيل الدخول"
56
+ },
57
+ "identity": {
58
+ "title": "من أنت؟",
59
+ "subtitle": "اختر اسم المستخدم وأدخل بريدك الإلكتروني"
60
+ },
61
+ "username": {
62
+ "helper": "3-30 حرفًا، أحرف وأرقام فقط",
63
+ "required": "يرجى إدخال اسم مستخدم",
64
+ "minLength": "يجب أن يكون اسم المستخدم 3 أحرف على الأقل"
65
+ },
66
+ "email": {
67
+ "required": "يرجى إدخال عنوان بريد إلكتروني",
68
+ "invalid": "يرجى إدخال عنوان بريد إلكتروني صالح",
69
+ "helper": "لن نشارك بريدك الإلكتروني أبدًا"
70
+ },
71
+ "security": {
72
+ "title": "أمّن حسابك",
73
+ "subtitle": "أنشئ كلمة مرور قوية لحماية حسابك"
74
+ },
75
+ "password": {
76
+ "helper": "8 أحرف على الأقل",
77
+ "required": "يرجى إدخال كلمة مرور",
78
+ "minLength": "يجب أن تكون كلمة المرور 8 أحرف على الأقل",
79
+ "confirmRequired": "يرجى تأكيد كلمة المرور",
80
+ "mismatch": "كلمات المرور غير متطابقة",
81
+ "confirmHint": "أعد إدخال كلمة المرور للتأكيد"
82
+ },
83
+ "summary": {
84
+ "title": "أوشكت على الانتهاء!",
85
+ "subtitle": "راجع معلوماتك وأنشئ حسابك",
86
+ "sectionTitle": "معلومات الحساب",
87
+ "fields": {
88
+ "username": "اسم المستخدم",
89
+ "email": "البريد الإلكتروني",
90
+ "password": "كلمة المرور"
91
+ },
92
+ "notSet": "غير محدد",
93
+ "securityTip": "لمزيد من الأمان، قم بتمكين المصادقة الحيوية من إعدادات الحساب بعد إنشاء حسابك.",
94
+ "legalReminder": "بإنشاء حساب، فإنك توافق على شروط الخدمة وسياسة الخصوصية الخاصة بنا."
95
+ }
96
+ },
97
+ "common": {
98
+ "actions": {
99
+ "back": "رجوع",
100
+ "continue": "متابعة",
101
+ "next": "التالي",
102
+ "getStarted": "ابدأ",
103
+ "createAccount": "إنشاء حساب",
104
+ "signIn": "تسجيل الدخول",
105
+ "verify": "التحقق",
106
+ "resetPassword": "إعادة تعيين كلمة المرور"
107
+ },
108
+ "links": {
109
+ "recoverAccount": "استعادة حسابك",
110
+ "signUp": "التسجيل"
111
+ },
112
+ "labels": {
113
+ "username": "اسم المستخدم",
114
+ "email": "البريد الإلكتروني",
115
+ "password": "كلمة المرور",
116
+ "confirmPassword": "تأكيد كلمة المرور"
117
+ }
118
+ }
119
+ }
120
+