@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.
- package/android/src/main/java/so/oxy/session/OxyBackgroundSessionStore.kt +11 -2
- package/lib/commonjs/ui/session/backgroundSession.js +36 -5
- package/lib/commonjs/ui/session/backgroundSession.js.map +1 -1
- package/lib/commonjs/ui/session/useBackgroundSessionSync.js +4 -5
- package/lib/commonjs/ui/session/useBackgroundSessionSync.js.map +1 -1
- package/lib/module/ui/session/backgroundSession.js +36 -5
- package/lib/module/ui/session/backgroundSession.js.map +1 -1
- package/lib/module/ui/session/useBackgroundSessionSync.js +4 -5
- package/lib/module/ui/session/useBackgroundSessionSync.js.map +1 -1
- package/lib/typescript/commonjs/ui/session/backgroundSession.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/useBackgroundSessionSync.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/backgroundSession.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/useBackgroundSessionSync.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ui/session/__tests__/backgroundSession.test.ts +51 -1
- package/src/ui/session/backgroundSession.ts +36 -5
- package/src/ui/session/useBackgroundSessionSync.ts +4 -5
|
@@ -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
|
-
|
|
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,
|
|
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
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
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":";;;;;;;;
|
|
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
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
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
|
|
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
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
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":";;AAAA;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,SAASA,2BAA2B,QAAQ,mBAAmB;
|
|
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
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
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
|
|
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":"
|
|
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,
|
|
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":"
|
|
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,
|
|
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
|
@@ -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
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
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
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
//
|
|
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
|
}
|