@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,435 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OxyServicesFedCMMixin = OxyServicesFedCMMixin;
4
+ exports.FedCMMixin = OxyServicesFedCMMixin;
5
+ const OxyServices_errors_1 = require("../OxyServices.errors");
6
+ const debugUtils_1 = require("../shared/utils/debugUtils");
7
+ const debug = (0, debugUtils_1.createDebugLogger)('FedCM');
8
+ // Global lock to prevent concurrent FedCM requests
9
+ // FedCM only allows one navigator.credentials.get request at a time
10
+ let fedCMRequestInProgress = false;
11
+ let fedCMRequestPromise = null;
12
+ let currentMediationMode = null;
13
+ /**
14
+ * Federated Credential Management (FedCM) Authentication Mixin
15
+ *
16
+ * Implements the modern browser-native identity federation API that enables
17
+ * Google-style cross-domain authentication without third-party cookies.
18
+ *
19
+ * Browser Support:
20
+ * - Chrome 108+
21
+ * - Safari 16.4+
22
+ * - Edge 108+
23
+ * - Firefox: Not yet supported (fallback required)
24
+ *
25
+ * Key Features:
26
+ * - No redirects or popups required
27
+ * - Browser-native UI prompts
28
+ * - Privacy-preserving (IdP can't track users)
29
+ * - Automatic SSO across domains
30
+ * - Silent re-authentication support
31
+ *
32
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/FedCM_API
33
+ */
34
+ function OxyServicesFedCMMixin(Base) {
35
+ var _a;
36
+ return _a = class extends Base {
37
+ constructor(...args) {
38
+ super(...args);
39
+ }
40
+ /**
41
+ * Check if FedCM is supported in the current browser
42
+ */
43
+ static isFedCMSupported() {
44
+ if (typeof window === 'undefined')
45
+ return false;
46
+ return 'IdentityCredential' in window && 'navigator' in window && 'credentials' in navigator;
47
+ }
48
+ /**
49
+ * Instance method to check FedCM support
50
+ */
51
+ isFedCMSupported() {
52
+ return this.constructor.isFedCMSupported();
53
+ }
54
+ /**
55
+ * Sign in using FedCM (Federated Credential Management API)
56
+ *
57
+ * This provides a Google-style authentication experience:
58
+ * - Browser shows native "Sign in with Oxy" prompt
59
+ * - No redirect or popup required
60
+ * - User approves → credential exchange happens in browser
61
+ * - All apps automatically get SSO after first sign-in
62
+ *
63
+ * @param options - Authentication options
64
+ * @returns Session with access token and user data
65
+ * @throws {OxyAuthenticationError} If FedCM not supported or user cancels
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * try {
70
+ * const session = await oxyServices.signInWithFedCM();
71
+ * console.log('Signed in:', session.user);
72
+ * } catch (error) {
73
+ * // Fallback to popup or redirect auth
74
+ * await oxyServices.signInWithPopup();
75
+ * }
76
+ * ```
77
+ */
78
+ async signInWithFedCM(options = {}) {
79
+ if (!this.isFedCMSupported()) {
80
+ throw new OxyServices_errors_1.OxyAuthenticationError('FedCM not supported in this browser. Please update your browser or use an alternative sign-in method.');
81
+ }
82
+ try {
83
+ const nonce = options.nonce || this.generateNonce();
84
+ const clientId = this.getClientId();
85
+ debug.log('Interactive sign-in: Requesting credential for', clientId);
86
+ // Request credential from browser's native identity flow
87
+ const credential = await this.requestIdentityCredential({
88
+ configURL: this.constructor.DEFAULT_CONFIG_URL,
89
+ clientId,
90
+ nonce,
91
+ context: options.context,
92
+ });
93
+ if (!credential || !credential.token) {
94
+ throw new OxyServices_errors_1.OxyAuthenticationError('No credential received from browser');
95
+ }
96
+ debug.log('Interactive sign-in: Got credential, exchanging for session');
97
+ // Exchange FedCM ID token for Oxy session
98
+ const session = await this.exchangeIdTokenForSession(credential.token);
99
+ // Store access token in HttpService (extract from response or get from session)
100
+ if (session && session.accessToken) {
101
+ this.httpService.setTokens(session.accessToken);
102
+ }
103
+ debug.log('Interactive sign-in: Success!', { userId: session?.user?.id });
104
+ return session;
105
+ }
106
+ catch (error) {
107
+ debug.log('Interactive sign-in failed:', error);
108
+ if (error.name === 'AbortError') {
109
+ throw new OxyServices_errors_1.OxyAuthenticationError('Sign-in was cancelled by user');
110
+ }
111
+ if (error.name === 'NetworkError') {
112
+ throw new OxyServices_errors_1.OxyAuthenticationError('Network error during sign-in. Please check your connection.');
113
+ }
114
+ throw error;
115
+ }
116
+ }
117
+ /**
118
+ * Silent sign-in using FedCM
119
+ *
120
+ * Attempts to automatically re-authenticate the user without any UI.
121
+ * This is what enables "instant sign-in" across all Oxy domains after
122
+ * the user has signed in once.
123
+ *
124
+ * The browser will:
125
+ * 1. Check if user has previously signed in to Oxy
126
+ * 2. Check if user is still signed in at auth.oxy.so
127
+ * 3. If yes, automatically provide credential without prompting
128
+ *
129
+ * @returns Session if user is already signed in, null otherwise
130
+ *
131
+ * @example
132
+ * ```typescript
133
+ * // On app startup
134
+ * useEffect(() => {
135
+ * const checkAuth = async () => {
136
+ * const session = await oxyServices.silentSignInWithFedCM();
137
+ * if (session) {
138
+ * setUser(session.user);
139
+ * } else {
140
+ * // Show sign-in button
141
+ * }
142
+ * };
143
+ * checkAuth();
144
+ * }, []);
145
+ * ```
146
+ */
147
+ async silentSignInWithFedCM() {
148
+ if (!this.isFedCMSupported()) {
149
+ debug.log('Silent SSO: FedCM not supported in this browser');
150
+ return null;
151
+ }
152
+ const clientId = this.getClientId();
153
+ debug.log('Silent SSO: Starting for', clientId);
154
+ // First try silent mediation (no UI) - works if user previously consented
155
+ let credential = null;
156
+ try {
157
+ const nonce = this.generateNonce();
158
+ debug.log('Silent SSO: Attempting silent mediation...');
159
+ credential = await this.requestIdentityCredential({
160
+ configURL: this.constructor.DEFAULT_CONFIG_URL,
161
+ clientId,
162
+ nonce,
163
+ mediation: 'silent',
164
+ });
165
+ debug.log('Silent SSO: Silent mediation result:', { hasCredential: !!credential, hasToken: !!credential?.token });
166
+ }
167
+ catch (silentError) {
168
+ // Silent mediation failed - this is expected if user hasn't consented before or is in quiet period
169
+ const errorName = silentError instanceof Error ? silentError.name : 'Unknown';
170
+ const errorMessage = silentError instanceof Error ? silentError.message : String(silentError);
171
+ debug.log('Silent SSO: Silent mediation error (will try optional):', { name: errorName, message: errorMessage });
172
+ }
173
+ // If silent failed, try optional mediation which shows browser UI if needed
174
+ if (!credential || !credential.token) {
175
+ try {
176
+ const nonce = this.generateNonce();
177
+ debug.log('Silent SSO: Trying optional mediation (may show browser UI)...');
178
+ credential = await this.requestIdentityCredential({
179
+ configURL: this.constructor.DEFAULT_CONFIG_URL,
180
+ clientId,
181
+ nonce,
182
+ mediation: 'optional',
183
+ });
184
+ debug.log('Silent SSO: Optional mediation result:', { hasCredential: !!credential, hasToken: !!credential?.token });
185
+ }
186
+ catch (optionalError) {
187
+ const errorName = optionalError instanceof Error ? optionalError.name : 'Unknown';
188
+ const errorMessage = optionalError instanceof Error ? optionalError.message : String(optionalError);
189
+ debug.log('Silent SSO: Optional mediation also failed:', { name: errorName, message: errorMessage });
190
+ return null;
191
+ }
192
+ }
193
+ if (!credential || !credential.token) {
194
+ debug.log('Silent SSO: No credential returned (user may have dismissed prompt or is not logged in at IdP)');
195
+ return null;
196
+ }
197
+ debug.log('Silent SSO: Got credential, exchanging for session...');
198
+ let session;
199
+ try {
200
+ session = await this.exchangeIdTokenForSession(credential.token);
201
+ }
202
+ catch (exchangeError) {
203
+ debug.error('Silent SSO: Token exchange failed:', exchangeError);
204
+ return null;
205
+ }
206
+ // Validate session response has required fields
207
+ if (!session) {
208
+ debug.error('Silent SSO: Exchange returned null session');
209
+ return null;
210
+ }
211
+ if (!session.sessionId) {
212
+ debug.error('Silent SSO: Exchange returned session without sessionId:', session);
213
+ return null;
214
+ }
215
+ if (!session.user) {
216
+ debug.error('Silent SSO: Exchange returned session without user:', session);
217
+ return null;
218
+ }
219
+ // Set the access token
220
+ if (session.accessToken) {
221
+ this.httpService.setTokens(session.accessToken);
222
+ debug.log('Silent SSO: Access token set');
223
+ }
224
+ else {
225
+ debug.warn('Silent SSO: No accessToken in session response');
226
+ }
227
+ debug.log('Silent SSO: Success!', {
228
+ sessionId: session.sessionId?.substring(0, 8) + '...',
229
+ userId: session.user?.id
230
+ });
231
+ return session;
232
+ }
233
+ /**
234
+ * Request identity credential from browser using FedCM API
235
+ *
236
+ * Uses a global lock to prevent concurrent requests, as FedCM only
237
+ * allows one navigator.credentials.get request at a time.
238
+ *
239
+ * Interactive requests (optional/required) wait for any silent request to finish first.
240
+ *
241
+ * @private
242
+ */
243
+ async requestIdentityCredential(options) {
244
+ const requestedMediation = options.mediation || 'optional';
245
+ const isInteractive = requestedMediation !== 'silent';
246
+ debug.log('requestIdentityCredential called:', {
247
+ mediation: requestedMediation,
248
+ clientId: options.clientId,
249
+ inProgress: fedCMRequestInProgress,
250
+ });
251
+ // If a request is already in progress...
252
+ if (fedCMRequestInProgress && fedCMRequestPromise) {
253
+ debug.log('Request already in progress, waiting...');
254
+ // If current request is silent and new request is interactive,
255
+ // wait for silent to finish, then make the interactive request
256
+ if (currentMediationMode === 'silent' && isInteractive) {
257
+ try {
258
+ await fedCMRequestPromise;
259
+ }
260
+ catch {
261
+ // Ignore silent request errors
262
+ }
263
+ // Now fall through to make the interactive request
264
+ }
265
+ else {
266
+ // Same type of request - wait for the existing one
267
+ try {
268
+ return await fedCMRequestPromise;
269
+ }
270
+ catch {
271
+ return null;
272
+ }
273
+ }
274
+ }
275
+ fedCMRequestInProgress = true;
276
+ currentMediationMode = requestedMediation;
277
+ const controller = new AbortController();
278
+ // Use shorter timeout for silent mediation since it should be quick
279
+ const timeoutMs = requestedMediation === 'silent'
280
+ ? this.constructor.FEDCM_SILENT_TIMEOUT
281
+ : this.constructor.FEDCM_TIMEOUT;
282
+ const timeout = setTimeout(() => {
283
+ debug.log('Request timed out after', timeoutMs, 'ms (mediation:', requestedMediation + ')');
284
+ controller.abort();
285
+ }, timeoutMs);
286
+ fedCMRequestPromise = (async () => {
287
+ try {
288
+ debug.log('Calling navigator.credentials.get with mediation:', requestedMediation);
289
+ // Type assertion needed as FedCM types may not be in all TypeScript versions
290
+ const credential = (await navigator.credentials.get({
291
+ identity: {
292
+ providers: [
293
+ {
294
+ configURL: options.configURL,
295
+ clientId: options.clientId,
296
+ // Send nonce at both levels for backward compatibility
297
+ nonce: options.nonce, // For older browsers
298
+ params: {
299
+ nonce: options.nonce, // For Chrome 145+
300
+ },
301
+ ...(options.context && { loginHint: options.context }),
302
+ },
303
+ ],
304
+ },
305
+ mediation: requestedMediation,
306
+ signal: controller.signal,
307
+ }));
308
+ debug.log('navigator.credentials.get returned:', {
309
+ hasCredential: !!credential,
310
+ type: credential?.type,
311
+ hasToken: !!credential?.token,
312
+ });
313
+ if (!credential || credential.type !== 'identity') {
314
+ debug.log('No valid identity credential returned');
315
+ return null;
316
+ }
317
+ debug.log('Got valid identity credential with token');
318
+ return { token: credential.token };
319
+ }
320
+ catch (error) {
321
+ const errorName = error instanceof Error ? error.name : 'Unknown';
322
+ const errorMessage = error instanceof Error ? error.message : String(error);
323
+ debug.log('navigator.credentials.get error:', { name: errorName, message: errorMessage });
324
+ throw error;
325
+ }
326
+ finally {
327
+ clearTimeout(timeout);
328
+ fedCMRequestInProgress = false;
329
+ fedCMRequestPromise = null;
330
+ currentMediationMode = null;
331
+ }
332
+ })();
333
+ return fedCMRequestPromise;
334
+ }
335
+ /**
336
+ * Exchange FedCM ID token for Oxy session
337
+ *
338
+ * The ID token is a JWT issued by auth.oxy.so that proves the user's
339
+ * identity. We exchange it for a full Oxy session with access token.
340
+ *
341
+ * @private
342
+ */
343
+ async exchangeIdTokenForSession(idToken) {
344
+ debug.log('exchangeIdTokenForSession: Starting exchange...');
345
+ debug.log('exchangeIdTokenForSession: Token length:', idToken?.length);
346
+ debug.log('exchangeIdTokenForSession: Token preview:', idToken?.substring(0, 50) + '...');
347
+ try {
348
+ const response = await this.makeRequest('POST', '/api/fedcm/exchange', { id_token: idToken }, { cache: false });
349
+ debug.log('exchangeIdTokenForSession: Response received:', {
350
+ hasResponse: !!response,
351
+ hasSessionId: !!response?.sessionId,
352
+ hasUser: !!response?.user,
353
+ hasAccessToken: !!response?.accessToken,
354
+ userId: response?.user?.id,
355
+ username: response?.user?.username,
356
+ responseKeys: response ? Object.keys(response) : [],
357
+ });
358
+ return response;
359
+ }
360
+ catch (error) {
361
+ debug.error('exchangeIdTokenForSession: Error:', {
362
+ name: error instanceof Error ? error.name : 'Unknown',
363
+ message: error instanceof Error ? error.message : String(error),
364
+ stack: error instanceof Error ? error.stack : undefined,
365
+ });
366
+ throw error;
367
+ }
368
+ }
369
+ /**
370
+ * Revoke FedCM credential (sign out)
371
+ *
372
+ * This tells the browser to forget the FedCM credential for this app.
373
+ * The user will need to re-authenticate next time.
374
+ */
375
+ async revokeFedCMCredential() {
376
+ if (!this.isFedCMSupported()) {
377
+ return;
378
+ }
379
+ try {
380
+ // FedCM logout API (if available)
381
+ if ('IdentityCredential' in window && 'logout' in window.IdentityCredential) {
382
+ const clientId = this.getClientId();
383
+ await window.IdentityCredential.logout({
384
+ configURL: this.constructor.DEFAULT_CONFIG_URL,
385
+ clientId,
386
+ });
387
+ }
388
+ }
389
+ catch (error) {
390
+ // Silent failure
391
+ }
392
+ }
393
+ /**
394
+ * Get configuration for FedCM
395
+ *
396
+ * @returns FedCM configuration with browser support info
397
+ */
398
+ getFedCMConfig() {
399
+ return {
400
+ enabled: this.isFedCMSupported(),
401
+ configURL: this.constructor.DEFAULT_CONFIG_URL,
402
+ clientId: this.getClientId(),
403
+ };
404
+ }
405
+ /**
406
+ * Generate a cryptographically secure nonce for FedCM
407
+ *
408
+ * @private
409
+ */
410
+ generateNonce() {
411
+ if (typeof window !== 'undefined' && window.crypto && window.crypto.randomUUID) {
412
+ return window.crypto.randomUUID();
413
+ }
414
+ // Fallback for older browsers
415
+ return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
416
+ }
417
+ /**
418
+ * Get the client ID for this origin
419
+ *
420
+ * @private
421
+ */
422
+ getClientId() {
423
+ if (typeof window === 'undefined') {
424
+ return 'unknown';
425
+ }
426
+ return window.location.origin;
427
+ }
428
+ },
429
+ _a.DEFAULT_CONFIG_URL = 'https://auth.oxy.so/fedcm.json',
430
+ _a.FEDCM_TIMEOUT = 60000 // 1 minute for interactive
431
+ ,
432
+ _a.FEDCM_SILENT_TIMEOUT = 10000 // 10 seconds for silent mediation
433
+ ,
434
+ _a;
435
+ }
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OxyServicesKarmaMixin = OxyServicesKarmaMixin;
4
+ const mixinHelpers_1 = require("./mixinHelpers");
5
+ function OxyServicesKarmaMixin(Base) {
6
+ return class extends Base {
7
+ constructor(...args) {
8
+ super(...args);
9
+ }
10
+ /**
11
+ * Get user karma
12
+ */
13
+ async getUserKarma(userId) {
14
+ try {
15
+ return await this.makeRequest('GET', `/api/karma/${userId}`, undefined, {
16
+ cache: true,
17
+ cacheTTL: 2 * 60 * 1000, // 2 minutes cache
18
+ });
19
+ }
20
+ catch (error) {
21
+ throw this.handleError(error);
22
+ }
23
+ }
24
+ /**
25
+ * Give karma to user
26
+ */
27
+ async giveKarma(userId, amount, reason) {
28
+ try {
29
+ return await this.makeRequest('POST', `/api/karma/${userId}/give`, {
30
+ amount,
31
+ reason
32
+ }, { cache: false });
33
+ }
34
+ catch (error) {
35
+ throw this.handleError(error);
36
+ }
37
+ }
38
+ /**
39
+ * Get user karma total
40
+ * @param userId - The user ID
41
+ * @returns User karma total
42
+ */
43
+ async getUserKarmaTotal(userId) {
44
+ try {
45
+ return await this.makeRequest('GET', `/api/karma/${userId}/total`, undefined, {
46
+ cache: true,
47
+ cacheTTL: mixinHelpers_1.CACHE_TIMES.MEDIUM,
48
+ });
49
+ }
50
+ catch (error) {
51
+ throw this.handleError(error);
52
+ }
53
+ }
54
+ /**
55
+ * Get user karma history
56
+ * @param userId - The user ID
57
+ * @param limit - Optional limit for results
58
+ * @param offset - Optional offset for pagination
59
+ * @returns User karma history
60
+ */
61
+ async getUserKarmaHistory(userId, limit, offset) {
62
+ try {
63
+ const params = {};
64
+ if (limit)
65
+ params.limit = limit;
66
+ if (offset)
67
+ params.offset = offset;
68
+ return await this.makeRequest('GET', `/api/karma/${userId}/history`, params, {
69
+ cache: true,
70
+ cacheTTL: mixinHelpers_1.CACHE_TIMES.MEDIUM,
71
+ });
72
+ }
73
+ catch (error) {
74
+ throw this.handleError(error);
75
+ }
76
+ }
77
+ /**
78
+ * Get karma leaderboard
79
+ * @returns Karma leaderboard
80
+ */
81
+ async getKarmaLeaderboard() {
82
+ try {
83
+ return await this.makeRequest('GET', '/api/karma/leaderboard', undefined, {
84
+ cache: true,
85
+ cacheTTL: mixinHelpers_1.CACHE_TIMES.LONG,
86
+ });
87
+ }
88
+ catch (error) {
89
+ throw this.handleError(error);
90
+ }
91
+ }
92
+ /**
93
+ * Get karma rules
94
+ * @returns Karma rules
95
+ */
96
+ async getKarmaRules() {
97
+ try {
98
+ return await this.makeRequest('GET', '/api/karma/rules', undefined, {
99
+ cache: true,
100
+ cacheTTL: mixinHelpers_1.CACHE_TIMES.EXTRA_LONG, // Rules don't change often
101
+ });
102
+ }
103
+ catch (error) {
104
+ throw this.handleError(error);
105
+ }
106
+ }
107
+ };
108
+ }