@oxyhq/services 24.0.0 → 24.0.1

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.
@@ -22,7 +22,16 @@ import so.oxy.storage.RecoveryPolicy
22
22
  * Neither ever rotates the credential — the server does not rotate it either.
23
23
  */
24
24
  internal object OxyBackgroundSessionStore {
25
- private const val PREFS_NAME = "oxy_background_session"
25
+ /**
26
+ * Suffix is the hosting app's package name. Oxy apps share the `so.oxy.shared`
27
+ * Android UID (and therefore one data directory), so a single global prefs name
28
+ * would let one app that leaves `backgroundSession` off wipe another app's
29
+ * live credential on launch. Scoping by package keeps each app's widget/sync
30
+ * store independent.
31
+ */
32
+ private const val PREFS_NAME_PREFIX = "oxy_background_session_"
33
+
34
+ private fun prefsName(context: Context): String = PREFS_NAME_PREFIX + context.packageName
26
35
 
27
36
  private const val KEY_BASE_URL = "baseUrl"
28
37
  private const val KEY_DEVICE_ID = "deviceId"
@@ -63,7 +72,7 @@ internal object OxyBackgroundSessionStore {
63
72
  * escalation is not merely discouraged here, it is unreachable.
64
73
  */
65
74
  private fun prefs(context: Context): SharedPreferences =
66
- OxyEncryptedPrefs.open(context, PREFS_NAME, RecoveryPolicy.RebuildFileOnly)
75
+ OxyEncryptedPrefs.open(context, prefsName(context), RecoveryPolicy.RebuildFileOnly)
67
76
 
68
77
  /**
69
78
  * The stored credential, or null when absent, incomplete, or expired.
@@ -7,6 +7,7 @@ exports.clearBackgroundSession = clearBackgroundSession;
7
7
  exports.isBackgroundSessionSupported = isBackgroundSessionSupported;
8
8
  exports.syncBackgroundSession = syncBackgroundSession;
9
9
  var _expoModulesCore = require("expo-modules-core");
10
+ var _reactNative = require("react-native");
10
11
  var _core = require("@oxyhq/core");
11
12
  /**
12
13
  * The JS half of the native background session credential.
@@ -35,10 +36,13 @@ var _core = require("@oxyhq/core");
35
36
  * live in different stores with one writer each, so there is nothing to keep in
36
37
  * lockstep and no state that can disagree.
37
38
  *
38
- * Android only. `requireOptionalNativeModule` resolves `null` on web and on any
39
- * app that does not ship the native module, and every function here degrades to a
40
- * no-op in that case so an app can enable this and lose nothing on other
41
- * platforms.
39
+ * ## Android only, and web is refused rather than merely unsupported
40
+ *
41
+ * Web is gated off by an explicit platform check, not by the native module being
42
+ * absent — provisioning in a browser origin would be a security downgrade, so it
43
+ * must not depend on an incidental fact. See {@link loadNativeModule}. iOS has no
44
+ * implementation yet and resolves to no module, so every function here degrades to
45
+ * a no-op: an app can enable this and lose nothing on other platforms.
42
46
  */
43
47
 
44
48
  /**
@@ -58,7 +62,30 @@ const RENEW_WHEN_REMAINING_MS = 7 * 24 * 60 * 60 * 1000;
58
62
  let nativeModule;
59
63
 
60
64
  /**
61
- * Resolve the native module once.
65
+ * Resolve the native module once — and refuse outright on web.
66
+ *
67
+ * ## Web is excluded on purpose, and not merely because the module is absent
68
+ *
69
+ * The credential is deliberately NON-ROTATING and long-lived, which is safe only
70
+ * because native background code is its sole consumer and nobody rotates it. A
71
+ * browser origin already holds the ROTATING `deviceSecret`, and it has no
72
+ * background worker to serve — so provisioning there would add a strictly weaker
73
+ * long-lived secret alongside a stronger short-lived one. That is a downgrade, not
74
+ * a harmless no-op, and the harm lands at the PROVISION CALL (which mints a live
75
+ * server-side credential) regardless of whether anything manages to store it.
76
+ *
77
+ * That property must not rest on `requireOptionalNativeModule` happening to return
78
+ * `null` on web. It does today — the module is registered for android only — but
79
+ * that is an incidental fact a future web shim or a web-build native-module mock
80
+ * would quietly reverse. So the platform is checked FIRST, before we even ask the
81
+ * registry, and every entry point in this module inherits the gate.
82
+ *
83
+ * (A pre-deploy server makes this visible too: oxy-api's `sessionDevice` router
84
+ * applies `requireSameSiteOrigin` path-agnostically, so an unmatched path answers
85
+ * `404` to a native caller but `403 BAD_ORIGIN` to a browser one. Core's degrade is
86
+ * deliberately narrow — `404 → null` only — because a real origin misconfiguration
87
+ * on a CURRENT server also returns 403, and swallowing that would hide a genuine
88
+ * bug. Platform gating belongs here, where the platform is actually known.)
62
89
  *
63
90
  * `requireOptionalNativeModule` (a static import Metro always resolves) rather
64
91
  * than a dynamic `import(moduleName)`: the latter compiles to `require(variable)`
@@ -68,6 +95,10 @@ let nativeModule;
68
95
  */
69
96
  function loadNativeModule() {
70
97
  if (nativeModule === undefined) {
98
+ if (_reactNative.Platform.OS === 'web') {
99
+ nativeModule = null;
100
+ return nativeModule;
101
+ }
71
102
  const native = (0, _expoModulesCore.requireOptionalNativeModule)('OxyBackgroundSession');
72
103
  nativeModule = native && typeof native.put === 'function' && typeof native.clear === 'function' && typeof native.peek === 'function' ? {
73
104
  put: native.put.bind(native),
@@ -1 +1 @@
1
- {"version":3,"names":["_expoModulesCore","require","_core","RENEW_WHEN_REMAINING_MS","nativeModule","loadNativeModule","undefined","native","requireOptionalNativeModule","put","clear","peek","bind","isBackgroundSessionSupported","clearBackgroundSession","error","logger","warn","component","assertProvisioningAvailable","oxyServices","provisionBackgroundCredential","message","Error","syncBackgroundSession","input","userId","canUsePrivateApi","isCurrent","stored","accountId","isForCurrentAccount","hasLifeLeft","expiresAt","Date","now","provisioned","parse","Number","isFinite","persisted","baseUrl","getBaseURL","deviceId","secret"],"sourceRoot":"../../../../src","sources":["ui/session/backgroundSession.ts"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_expoModulesCore","require","_reactNative","_core","RENEW_WHEN_REMAINING_MS","nativeModule","loadNativeModule","undefined","Platform","OS","native","requireOptionalNativeModule","put","clear","peek","bind","isBackgroundSessionSupported","clearBackgroundSession","error","logger","warn","component","assertProvisioningAvailable","oxyServices","provisionBackgroundCredential","message","Error","syncBackgroundSession","input","userId","canUsePrivateApi","isCurrent","stored","accountId","isForCurrentAccount","hasLifeLeft","expiresAt","Date","now","provisioned","parse","Number","isFinite","persisted","baseUrl","getBaseURL","deviceId","secret"],"sourceRoot":"../../../../src","sources":["ui/session/backgroundSession.ts"],"mappings":";;;;;;;;AAmCA,IAAAA,gBAAA,GAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AAEA,IAAAE,KAAA,GAAAF,OAAA;AAtCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAMA;AACA;AACA;AACA;AACA;;AAiBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMG,uBAAuB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI;AAEvD,IAAIC,YAAiE;;AAErE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASC,gBAAgBA,CAAA,EAA4C;EACnE,IAAID,YAAY,KAAKE,SAAS,EAAE;IAC9B,IAAIC,qBAAQ,CAACC,EAAE,KAAK,KAAK,EAAE;MACzBJ,YAAY,GAAG,IAAI;MACnB,OAAOA,YAAY;IACrB;IACA,MAAMK,MAAM,GAAG,IAAAC,4CAA2B,EACxC,sBACF,CAAC;IACDN,YAAY,GACVK,MAAM,IACN,OAAOA,MAAM,CAACE,GAAG,KAAK,UAAU,IAChC,OAAOF,MAAM,CAACG,KAAK,KAAK,UAAU,IAClC,OAAOH,MAAM,CAACI,IAAI,KAAK,UAAU,GAC7B;MACEF,GAAG,EAAEF,MAAM,CAACE,GAAG,CAACG,IAAI,CAACL,MAAM,CAAC;MAC5BG,KAAK,EAAEH,MAAM,CAACG,KAAK,CAACE,IAAI,CAACL,MAAM,CAAC;MAChCI,IAAI,EAAEJ,MAAM,CAACI,IAAI,CAACC,IAAI,CAACL,MAAM;IAC/B,CAAC,GACD,IAAI;EACZ;EACA,OAAOL,YAAY;AACrB;;AAEA;AACO,SAASW,4BAA4BA,CAAA,EAAY;EACtD,OAAOV,gBAAgB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","ignoreList":[]}
@@ -38,11 +38,10 @@ function useBackgroundSessionSync({
38
38
  return;
39
39
  }
40
40
  if (!enabled) {
41
- // An app that turns the feature OFF must not leave a live credential at
42
- // rest for the rest of its 30-day lifetime. The cost is one local store
43
- // open per launch on Android apps that never use the feature, which is
44
- // accepted deliberately: correct revocation is worth more than skipping a
45
- // memoized keystore open.
41
+ // An app that leaves the feature off must not keep a credential THIS app
42
+ // provisioned earlier. The store is package-scoped (see
43
+ // OxyBackgroundSessionStore), so this cannot revoke a sibling Oxy app's
44
+ // credential under the shared UID.
46
45
  void (0, _backgroundSession.clearBackgroundSession)();
47
46
  return;
48
47
  }
@@ -1 +1 @@
1
- {"version":3,"names":["_react","require","_reactNative","_backgroundSession","useBackgroundSessionSync","enabled","oxyServices","userId","canUsePrivateApi","useEffect","isBackgroundSessionSupported","clearBackgroundSession","current","run","syncBackgroundSession","isCurrent","subscription","AppState","addEventListener","state","remove"],"sourceRoot":"../../../../src","sources":["ui/session/useBackgroundSessionSync.ts"],"mappings":";;;;;;AAQA,IAAAA,MAAA,GAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AAEA,IAAAE,kBAAA,GAAAF,OAAA;AAXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAoBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASG,wBAAwBA,CAAC;EACvCC,OAAO;EACPC,WAAW;EACXC,MAAM;EACNC;AAC6B,CAAC,EAAQ;EACtC,IAAAC,gBAAS,EAAC,MAAM;IACd,IAAI,CAAC,IAAAC,+CAA4B,EAAC,CAAC,EAAE;MACnC;IACF;IAEA,IAAI,CAACL,OAAO,EAAE;MACZ;MACA;MACA;MACA;MACA;MACA,KAAK,IAAAM,yCAAsB,EAAC,CAAC;MAC7B;IACF;;IAEA;IACA;IACA,IAAIC,OAAO,GAAG,IAAI;IAClB;IACA;IACA;IACA;IACA;IACA,MAAMC,GAAG,GAAGA,CAAA,KAAM;MAChB,KAAK,IAAAC,wCAAqB,EAAC;QACzBR,WAAW;QACXC,MAAM;QACNC,gBAAgB;QAChBO,SAAS,EAAEA,CAAA,KAAMH;MACnB,CAAC,CAAC;IACJ,CAAC;IAEDC,GAAG,CAAC,CAAC;IACL,MAAMG,YAAY,GAAGC,qBAAQ,CAACC,gBAAgB,CAAC,QAAQ,EAAGC,KAAK,IAAK;MAClE,IAAIA,KAAK,KAAK,QAAQ,EAAE;QACtBN,GAAG,CAAC,CAAC;MACP;IACF,CAAC,CAAC;IAEF,OAAO,MAAM;MACXD,OAAO,GAAG,KAAK;MACfI,YAAY,CAACI,MAAM,CAAC,CAAC;MACrB;MACA;MACA;MACA;IACF,CAAC;EACH,CAAC,EAAE,CAACf,OAAO,EAAEC,WAAW,EAAEC,MAAM,EAAEC,gBAAgB,CAAC,CAAC;AACtD","ignoreList":[]}
1
+ {"version":3,"names":["_react","require","_reactNative","_backgroundSession","useBackgroundSessionSync","enabled","oxyServices","userId","canUsePrivateApi","useEffect","isBackgroundSessionSupported","clearBackgroundSession","current","run","syncBackgroundSession","isCurrent","subscription","AppState","addEventListener","state","remove"],"sourceRoot":"../../../../src","sources":["ui/session/useBackgroundSessionSync.ts"],"mappings":";;;;;;AAQA,IAAAA,MAAA,GAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AAEA,IAAAE,kBAAA,GAAAF,OAAA;AAXA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAoBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASG,wBAAwBA,CAAC;EACvCC,OAAO;EACPC,WAAW;EACXC,MAAM;EACNC;AAC6B,CAAC,EAAQ;EACtC,IAAAC,gBAAS,EAAC,MAAM;IACd,IAAI,CAAC,IAAAC,+CAA4B,EAAC,CAAC,EAAE;MACnC;IACF;IAEA,IAAI,CAACL,OAAO,EAAE;MACZ;MACA;MACA;MACA;MACA,KAAK,IAAAM,yCAAsB,EAAC,CAAC;MAC7B;IACF;;IAEA;IACA;IACA,IAAIC,OAAO,GAAG,IAAI;IAClB;IACA;IACA;IACA;IACA;IACA,MAAMC,GAAG,GAAGA,CAAA,KAAM;MAChB,KAAK,IAAAC,wCAAqB,EAAC;QACzBR,WAAW;QACXC,MAAM;QACNC,gBAAgB;QAChBO,SAAS,EAAEA,CAAA,KAAMH;MACnB,CAAC,CAAC;IACJ,CAAC;IAEDC,GAAG,CAAC,CAAC;IACL,MAAMG,YAAY,GAAGC,qBAAQ,CAACC,gBAAgB,CAAC,QAAQ,EAAGC,KAAK,IAAK;MAClE,IAAIA,KAAK,KAAK,QAAQ,EAAE;QACtBN,GAAG,CAAC,CAAC;MACP;IACF,CAAC,CAAC;IAEF,OAAO,MAAM;MACXD,OAAO,GAAG,KAAK;MACfI,YAAY,CAACI,MAAM,CAAC,CAAC;MACrB;MACA;MACA;MACA;IACF,CAAC;EACH,CAAC,EAAE,CAACf,OAAO,EAAEC,WAAW,EAAEC,MAAM,EAAEC,gBAAgB,CAAC,CAAC;AACtD","ignoreList":[]}
@@ -27,12 +27,16 @@
27
27
  * live in different stores with one writer each, so there is nothing to keep in
28
28
  * lockstep and no state that can disagree.
29
29
  *
30
- * Android only. `requireOptionalNativeModule` resolves `null` on web and on any
31
- * app that does not ship the native module, and every function here degrades to a
32
- * no-op in that case so an app can enable this and lose nothing on other
33
- * platforms.
30
+ * ## Android only, and web is refused rather than merely unsupported
31
+ *
32
+ * Web is gated off by an explicit platform check, not by the native module being
33
+ * absent — provisioning in a browser origin would be a security downgrade, so it
34
+ * must not depend on an incidental fact. See {@link loadNativeModule}. iOS has no
35
+ * implementation yet and resolves to no module, so every function here degrades to
36
+ * a no-op: an app can enable this and lose nothing on other platforms.
34
37
  */
35
38
  import { requireOptionalNativeModule } from 'expo-modules-core';
39
+ import { Platform } from 'react-native';
36
40
  import { logger } from '@oxyhq/core';
37
41
 
38
42
  /**
@@ -52,7 +56,30 @@ const RENEW_WHEN_REMAINING_MS = 7 * 24 * 60 * 60 * 1000;
52
56
  let nativeModule;
53
57
 
54
58
  /**
55
- * Resolve the native module once.
59
+ * Resolve the native module once — and refuse outright on web.
60
+ *
61
+ * ## Web is excluded on purpose, and not merely because the module is absent
62
+ *
63
+ * The credential is deliberately NON-ROTATING and long-lived, which is safe only
64
+ * because native background code is its sole consumer and nobody rotates it. A
65
+ * browser origin already holds the ROTATING `deviceSecret`, and it has no
66
+ * background worker to serve — so provisioning there would add a strictly weaker
67
+ * long-lived secret alongside a stronger short-lived one. That is a downgrade, not
68
+ * a harmless no-op, and the harm lands at the PROVISION CALL (which mints a live
69
+ * server-side credential) regardless of whether anything manages to store it.
70
+ *
71
+ * That property must not rest on `requireOptionalNativeModule` happening to return
72
+ * `null` on web. It does today — the module is registered for android only — but
73
+ * that is an incidental fact a future web shim or a web-build native-module mock
74
+ * would quietly reverse. So the platform is checked FIRST, before we even ask the
75
+ * registry, and every entry point in this module inherits the gate.
76
+ *
77
+ * (A pre-deploy server makes this visible too: oxy-api's `sessionDevice` router
78
+ * applies `requireSameSiteOrigin` path-agnostically, so an unmatched path answers
79
+ * `404` to a native caller but `403 BAD_ORIGIN` to a browser one. Core's degrade is
80
+ * deliberately narrow — `404 → null` only — because a real origin misconfiguration
81
+ * on a CURRENT server also returns 403, and swallowing that would hide a genuine
82
+ * bug. Platform gating belongs here, where the platform is actually known.)
56
83
  *
57
84
  * `requireOptionalNativeModule` (a static import Metro always resolves) rather
58
85
  * than a dynamic `import(moduleName)`: the latter compiles to `require(variable)`
@@ -62,6 +89,10 @@ let nativeModule;
62
89
  */
63
90
  function loadNativeModule() {
64
91
  if (nativeModule === undefined) {
92
+ if (Platform.OS === 'web') {
93
+ nativeModule = null;
94
+ return nativeModule;
95
+ }
65
96
  const native = requireOptionalNativeModule('OxyBackgroundSession');
66
97
  nativeModule = native && typeof native.put === 'function' && typeof native.clear === 'function' && typeof native.peek === 'function' ? {
67
98
  put: native.put.bind(native),
@@ -1 +1 @@
1
- {"version":3,"names":["requireOptionalNativeModule","logger","RENEW_WHEN_REMAINING_MS","nativeModule","loadNativeModule","undefined","native","put","clear","peek","bind","isBackgroundSessionSupported","clearBackgroundSession","error","warn","component","assertProvisioningAvailable","oxyServices","provisionBackgroundCredential","message","Error","syncBackgroundSession","input","userId","canUsePrivateApi","isCurrent","stored","accountId","isForCurrentAccount","hasLifeLeft","expiresAt","Date","now","provisioned","parse","Number","isFinite","persisted","baseUrl","getBaseURL","deviceId","secret"],"sourceRoot":"../../../../src","sources":["ui/session/backgroundSession.ts"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["requireOptionalNativeModule","Platform","logger","RENEW_WHEN_REMAINING_MS","nativeModule","loadNativeModule","undefined","OS","native","put","clear","peek","bind","isBackgroundSessionSupported","clearBackgroundSession","error","warn","component","assertProvisioningAvailable","oxyServices","provisionBackgroundCredential","message","Error","syncBackgroundSession","input","userId","canUsePrivateApi","isCurrent","stored","accountId","isForCurrentAccount","hasLifeLeft","expiresAt","Date","now","provisioned","parse","Number","isFinite","persisted","baseUrl","getBaseURL","deviceId","secret"],"sourceRoot":"../../../../src","sources":["ui/session/backgroundSession.ts"],"mappings":"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","ignoreList":[]}
@@ -33,11 +33,10 @@ export function useBackgroundSessionSync({
33
33
  return;
34
34
  }
35
35
  if (!enabled) {
36
- // An app that turns the feature OFF must not leave a live credential at
37
- // rest for the rest of its 30-day lifetime. The cost is one local store
38
- // open per launch on Android apps that never use the feature, which is
39
- // accepted deliberately: correct revocation is worth more than skipping a
40
- // memoized keystore open.
36
+ // An app that leaves the feature off must not keep a credential THIS app
37
+ // provisioned earlier. The store is package-scoped (see
38
+ // OxyBackgroundSessionStore), so this cannot revoke a sibling Oxy app's
39
+ // credential under the shared UID.
41
40
  void clearBackgroundSession();
42
41
  return;
43
42
  }
@@ -1 +1 @@
1
- {"version":3,"names":["useEffect","AppState","clearBackgroundSession","isBackgroundSessionSupported","syncBackgroundSession","useBackgroundSessionSync","enabled","oxyServices","userId","canUsePrivateApi","current","run","isCurrent","subscription","addEventListener","state","remove"],"sourceRoot":"../../../../src","sources":["ui/session/useBackgroundSessionSync.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASA,SAAS,QAAQ,OAAO;AACjC,SAASC,QAAQ,QAAQ,cAAc;AAEvC,SACEC,sBAAsB,EACtBC,4BAA4B,EAC5BC,qBAAqB,QAChB,wBAAqB;AAY5B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,wBAAwBA,CAAC;EACvCC,OAAO;EACPC,WAAW;EACXC,MAAM;EACNC;AAC6B,CAAC,EAAQ;EACtCT,SAAS,CAAC,MAAM;IACd,IAAI,CAACG,4BAA4B,CAAC,CAAC,EAAE;MACnC;IACF;IAEA,IAAI,CAACG,OAAO,EAAE;MACZ;MACA;MACA;MACA;MACA;MACA,KAAKJ,sBAAsB,CAAC,CAAC;MAC7B;IACF;;IAEA;IACA;IACA,IAAIQ,OAAO,GAAG,IAAI;IAClB;IACA;IACA;IACA;IACA;IACA,MAAMC,GAAG,GAAGA,CAAA,KAAM;MAChB,KAAKP,qBAAqB,CAAC;QACzBG,WAAW;QACXC,MAAM;QACNC,gBAAgB;QAChBG,SAAS,EAAEA,CAAA,KAAMF;MACnB,CAAC,CAAC;IACJ,CAAC;IAEDC,GAAG,CAAC,CAAC;IACL,MAAME,YAAY,GAAGZ,QAAQ,CAACa,gBAAgB,CAAC,QAAQ,EAAGC,KAAK,IAAK;MAClE,IAAIA,KAAK,KAAK,QAAQ,EAAE;QACtBJ,GAAG,CAAC,CAAC;MACP;IACF,CAAC,CAAC;IAEF,OAAO,MAAM;MACXD,OAAO,GAAG,KAAK;MACfG,YAAY,CAACG,MAAM,CAAC,CAAC;MACrB;MACA;MACA;MACA;IACF,CAAC;EACH,CAAC,EAAE,CAACV,OAAO,EAAEC,WAAW,EAAEC,MAAM,EAAEC,gBAAgB,CAAC,CAAC;AACtD","ignoreList":[]}
1
+ {"version":3,"names":["useEffect","AppState","clearBackgroundSession","isBackgroundSessionSupported","syncBackgroundSession","useBackgroundSessionSync","enabled","oxyServices","userId","canUsePrivateApi","current","run","isCurrent","subscription","addEventListener","state","remove"],"sourceRoot":"../../../../src","sources":["ui/session/useBackgroundSessionSync.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASA,SAAS,QAAQ,OAAO;AACjC,SAASC,QAAQ,QAAQ,cAAc;AAEvC,SACEC,sBAAsB,EACtBC,4BAA4B,EAC5BC,qBAAqB,QAChB,wBAAqB;AAY5B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,wBAAwBA,CAAC;EACvCC,OAAO;EACPC,WAAW;EACXC,MAAM;EACNC;AAC6B,CAAC,EAAQ;EACtCT,SAAS,CAAC,MAAM;IACd,IAAI,CAACG,4BAA4B,CAAC,CAAC,EAAE;MACnC;IACF;IAEA,IAAI,CAACG,OAAO,EAAE;MACZ;MACA;MACA;MACA;MACA,KAAKJ,sBAAsB,CAAC,CAAC;MAC7B;IACF;;IAEA;IACA;IACA,IAAIQ,OAAO,GAAG,IAAI;IAClB;IACA;IACA;IACA;IACA;IACA,MAAMC,GAAG,GAAGA,CAAA,KAAM;MAChB,KAAKP,qBAAqB,CAAC;QACzBG,WAAW;QACXC,MAAM;QACNC,gBAAgB;QAChBG,SAAS,EAAEA,CAAA,KAAMF;MACnB,CAAC,CAAC;IACJ,CAAC;IAEDC,GAAG,CAAC,CAAC;IACL,MAAME,YAAY,GAAGZ,QAAQ,CAACa,gBAAgB,CAAC,QAAQ,EAAGC,KAAK,IAAK;MAClE,IAAIA,KAAK,KAAK,QAAQ,EAAE;QACtBJ,GAAG,CAAC,CAAC;MACP;IACF,CAAC,CAAC;IAEF,OAAO,MAAM;MACXD,OAAO,GAAG,KAAK;MACfG,YAAY,CAACG,MAAM,CAAC,CAAC;MACrB;MACA;MACA;MACA;IACF,CAAC;EACH,CAAC,EAAE,CAACV,OAAO,EAAEC,WAAW,EAAEC,MAAM,EAAEC,gBAAgB,CAAC,CAAC;AACtD","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"backgroundSession.d.ts","sourceRoot":"","sources":["../../../../../src/ui/session/backgroundSession.ts"],"names":[],"mappings":"AAiCA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAgE/C,yEAAyE;AACzE,wBAAgB,4BAA4B,IAAI,OAAO,CAEtD;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC,CAa5D;AAED,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,WAAW,CAAC;IACzB,wDAAwD;IACxD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,qEAAqE;IACrE,gBAAgB,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,SAAS,EAAE,MAAM,OAAO,CAAC;CAC1B;AA8BD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,0BAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CAqF5F"}
1
+ {"version":3,"file":"backgroundSession.d.ts","sourceRoot":"","sources":["../../../../../src/ui/session/backgroundSession.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AA2F/C,yEAAyE;AACzE,wBAAgB,4BAA4B,IAAI,OAAO,CAEtD;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC,CAa5D;AAED,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,WAAW,CAAC;IACzB,wDAAwD;IACxD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,qEAAqE;IACrE,gBAAgB,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,SAAS,EAAE,MAAM,OAAO,CAAC;CAC1B;AA8BD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,0BAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CAqF5F"}
@@ -1 +1 @@
1
- {"version":3,"file":"useBackgroundSessionSync.d.ts","sourceRoot":"","sources":["../../../../../src/ui/session/useBackgroundSessionSync.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAO/C,MAAM,WAAW,6BAA6B;IAC5C,4CAA4C;IAC5C,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,WAAW,CAAC;IACzB,wDAAwD;IACxD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,kEAAkE;IAClE,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,OAAO,EACP,WAAW,EACX,MAAM,EACN,gBAAgB,GACjB,EAAE,6BAA6B,GAAG,IAAI,CAiDtC"}
1
+ {"version":3,"file":"useBackgroundSessionSync.d.ts","sourceRoot":"","sources":["../../../../../src/ui/session/useBackgroundSessionSync.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAO/C,MAAM,WAAW,6BAA6B;IAC5C,4CAA4C;IAC5C,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,WAAW,CAAC;IACzB,wDAAwD;IACxD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,kEAAkE;IAClE,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,OAAO,EACP,WAAW,EACX,MAAM,EACN,gBAAgB,GACjB,EAAE,6BAA6B,GAAG,IAAI,CAgDtC"}
@@ -1 +1 @@
1
- {"version":3,"file":"backgroundSession.d.ts","sourceRoot":"","sources":["../../../../../src/ui/session/backgroundSession.ts"],"names":[],"mappings":"AAiCA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAgE/C,yEAAyE;AACzE,wBAAgB,4BAA4B,IAAI,OAAO,CAEtD;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC,CAa5D;AAED,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,WAAW,CAAC;IACzB,wDAAwD;IACxD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,qEAAqE;IACrE,gBAAgB,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,SAAS,EAAE,MAAM,OAAO,CAAC;CAC1B;AA8BD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,0BAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CAqF5F"}
1
+ {"version":3,"file":"backgroundSession.d.ts","sourceRoot":"","sources":["../../../../../src/ui/session/backgroundSession.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AA2F/C,yEAAyE;AACzE,wBAAgB,4BAA4B,IAAI,OAAO,CAEtD;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC,CAa5D;AAED,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,WAAW,CAAC;IACzB,wDAAwD;IACxD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,qEAAqE;IACrE,gBAAgB,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,SAAS,EAAE,MAAM,OAAO,CAAC;CAC1B;AA8BD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,0BAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CAqF5F"}
@@ -1 +1 @@
1
- {"version":3,"file":"useBackgroundSessionSync.d.ts","sourceRoot":"","sources":["../../../../../src/ui/session/useBackgroundSessionSync.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAO/C,MAAM,WAAW,6BAA6B;IAC5C,4CAA4C;IAC5C,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,WAAW,CAAC;IACzB,wDAAwD;IACxD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,kEAAkE;IAClE,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,OAAO,EACP,WAAW,EACX,MAAM,EACN,gBAAgB,GACjB,EAAE,6BAA6B,GAAG,IAAI,CAiDtC"}
1
+ {"version":3,"file":"useBackgroundSessionSync.d.ts","sourceRoot":"","sources":["../../../../../src/ui/session/useBackgroundSessionSync.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAO/C,MAAM,WAAW,6BAA6B;IAC5C,4CAA4C;IAC5C,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,WAAW,CAAC;IACzB,wDAAwD;IACxD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,kEAAkE;IAClE,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,OAAO,EACP,WAAW,EACX,MAAM,EACN,gBAAgB,GACjB,EAAE,6BAA6B,GAAG,IAAI,CAgDtC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/services",
3
- "version": "24.0.0",
3
+ "version": "24.0.1",
4
4
  "description": "OxyHQ Expo/React Native SDK — UI components, screens, and native features",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -56,12 +56,20 @@ function fakeOxy(
56
56
  * Re-import the module per test: it memoises the resolved native module, which is
57
57
  * correct in production (one resolution per process) and must not leak between
58
58
  * cases here.
59
+ *
60
+ * `platform` is explicit because the react-native stub defaults `Platform.OS` to
61
+ * `'web'`, where this feature is gated OFF by design. Every case therefore has to
62
+ * say which platform it is describing — defaulting to `'android'` here, since that
63
+ * is the only platform the feature ships on.
59
64
  */
60
- async function loadModule(native: NativeCalls | null) {
65
+ async function loadModule(native: NativeCalls | null, platform: 'android' | 'web' = 'android') {
61
66
  jest.resetModules();
67
+ requireOptionalNativeModule.mockClear();
62
68
  requireOptionalNativeModule.mockReturnValue(
63
69
  native ? { put: native.put, clear: native.clear, peek: native.peek } : null,
64
70
  );
71
+ const { Platform } = await import('react-native');
72
+ Platform.OS = platform;
65
73
  return import('../backgroundSession');
66
74
  }
67
75
 
@@ -316,6 +324,48 @@ describe('a @oxyhq/core too old to provision', () => {
316
324
  });
317
325
  });
318
326
 
327
+ describe('on web', () => {
328
+ /**
329
+ * Web must never provision. The credential is non-rotating and long-lived, which
330
+ * is only safe because native background code is its sole consumer; a browser
331
+ * origin already holds the ROTATING deviceSecret and has no background worker, so
332
+ * a successful provision there is a security DOWNGRADE. The harm lands at the
333
+ * network call, which mints a live server-side credential, so "storage failed
334
+ * anyway" is not a defence.
335
+ *
336
+ * These tests present a native module that IS available, which is the whole
337
+ * point: they fail if the gate ever relies on `requireOptionalNativeModule`
338
+ * returning null on web rather than on the platform check.
339
+ */
340
+ test('does not provision even when a native module IS present', async () => {
341
+ const native = fakeNative(null);
342
+ const { syncBackgroundSession, isBackgroundSessionSupported } = await loadModule(native, 'web');
343
+ const provision = jest.fn(async () => provisioned());
344
+
345
+ expect(isBackgroundSessionSupported()).toBe(false);
346
+ await syncBackgroundSession({
347
+ oxyServices: fakeOxy(provision),
348
+ userId: 'user-1',
349
+ canUsePrivateApi: true,
350
+ isCurrent: () => true,
351
+ });
352
+
353
+ expect(provision).not.toHaveBeenCalled();
354
+ expect(native.put).not.toHaveBeenCalled();
355
+ // Not even a read, so nothing in a browser origin is touched at all.
356
+ expect(native.peek).not.toHaveBeenCalled();
357
+ expect(native.clear).not.toHaveBeenCalled();
358
+ });
359
+
360
+ test('never asks the native registry on web', async () => {
361
+ const { isBackgroundSessionSupported } = await loadModule(fakeNative(null), 'web');
362
+ expect(isBackgroundSessionSupported()).toBe(false);
363
+ // The platform decides BEFORE the registry is consulted, so a web-build
364
+ // native-module mock or a future web shim cannot reopen the gate.
365
+ expect(requireOptionalNativeModule).not.toHaveBeenCalled();
366
+ });
367
+ });
368
+
319
369
  describe('without the native module', () => {
320
370
  test('every entry point is an inert no-op', async () => {
321
371
  const { syncBackgroundSession, clearBackgroundSession, isBackgroundSessionSupported } =
@@ -25,12 +25,16 @@
25
25
  * live in different stores with one writer each, so there is nothing to keep in
26
26
  * lockstep and no state that can disagree.
27
27
  *
28
- * Android only. `requireOptionalNativeModule` resolves `null` on web and on any
29
- * app that does not ship the native module, and every function here degrades to a
30
- * no-op in that case so an app can enable this and lose nothing on other
31
- * platforms.
28
+ * ## Android only, and web is refused rather than merely unsupported
29
+ *
30
+ * Web is gated off by an explicit platform check, not by the native module being
31
+ * absent — provisioning in a browser origin would be a security downgrade, so it
32
+ * must not depend on an incidental fact. See {@link loadNativeModule}. iOS has no
33
+ * implementation yet and resolves to no module, so every function here degrades to
34
+ * a no-op: an app can enable this and lose nothing on other platforms.
32
35
  */
33
36
  import { requireOptionalNativeModule } from 'expo-modules-core';
37
+ import { Platform } from 'react-native';
34
38
  import type { OxyServices } from '@oxyhq/core';
35
39
  import { logger } from '@oxyhq/core';
36
40
 
@@ -67,7 +71,30 @@ const RENEW_WHEN_REMAINING_MS = 7 * 24 * 60 * 60 * 1000;
67
71
  let nativeModule: OxyBackgroundSessionNativeModule | null | undefined;
68
72
 
69
73
  /**
70
- * Resolve the native module once.
74
+ * Resolve the native module once — and refuse outright on web.
75
+ *
76
+ * ## Web is excluded on purpose, and not merely because the module is absent
77
+ *
78
+ * The credential is deliberately NON-ROTATING and long-lived, which is safe only
79
+ * because native background code is its sole consumer and nobody rotates it. A
80
+ * browser origin already holds the ROTATING `deviceSecret`, and it has no
81
+ * background worker to serve — so provisioning there would add a strictly weaker
82
+ * long-lived secret alongside a stronger short-lived one. That is a downgrade, not
83
+ * a harmless no-op, and the harm lands at the PROVISION CALL (which mints a live
84
+ * server-side credential) regardless of whether anything manages to store it.
85
+ *
86
+ * That property must not rest on `requireOptionalNativeModule` happening to return
87
+ * `null` on web. It does today — the module is registered for android only — but
88
+ * that is an incidental fact a future web shim or a web-build native-module mock
89
+ * would quietly reverse. So the platform is checked FIRST, before we even ask the
90
+ * registry, and every entry point in this module inherits the gate.
91
+ *
92
+ * (A pre-deploy server makes this visible too: oxy-api's `sessionDevice` router
93
+ * applies `requireSameSiteOrigin` path-agnostically, so an unmatched path answers
94
+ * `404` to a native caller but `403 BAD_ORIGIN` to a browser one. Core's degrade is
95
+ * deliberately narrow — `404 → null` only — because a real origin misconfiguration
96
+ * on a CURRENT server also returns 403, and swallowing that would hide a genuine
97
+ * bug. Platform gating belongs here, where the platform is actually known.)
71
98
  *
72
99
  * `requireOptionalNativeModule` (a static import Metro always resolves) rather
73
100
  * than a dynamic `import(moduleName)`: the latter compiles to `require(variable)`
@@ -77,6 +104,10 @@ let nativeModule: OxyBackgroundSessionNativeModule | null | undefined;
77
104
  */
78
105
  function loadNativeModule(): OxyBackgroundSessionNativeModule | null {
79
106
  if (nativeModule === undefined) {
107
+ if (Platform.OS === 'web') {
108
+ nativeModule = null;
109
+ return nativeModule;
110
+ }
80
111
  const native = requireOptionalNativeModule<Partial<OxyBackgroundSessionNativeModule>>(
81
112
  'OxyBackgroundSession',
82
113
  );
@@ -48,11 +48,10 @@ export function useBackgroundSessionSync({
48
48
  }
49
49
 
50
50
  if (!enabled) {
51
- // An app that turns the feature OFF must not leave a live credential at
52
- // rest for the rest of its 30-day lifetime. The cost is one local store
53
- // open per launch on Android apps that never use the feature, which is
54
- // accepted deliberately: correct revocation is worth more than skipping a
55
- // memoized keystore open.
51
+ // An app that leaves the feature off must not keep a credential THIS app
52
+ // provisioned earlier. The store is package-scoped (see
53
+ // OxyBackgroundSessionStore), so this cannot revoke a sibling Oxy app's
54
+ // credential under the shared UID.
56
55
  void clearBackgroundSession();
57
56
  return;
58
57
  }