@oxy.so/services 11.0.0 → 11.2.0-oxy-one.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.
- package/lib/commonjs/index.js +13 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/boot/runProviderColdBoot.js +14 -1
- package/lib/commonjs/ui/boot/runProviderColdBoot.js.map +1 -1
- package/lib/commonjs/ui/components/OxySignInButton.js +25 -7
- package/lib/commonjs/ui/components/OxySignInButton.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +198 -41
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/context/commitSessionFlow.js +5 -0
- package/lib/commonjs/ui/context/commitSessionFlow.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js +56 -9
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/commonjs/ui/context/isolatedOAuthSession.js +52 -0
- package/lib/commonjs/ui/context/isolatedOAuthSession.js.map +1 -0
- package/lib/commonjs/ui/context/useOxyAccountGraph.js +7 -5
- package/lib/commonjs/ui/context/useOxyAccountGraph.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/usePersonalPlans.js +43 -0
- package/lib/commonjs/ui/hooks/queries/usePersonalPlans.js.map +1 -0
- package/lib/commonjs/ui/hooks/useAuth.js +7 -3
- package/lib/commonjs/ui/hooks/useAuth.js.map +1 -1
- package/lib/commonjs/ui/oauth/applicationSessionLane.js +32 -0
- package/lib/commonjs/ui/oauth/applicationSessionLane.js.map +1 -0
- package/lib/commonjs/ui/oauth/completeOAuthCode.js +4 -2
- package/lib/commonjs/ui/oauth/completeOAuthCode.js.map +1 -1
- package/lib/commonjs/ui/oauth/nativeAuthTransport.js +119 -0
- package/lib/commonjs/ui/oauth/nativeAuthTransport.js.map +1 -0
- package/lib/commonjs/ui/session/createSessionClient.js +14 -2
- package/lib/commonjs/ui/session/createSessionClient.js.map +1 -1
- package/lib/commonjs/ui/session/deviceCredentialTracker.js +8 -0
- package/lib/commonjs/ui/session/deviceCredentialTracker.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/boot/runProviderColdBoot.js +14 -1
- package/lib/module/ui/boot/runProviderColdBoot.js.map +1 -1
- package/lib/module/ui/components/OxySignInButton.js +25 -7
- package/lib/module/ui/components/OxySignInButton.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +200 -43
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/commitSessionFlow.js +5 -0
- package/lib/module/ui/context/commitSessionFlow.js.map +1 -1
- package/lib/module/ui/context/hooks/useAuthOperations.js +56 -9
- package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/module/ui/context/isolatedOAuthSession.js +44 -0
- package/lib/module/ui/context/isolatedOAuthSession.js.map +1 -0
- package/lib/module/ui/context/useOxyAccountGraph.js +7 -5
- package/lib/module/ui/context/useOxyAccountGraph.js.map +1 -1
- package/lib/module/ui/hooks/queries/usePersonalPlans.js +39 -0
- package/lib/module/ui/hooks/queries/usePersonalPlans.js.map +1 -0
- package/lib/module/ui/hooks/useAuth.js +7 -3
- package/lib/module/ui/hooks/useAuth.js.map +1 -1
- package/lib/module/ui/oauth/applicationSessionLane.js +26 -0
- package/lib/module/ui/oauth/applicationSessionLane.js.map +1 -0
- package/lib/module/ui/oauth/completeOAuthCode.js +4 -2
- package/lib/module/ui/oauth/completeOAuthCode.js.map +1 -1
- package/lib/module/ui/oauth/nativeAuthTransport.js +115 -0
- package/lib/module/ui/oauth/nativeAuthTransport.js.map +1 -0
- package/lib/module/ui/session/createSessionClient.js +14 -2
- package/lib/module/ui/session/createSessionClient.js.map +1 -1
- package/lib/module/ui/session/deviceCredentialTracker.js +8 -0
- package/lib/module/ui/session/deviceCredentialTracker.js.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +2 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts +7 -0
- package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxySignInButton.d.ts +2 -0
- package/lib/typescript/commonjs/ui/components/OxySignInButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +3 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/commitSessionFlow.d.ts +2 -0
- package/lib/typescript/commonjs/ui/context/commitSessionFlow.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts +3 -2
- package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/isolatedOAuthSession.d.ts +19 -0
- package/lib/typescript/commonjs/ui/context/isolatedOAuthSession.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts +3 -0
- package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts +2 -1
- package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +6 -0
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/usePersonalPlans.d.ts +64 -0
- package/lib/typescript/commonjs/ui/hooks/queries/usePersonalPlans.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/oauth/applicationSessionLane.d.ts +6 -0
- package/lib/typescript/commonjs/ui/oauth/applicationSessionLane.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/oauth/completeOAuthCode.d.ts +2 -2
- package/lib/typescript/commonjs/ui/oauth/nativeAuthTransport.d.ts +28 -0
- package/lib/typescript/commonjs/ui/oauth/nativeAuthTransport.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts +1 -1
- package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/deviceCredentialTracker.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +2 -0
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts +7 -0
- package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxySignInButton.d.ts +2 -0
- package/lib/typescript/module/ui/components/OxySignInButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts +3 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/commitSessionFlow.d.ts +2 -0
- package/lib/typescript/module/ui/context/commitSessionFlow.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts +3 -2
- package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/isolatedOAuthSession.d.ts +19 -0
- package/lib/typescript/module/ui/context/isolatedOAuthSession.d.ts.map +1 -0
- package/lib/typescript/module/ui/context/oxyContextTypes.d.ts +3 -0
- package/lib/typescript/module/ui/context/oxyContextTypes.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts +2 -1
- package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +6 -0
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queries/usePersonalPlans.d.ts +64 -0
- package/lib/typescript/module/ui/hooks/queries/usePersonalPlans.d.ts.map +1 -0
- package/lib/typescript/module/ui/hooks/useAuth.d.ts.map +1 -1
- package/lib/typescript/module/ui/oauth/applicationSessionLane.d.ts +6 -0
- package/lib/typescript/module/ui/oauth/applicationSessionLane.d.ts.map +1 -0
- package/lib/typescript/module/ui/oauth/completeOAuthCode.d.ts +2 -2
- package/lib/typescript/module/ui/oauth/nativeAuthTransport.d.ts +28 -0
- package/lib/typescript/module/ui/oauth/nativeAuthTransport.d.ts.map +1 -0
- package/lib/typescript/module/ui/session/createSessionClient.d.ts +1 -1
- package/lib/typescript/module/ui/session/createSessionClient.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/deviceCredentialTracker.d.ts.map +1 -1
- package/package.json +30 -30
- package/src/index.ts +4 -0
- package/src/ui/boot/__tests__/coldBootNoIdpRedirect.test.ts +56 -2
- package/src/ui/boot/runProviderColdBoot.ts +22 -2
- package/src/ui/components/OxySignInButton.tsx +20 -4
- package/src/ui/context/OxyContext.tsx +171 -40
- package/src/ui/context/__tests__/commitSessionFlow.test.ts +13 -0
- package/src/ui/context/__tests__/isolatedOAuthSession.test.ts +35 -0
- package/src/ui/context/commitSessionFlow.ts +8 -0
- package/src/ui/context/hooks/useAuthOperations.ts +55 -9
- package/src/ui/context/isolatedOAuthSession.ts +48 -0
- package/src/ui/context/oxyContextTypes.ts +4 -0
- package/src/ui/context/useOxyAccountGraph.ts +8 -5
- package/src/ui/hooks/queries/usePersonalPlans.ts +23 -0
- package/src/ui/hooks/useAuth.ts +6 -2
- package/src/ui/oauth/__tests__/completeOAuthCode.test.ts +1 -1
- package/src/ui/oauth/__tests__/nativeAuthTransport.test.ts +57 -0
- package/src/ui/oauth/__tests__/oauthBearerCommit.http.test.ts +131 -0
- package/src/ui/oauth/applicationSessionLane.ts +26 -0
- package/src/ui/oauth/completeOAuthCode.ts +3 -3
- package/src/ui/oauth/nativeAuthTransport.ts +67 -0
- package/src/ui/session/createSessionClient.ts +13 -1
- package/src/ui/session/deviceCredentialTracker.ts +7 -1
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { OxyServices } from '@oxy.so/core';
|
|
2
|
+
import type { OAuthSessionCommitInput } from './types.js';
|
|
3
|
+
export interface StartNativeOAuthSignInOptions {
|
|
4
|
+
/** Exact registered URI, including any fixed query. Never inferred. */
|
|
5
|
+
redirectUri: string;
|
|
6
|
+
}
|
|
7
|
+
export type NativeOAuthSignInResult = {
|
|
8
|
+
status: 'signed-in';
|
|
9
|
+
} | {
|
|
10
|
+
status: 'cancelled';
|
|
11
|
+
} | {
|
|
12
|
+
status: 'unsupported';
|
|
13
|
+
reason: 'unsupported-platform' | 'missing-client-id' | 'identity-bound' | 'not-third-party';
|
|
14
|
+
} | {
|
|
15
|
+
status: 'failed';
|
|
16
|
+
reason: 'invalid-redirect-uri' | 'client-resolution-failed' | 'native-callback-invalid' | 'state-mismatch' | 'exchange-failed' | 'already-in-progress';
|
|
17
|
+
};
|
|
18
|
+
export interface NativeOAuthSignInContext {
|
|
19
|
+
platform: 'native' | 'unsupported';
|
|
20
|
+
oxyServices: OxyServices;
|
|
21
|
+
clientId: string | null;
|
|
22
|
+
authorizeBaseUrl?: string;
|
|
23
|
+
identityBound: boolean;
|
|
24
|
+
commitSession: (input: OAuthSessionCommitInput) => Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
/** SDK owns the in-memory handshake, observed native callback and session commit. */
|
|
27
|
+
export declare function startNativeOAuthSignIn(context: NativeOAuthSignInContext, options: StartNativeOAuthSignInOptions): Promise<NativeOAuthSignInResult>;
|
|
28
|
+
//# sourceMappingURL=nativeAuthTransport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nativeAuthTransport.d.ts","sourceRoot":"","sources":["../../../../../src/ui/oauth/nativeAuthTransport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAKhD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAS,CAAC;AAEvD,MAAM,WAAW,6BAA6B;IAC5C,uEAAuE;IACvE,WAAW,EAAE,MAAM,CAAC;CACrB;AACD,MAAM,MAAM,uBAAuB,GAC/B;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,GACvB;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,GACvB;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,sBAAsB,GAAG,mBAAmB,GAAG,gBAAgB,GAAG,iBAAiB,CAAA;CAAE,GACtH;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,sBAAsB,GAAG,0BAA0B,GAAG,yBAAyB,GAAG,gBAAgB,GAAG,iBAAiB,GAAG,qBAAqB,CAAA;CAAE,CAAC;AACjL,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,QAAQ,GAAG,aAAa,CAAC;IACnC,WAAW,EAAE,WAAW,CAAC;IACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAClE;AAYD,qFAAqF;AACrF,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,wBAAwB,EACjC,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,uBAAuB,CAAC,CA2BlC"}
|
|
@@ -34,7 +34,7 @@ import type { OxyServices } from '@oxy.so/core';
|
|
|
34
34
|
* account, the client tracks device state truthfully but never re-binds its
|
|
35
35
|
* bearer to the device's mutable active account.
|
|
36
36
|
*/
|
|
37
|
-
export declare function createSessionClient(oxyServices: OxyServices, onUnauthenticated?: (origin: SessionStateOrigin) => void, getPinnedAccountId?: () => string | null): {
|
|
37
|
+
export declare function createSessionClient(oxyServices: OxyServices, onUnauthenticated?: (origin: SessionStateOrigin) => void, getPinnedAccountId?: () => string | null, isDeviceSessionAllowed?: () => boolean, onFullExplicitSignOut?: () => Promise<void>): {
|
|
38
38
|
client: SessionClient;
|
|
39
39
|
host: ReturnType<typeof createSessionClientHost>;
|
|
40
40
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createSessionClient.d.ts","sourceRoot":"","sources":["../../../../../src/ui/session/createSessionClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,KAAK,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACvG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,WAAW,EACxB,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,EACxD,kBAAkB,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"createSessionClient.d.ts","sourceRoot":"","sources":["../../../../../src/ui/session/createSessionClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,KAAK,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACvG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,WAAW,EACxB,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,EACxD,kBAAkB,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,EACxC,sBAAsB,GAAE,MAAM,OAAoB,EAClD,qBAAqB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAC1C;IACD,MAAM,EAAE,aAAa,CAAC;IACtB,IAAI,EAAE,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAC;CAClD,CAmBA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deviceCredentialTracker.d.ts","sourceRoot":"","sources":["../../../../../src/ui/session/deviceCredentialTracker.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"deviceCredentialTracker.d.ts","sourceRoot":"","sources":["../../../../../src/ui/session/deviceCredentialTracker.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,cAAc,EAA2C,MAAM,sBAAsB,CAAC;AAEpG,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gCAAiC,SAAQ,cAAc;IACtE;;;OAGG;IACH,oBAAoB,IAAI,oBAAoB,GAAG,IAAI,GAAG,SAAS,CAAC;CACjE;AAQD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,cAAc,GAAG,gCAAgC,CAwB7F"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxy.so/services",
|
|
3
|
-
"version": "11.0.0",
|
|
3
|
+
"version": "11.2.0-oxy-one.0",
|
|
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",
|
|
@@ -155,7 +155,7 @@
|
|
|
155
155
|
},
|
|
156
156
|
"dependencies": {
|
|
157
157
|
"@expo/config-plugins": "57.0.9",
|
|
158
|
-
"@oxy.so/contracts": "
|
|
158
|
+
"@oxy.so/contracts": "4.11.0-oxy-one.0",
|
|
159
159
|
"color": "^4.2.3"
|
|
160
160
|
},
|
|
161
161
|
"devDependencies": {
|
|
@@ -163,7 +163,7 @@
|
|
|
163
163
|
"@commitlint/cli": "^21.2.1",
|
|
164
164
|
"@commitlint/config-conventional": "^21.2.0",
|
|
165
165
|
"@expo/vector-icons": "^15.0.3",
|
|
166
|
-
"@oxy.so/core": "4.0.0",
|
|
166
|
+
"@oxy.so/core": "4.5.0-oxy-one.0",
|
|
167
167
|
"@react-native-async-storage/async-storage": "^2.0.0",
|
|
168
168
|
"@react-native-community/netinfo": "^12.0.1",
|
|
169
169
|
"@react-native/babel-preset": "^0.86.2",
|
|
@@ -178,7 +178,7 @@
|
|
|
178
178
|
"@types/jest": "^30.0.0",
|
|
179
179
|
"@types/node": "^20.19.43",
|
|
180
180
|
"@types/react": "*",
|
|
181
|
-
"@oxy.so/bloom": "
|
|
181
|
+
"@oxy.so/bloom": "6.2.1",
|
|
182
182
|
"copyfiles": "^2.4.1",
|
|
183
183
|
"expo-clipboard": "~57.0.2",
|
|
184
184
|
"expo-constants": "~57.0.5",
|
|
@@ -212,42 +212,42 @@
|
|
|
212
212
|
"typescript": "^5.9.2",
|
|
213
213
|
"zustand": "^5.0.14"
|
|
214
214
|
},
|
|
215
|
-
"//@oxy.so/bloom": "RE-RAISE THIS FLOOR ON EVERY BREAKING BLOOM RELEASE, in the same commit as the root package.json catalog entry and the code that requires it. The breaking position is the MAJOR, so `^3.0.1` means `>=3.0.1 <4.0.0`: 3.x minors are admitted on semver's additive promise, and Bloom 4.0.0 is refused until someone raises this deliberately. The CAP is the part that is load-bearing. An earlier revision of this key recorded the opposite trade -- an uncapped `>=` floor, accepted so that intentionally-breaking Bloom 0.x minors would not be blocked -- and that trade is closed by evidence rather than by preference: an unbounded range is exactly what let `bun install` pair this package with Bloom 1.0.0 and emit no warning at all, and 1.0.0 broke it four ways (a deleted provider, a renamed confirm option, a renamed Chip fill, and two REMOVED SUBPATHS that fail at bundle time, before any typecheck runs). A floor is a claim about what was measured; a cap is the claim that nothing beyond it was. RAISED TO 3.0.1 from `^2.1.0`. Bloom 3.0.0 breaks exactly two things -- `PageHeader` defaults to `presentation=\"floating\"` and stops hiding the title under `transparent`, and `Field disabled` now reaches the control that reads the field context -- and THIS PACKAGE IMPORTS NEITHER: it renders no `PageHeader` and no `Field`, and 3.0 removes no subpath (unlike 2.0.0, which is why that floor cost code changes and this one does not). Its six `transparent` occurrences are CSS colour values, not the PageHeader prop. The cap had to move rather than widen to a union, because leaving `^2.1.0` in place is what made `bun install` resolve a SECOND, nested Bloom 2.12.4 under this package for any app on Bloom 3 -- two copies of a library whose 3.0 contracts (`control-surface`, `TopEdgeProvider`, `ScrollOffsetProvider`) are React contexts, so a provider from one copy cannot be read by a consumer from the other. Mention's `oxyRuntimeCompatibility` test is what caught it; nothing in a typecheck can. RAISED AGAIN TO 3.2.0 from `^3.0.1`, for the same class of reason the 2.x note records, only in the other direction: 3.2.0 ADDS the per-glyph icon subpaths (`@oxy.so/bloom/icons/RiAtLine`) that this package's screens now import instead of the `@oxy.so/bloom/icons` barrel, and a missing subpath fails at bundle time, in the consumer's Metro, long after this package has typechecked and shipped. `^3.0.1` would have admitted Bloom 3.0.1 and 3.1.0, neither of which has those subpaths, so the floor is the only thing standing between a consumer and a red build. Nothing else about 3.1.0 or 3.2.0 is required here; the cap stays at the major. RAISED TO ^4.2.0: FollowButton now delegates its visuals to Bloom media-header FollowButton with loading, textStyle and large size; the new shared UI contracts require one Bloom4 context across Services and its consumer. RAISED TO ^4.31.0: the sign-in and account screens type one-time codes into `InputOtp` with `type=\"alphanumeric\"` (the long sign-in code and backup codes), which 4.31.0 adds; older 4.x drops the letters. RAISED TO ^4.35.0: every password field passes `revealable` to `TextFieldInput` (the eye button, 4.35.0); older 4.x drops the prop silently and the password cannot be shown. The provider also mounts `@oxy.so/bloom/locale` (4.32.0+). RAISED TO ^5.1.0 (services 9.0.0, a major because consumers must move to Bloom 5 with it): Bloom 5.0.0 types `Card`'s `style` without radius, border width or `overflow` (geometry goes through `radius`/`border`/`clipContent`) and stops clipping a Card's children by default, so `BenefitList`'s `overflow: 'visible'` became both a type error and unnecessary; it is removed. The cap MOVES rather than widening to a `^4 || ^5` union, for the reason the 3.0.1 note records: a union lets an app resolve a second, nested Bloom copy and split the React contexts. The floor is 5.1.0 because that is the version measured here (every package builds and tests against it); 5.0.0 was not measured. RAISED TO ^6.0.0 (services 10.0.0, a major because consumers must move to Bloom 6 with it, and because this package's own `FollowButton`/`FollowTargetButton` `size` moves with Bloom's vocabulary): Bloom 6.0.0 removes the duplicate spellings its 5.x types still admitted, and this package used four of them. The control `size` narrows to `xs|sm|md|lg`, so every `small`/`medium`/`large` handed to a Bloom control became `sm`/`md`/`lg`, and the Follow buttons' public `size` became `sm|md|lg` because it is passed straight through to Bloom's media-header `FollowButton`. `Switch` is controlled by `checked`/`onCheckedChange`, not `value`/`onValueChange`. `Button` has no `variant`: each call site names `appearance` and `tone` (the pair the 5.x recipe resolved the variant to, so nothing repaints), `LinkButton`'s colour is `linkTone`, and `PeableButton` renders `InverseButton`. `Card` takes `appearance=\"outline\"` for `variant=\"outlined\"`. The deprecated spellings Bloom 6 still accepts (`isInvalid`, Chip `color`/`variant`) are not breaks and are left alone. The cap MOVES rather than widening to a `^5 || ^6` union, for the reason the 3.0.1 note records: a union lets an app resolve a second, nested Bloom copy and split the React contexts. The floor is 6.0.0 because that is the version measured when this raise was made; the workspace catalog has since moved to ^6.2.0, which adds only the `cover-header` subpath this package does not import, so the floor stays.",
|
|
215
|
+
"//@oxy.so/bloom": "RE-RAISE THIS FLOOR ON EVERY BREAKING BLOOM RELEASE, in the same commit as the root package.json catalog entry and the code that requires it. The breaking position is the MAJOR, so `^3.0.1` means `>=3.0.1 <4.0.0`: 3.x minors are admitted on semver's additive promise, and Bloom 4.0.0 is refused until someone raises this deliberately. The CAP is the part that is load-bearing. An earlier revision of this key recorded the opposite trade -- an uncapped `>=` floor, accepted so that intentionally-breaking Bloom 0.x minors would not be blocked -- and that trade is closed by evidence rather than by preference: an unbounded range is exactly what let `bun install` pair this package with Bloom 1.0.0 and emit no warning at all, and 1.0.0 broke it four ways (a deleted provider, a renamed confirm option, a renamed Chip fill, and two REMOVED SUBPATHS that fail at bundle time, before any typecheck runs). A floor is a claim about what was measured; a cap is the claim that nothing beyond it was. RAISED TO 3.0.1 from `^2.1.0`. Bloom 3.0.0 breaks exactly two things -- `PageHeader` defaults to `presentation=\"floating\"` and stops hiding the title under `transparent`, and `Field disabled` now reaches the control that reads the field context -- and THIS PACKAGE IMPORTS NEITHER: it renders no `PageHeader` and no `Field`, and 3.0 removes no subpath (unlike 2.0.0, which is why that floor cost code changes and this one does not). Its six `transparent` occurrences are CSS colour values, not the PageHeader prop. The cap had to move rather than widen to a union, because leaving `^2.1.0` in place is what made `bun install` resolve a SECOND, nested Bloom 2.12.4 under this package for any app on Bloom 3 -- two copies of a library whose 3.0 contracts (`control-surface`, `TopEdgeProvider`, `ScrollOffsetProvider`) are React contexts, so a provider from one copy cannot be read by a consumer from the other. Mention's `oxyRuntimeCompatibility` test is what caught it; nothing in a typecheck can. RAISED AGAIN TO 3.2.0 from `^3.0.1`, for the same class of reason the 2.x note records, only in the other direction: 3.2.0 ADDS the per-glyph icon subpaths (`@oxy.so/bloom/icons/RiAtLine`) that this package's screens now import instead of the `@oxy.so/bloom/icons` barrel, and a missing subpath fails at bundle time, in the consumer's Metro, long after this package has typechecked and shipped. `^3.0.1` would have admitted Bloom 3.0.1 and 3.1.0, neither of which has those subpaths, so the floor is the only thing standing between a consumer and a red build. Nothing else about 3.1.0 or 3.2.0 is required here; the cap stays at the major. RAISED TO ^4.2.0: FollowButton now delegates its visuals to Bloom media-header FollowButton with loading, textStyle and large size; the new shared UI contracts require one Bloom4 context across Services and its consumer. RAISED TO ^4.31.0: the sign-in and account screens type one-time codes into `InputOtp` with `type=\"alphanumeric\"` (the long sign-in code and backup codes), which 4.31.0 adds; older 4.x drops the letters. RAISED TO ^4.35.0: every password field passes `revealable` to `TextFieldInput` (the eye button, 4.35.0); older 4.x drops the prop silently and the password cannot be shown. The provider also mounts `@oxy.so/bloom/locale` (4.32.0+). RAISED TO ^5.1.0 (services 9.0.0, a major because consumers must move to Bloom 5 with it): Bloom 5.0.0 types `Card`'s `style` without radius, border width or `overflow` (geometry goes through `radius`/`border`/`clipContent`) and stops clipping a Card's children by default, so `BenefitList`'s `overflow: 'visible'` became both a type error and unnecessary; it is removed. The cap MOVES rather than widening to a `^4 || ^5` union, for the reason the 3.0.1 note records: a union lets an app resolve a second, nested Bloom copy and split the React contexts. The floor is 5.1.0 because that is the version measured here (every package builds and tests against it); 5.0.0 was not measured. RAISED TO ^6.0.0 (services 10.0.0, a major because consumers must move to Bloom 6 with it, and because this package's own `FollowButton`/`FollowTargetButton` `size` moves with Bloom's vocabulary): Bloom 6.0.0 removes the duplicate spellings its 5.x types still admitted, and this package used four of them. The control `size` narrows to `xs|sm|md|lg`, so every `small`/`medium`/`large` handed to a Bloom control became `sm`/`md`/`lg`, and the Follow buttons' public `size` became `sm|md|lg` because it is passed straight through to Bloom's media-header `FollowButton`. `Switch` is controlled by `checked`/`onCheckedChange`, not `value`/`onValueChange`. `Button` has no `variant`: each call site names `appearance` and `tone` (the pair the 5.x recipe resolved the variant to, so nothing repaints), `LinkButton`'s colour is `linkTone`, and `PeableButton` renders `InverseButton`. `Card` takes `appearance=\"outline\"` for `variant=\"outlined\"`. The deprecated spellings Bloom 6 still accepts (`isInvalid`, Chip `color`/`variant`) are not breaks and are left alone. The cap MOVES rather than widening to a `^5 || ^6` union, for the reason the 3.0.1 note records: a union lets an app resolve a second, nested Bloom copy and split the React contexts. The floor is 6.0.0 because that is the version measured when this raise was made; the workspace catalog has since moved to ^6.2.0, which adds only the `cover-header` subpath this package does not import, so the floor stays. MAINTENANCE 6.2.1: floor >=6.2.1 and cap <6.3.0 deliberately admit only the reviewed patch line with native Back/X completion and SVG paint/resize fixes. Known 6.4.0 lacks these fixes; the exact root catalog pin prevents a caret from selecting it. A broader minor range requires forward-port and consumer acceptance. VERIFIED 7.1.2: the additional >=7.1.2 <7.2.0 line contains the reviewed native fixes. Services build/types and the website real browser provider/theme tests pass with one byte-verified Bloom copy at both the root and Services importer. The root catalog remains pinned to 6.2.1; consumers explicitly choose one supported line. Versions 6.3.x, 6.4.x and 7.1.0/7.1.1 remain excluded.",
|
|
216
216
|
"peerDependencies": {
|
|
217
217
|
"@expo/vector-icons": "^15.0.3",
|
|
218
|
-
"@oxy.so/bloom": "
|
|
219
|
-
"@oxy.so/core": "
|
|
218
|
+
"@oxy.so/bloom": ">=6.2.1 <6.3.0 || >=7.1.2 <7.2.0",
|
|
219
|
+
"@oxy.so/core": "4.5.0-oxy-one.0",
|
|
220
220
|
"@react-native-async-storage/async-storage": "^2.0.0",
|
|
221
|
-
"@react-native-community/netinfo": ">=11.4.1",
|
|
221
|
+
"@react-native-community/netinfo": ">=11.4.1 <13.0.0",
|
|
222
222
|
"@tanstack/query-async-storage-persister": "^5.101",
|
|
223
223
|
"@tanstack/query-sync-storage-persister": "^5.101",
|
|
224
224
|
"@tanstack/react-query": "^5.101",
|
|
225
225
|
"@tanstack/react-query-persist-client": "^5.101",
|
|
226
|
-
"@types/react": "
|
|
227
|
-
"@types/react-native": "
|
|
228
|
-
"expo": ">=56.0.0",
|
|
226
|
+
"@types/react": "<20.0.0",
|
|
227
|
+
"@types/react-native": "<1.0.0",
|
|
228
|
+
"expo": ">=56.0.0 <58.0.0",
|
|
229
229
|
"expo-clipboard": ">=56.0.0 <59.0.0",
|
|
230
|
-
"expo-constants": ">=56.0.0",
|
|
231
|
-
"expo-document-picker": ">=56.0.0",
|
|
232
|
-
"expo-file-system": ">=56.0.0",
|
|
233
|
-
"expo-haptics": ">=56.0.0",
|
|
234
|
-
"expo-image": ">=2.0.0",
|
|
235
|
-
"expo-image-manipulator": ">=56.0.0",
|
|
236
|
-
"expo-image-picker": ">=56.0.0",
|
|
237
|
-
"expo-linear-gradient": ">=56.0.0",
|
|
238
|
-
"expo-modules-core": "
|
|
239
|
-
"expo-notifications": ">=56.0.0",
|
|
240
|
-
"expo-web-browser": ">=56.0.0",
|
|
241
|
-
"nativewind": ">=5.0.0",
|
|
230
|
+
"expo-constants": ">=56.0.0 <58.0.0",
|
|
231
|
+
"expo-document-picker": ">=56.0.0 <58.0.0",
|
|
232
|
+
"expo-file-system": ">=56.0.0 <58.0.0",
|
|
233
|
+
"expo-haptics": ">=56.0.0 <58.0.0",
|
|
234
|
+
"expo-image": ">=2.0.0 <58.0.0",
|
|
235
|
+
"expo-image-manipulator": ">=56.0.0 <58.0.0",
|
|
236
|
+
"expo-image-picker": ">=56.0.0 <58.0.0",
|
|
237
|
+
"expo-linear-gradient": ">=56.0.0 <58.0.0",
|
|
238
|
+
"expo-modules-core": "<58.0.0",
|
|
239
|
+
"expo-notifications": ">=56.0.0 <58.0.0",
|
|
240
|
+
"expo-web-browser": ">=56.0.0 <58.0.0",
|
|
241
|
+
"nativewind": ">=5.0.0-preview.3 <6.0.0",
|
|
242
242
|
"react-native-css": "^3.0.6",
|
|
243
|
-
"react": ">=18.0.0",
|
|
244
|
-
"react-native": ">=0.85.0",
|
|
245
|
-
"react-native-gesture-handler": ">=2.31.1",
|
|
246
|
-
"react-native-keyboard-controller": ">=1.21.0",
|
|
243
|
+
"react": ">=18.0.0 <20.0.0",
|
|
244
|
+
"react-native": ">=0.85.0 <0.87.0",
|
|
245
|
+
"react-native-gesture-handler": ">=2.31.1 <3.0.0",
|
|
246
|
+
"react-native-keyboard-controller": ">=1.21.0 <2.0.0",
|
|
247
247
|
"react-native-qrcode-svg": "^6.3.0",
|
|
248
|
-
"react-native-reanimated": ">=4.3.0",
|
|
249
|
-
"react-native-safe-area-context": ">=5.7.0",
|
|
250
|
-
"react-native-svg": ">=15.15.0",
|
|
248
|
+
"react-native-reanimated": ">=4.3.0 <5.0.0",
|
|
249
|
+
"react-native-safe-area-context": ">=5.7.0 <6.0.0",
|
|
250
|
+
"react-native-svg": ">=15.15.0 <16.0.0",
|
|
251
251
|
"zustand": "^5.0.0"
|
|
252
252
|
},
|
|
253
253
|
"peerDependenciesMeta": {
|
package/src/index.ts
CHANGED
|
@@ -436,3 +436,7 @@ export {
|
|
|
436
436
|
closeAccountDialog,
|
|
437
437
|
subscribeToAccountDialog,
|
|
438
438
|
} from './ui/navigation/accountDialogManager';
|
|
439
|
+
|
|
440
|
+
export type { StartNativeOAuthSignInOptions, NativeOAuthSignInResult } from './ui/oauth/nativeAuthTransport';
|
|
441
|
+
|
|
442
|
+
export { usePersonalPlans, usePersonalPlanSubscriptions } from './ui/hooks/queries/usePersonalPlans';
|
|
@@ -78,7 +78,7 @@ function makeHarness(overrides: Partial<RunProviderColdBootOptions> = {}): Harne
|
|
|
78
78
|
const sessionClientStart = jest.fn(async () => undefined);
|
|
79
79
|
|
|
80
80
|
const options: RunProviderColdBootOptions = {
|
|
81
|
-
oxyServices: {} as RunProviderColdBootOptions['oxyServices'],
|
|
81
|
+
oxyServices: { apps: { getPublic: jest.fn(async () => ({ id: 'registered-app', name: 'Registered App', type: 'first_party', isOfficial: false, isInternal: false, scopes: [] })) } } as unknown as RunProviderColdBootOptions['oxyServices'],
|
|
82
82
|
authStore: makeEmptyAuthStore(),
|
|
83
83
|
clientId: CLIENT_ID,
|
|
84
84
|
sessionClient: { start: sessionClientStart } as unknown as SessionClient,
|
|
@@ -198,7 +198,7 @@ describe('runProviderColdBoot — no SDK-initiated IdP navigation (#691 phase 7b
|
|
|
198
198
|
await commit.commitSession({ sessionId: 's1', userId: 'u1' });
|
|
199
199
|
expect(options.commitSession).toHaveBeenCalledWith(
|
|
200
200
|
{ sessionId: 's1', userId: 'u1' },
|
|
201
|
-
{ activate: true },
|
|
201
|
+
{ activate: true, oauth: true },
|
|
202
202
|
);
|
|
203
203
|
});
|
|
204
204
|
});
|
|
@@ -213,3 +213,57 @@ describe('runProviderColdBoot — no SDK-initiated IdP navigation (#691 phase 7b
|
|
|
213
213
|
expect(mockRedirectToAuthorize).not.toHaveBeenCalled();
|
|
214
214
|
});
|
|
215
215
|
});
|
|
216
|
+
|
|
217
|
+
describe('registered cold-boot classification', () => {
|
|
218
|
+
beforeEach(() => { mockRunSessionColdBoot.mockReset(); mockTryCompleteOAuthReturn.mockReset(); mockTryCompleteOAuthReturn.mockResolvedValue(false); });
|
|
219
|
+
it('external classification bypasses prior secrets, shared device and identity pin work', async () => {
|
|
220
|
+
const { options } = makeHarness();
|
|
221
|
+
(options.oxyServices.apps.getPublic as jest.Mock).mockResolvedValue({ id: 'external', name: 'External', type: 'third_party', isOfficial: false, isInternal: false, scopes: [] });
|
|
222
|
+
const beforeDeviceBoot = jest.fn();
|
|
223
|
+
await runProviderColdBoot({ ...options, beforeDeviceBoot });
|
|
224
|
+
expect(options.oxyServices.apps.getPublic).toHaveBeenCalledWith(CLIENT_ID, { cache: false });
|
|
225
|
+
expect(options.authStore.load).not.toHaveBeenCalled();
|
|
226
|
+
expect(options.authStore.clear).not.toHaveBeenCalled();
|
|
227
|
+
expect(options.syncDeviceCredentialToHost).not.toHaveBeenCalled();
|
|
228
|
+
expect(beforeDeviceBoot).not.toHaveBeenCalled();
|
|
229
|
+
expect(mockRunSessionColdBoot).not.toHaveBeenCalled();
|
|
230
|
+
});
|
|
231
|
+
it.each(['missing', 'inactive', 'offline', 'invalid'])('fails closed for %s before secrets and callback work', async (mode) => {
|
|
232
|
+
const { options } = makeHarness();
|
|
233
|
+
if (mode === 'missing') options.clientId = undefined;
|
|
234
|
+
else if (mode === 'invalid') (options.oxyServices.apps.getPublic as jest.Mock).mockResolvedValue({ type: 'unknown' });
|
|
235
|
+
else (options.oxyServices.apps.getPublic as jest.Mock).mockRejectedValue(new Error(mode));
|
|
236
|
+
await runProviderColdBoot(options);
|
|
237
|
+
expect(options.markAuthResolved).toHaveBeenCalled();
|
|
238
|
+
expect(options.authStore.load).not.toHaveBeenCalled();
|
|
239
|
+
expect(options.authStore.clear).not.toHaveBeenCalled();
|
|
240
|
+
expect(options.syncDeviceCredentialToHost).not.toHaveBeenCalled();
|
|
241
|
+
expect(mockRunSessionColdBoot).not.toHaveBeenCalled();
|
|
242
|
+
expect(mockTryCompleteOAuthReturn).not.toHaveBeenCalled();
|
|
243
|
+
});
|
|
244
|
+
it('a cold external OAuth callback never probes a previous device secret', async () => {
|
|
245
|
+
const { options } = makeHarness();
|
|
246
|
+
(options.oxyServices.apps.getPublic as jest.Mock).mockResolvedValue({ id: 'external', name: 'External', type: 'third_party', isOfficial: false, isInternal: false, scopes: [] });
|
|
247
|
+
mockTryCompleteOAuthReturn.mockImplementation(async ({ commitSession }) => {
|
|
248
|
+
await commitSession({ sessionId: 'isolated', userId: 'external-user', accessToken: 'isolated-bearer' }); return true;
|
|
249
|
+
});
|
|
250
|
+
await runProviderColdBoot(options);
|
|
251
|
+
expect(options.commitSession).toHaveBeenCalledWith({ sessionId: 'isolated', userId: 'external-user', accessToken: 'isolated-bearer' }, { activate: true, oauth: true });
|
|
252
|
+
expect(options.authStore.load).not.toHaveBeenCalled();
|
|
253
|
+
expect(options.syncDeviceCredentialToHost).not.toHaveBeenCalled();
|
|
254
|
+
expect(mockRunSessionColdBoot).not.toHaveBeenCalled();
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('an unresponsive registry resolves signed-out without a late privileged continuation', async () => {
|
|
259
|
+
jest.useFakeTimers();
|
|
260
|
+
try {
|
|
261
|
+
const {options}=makeHarness(); let release!: (app:unknown)=>void;
|
|
262
|
+
(options.oxyServices.apps.getPublic as jest.Mock).mockImplementation(()=>new Promise(resolve=>{release=resolve;}));
|
|
263
|
+
mockRunSessionColdBoot.mockReset(); mockTryCompleteOAuthReturn.mockReset();
|
|
264
|
+
const boot=runProviderColdBoot(options); await jest.advanceTimersByTimeAsync(5000); await boot;
|
|
265
|
+
expect(options.markAuthResolved).toHaveBeenCalled(); expect(options.authStore.load).not.toHaveBeenCalled();
|
|
266
|
+
release({id:'app',name:'Late',type:'first_party',isOfficial:true,isInternal:false,scopes:[]}); await Promise.resolve();
|
|
267
|
+
expect(mockRunSessionColdBoot).not.toHaveBeenCalled(); expect(mockTryCompleteOAuthReturn).not.toHaveBeenCalled();
|
|
268
|
+
} finally {jest.useRealTimers();}
|
|
269
|
+
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveApplicationSessionLane } from '../oauth/applicationSessionLane';
|
|
1
2
|
import { logger as loggerUtil, type OxyServices } from '@oxy.so/core';
|
|
2
3
|
import { runSessionColdBoot, type AuthStateStore, type IdentityBinding, type SessionMode } from '@oxy.so/core/session';
|
|
3
4
|
import type { SessionClient } from '@oxy.so/core/session';
|
|
@@ -80,8 +81,14 @@ export interface RunProviderColdBootOptions {
|
|
|
80
81
|
/** The identity binding required by `sessionMode: 'identity'`. */
|
|
81
82
|
identity?: IdentityBinding;
|
|
82
83
|
sessionClient: SessionClient;
|
|
84
|
+
/** Provider-owned fresh classifier, also used by explicit action recovery. */
|
|
85
|
+
resolveSessionLane?: () => Promise<'device' | 'oauth'>;
|
|
86
|
+
/** Identity pin work may run only after a fresh device classification. */
|
|
87
|
+
onClassified?: (lane: 'device' | 'oauth') => void | Promise<void>;
|
|
88
|
+
beforeDeviceBoot?: () => Promise<void>;
|
|
89
|
+
afterDeviceBoot?: () => Promise<void>;
|
|
83
90
|
syncDeviceCredentialToHost: () => Promise<void>;
|
|
84
|
-
commitSession: (input: CommitInput, options: { activate: boolean }) => Promise<void>;
|
|
91
|
+
commitSession: (input: CommitInput, options: { activate: boolean; oauth?: boolean }) => Promise<void>;
|
|
85
92
|
markAuthResolved: () => void;
|
|
86
93
|
setTokenReady: (ready: boolean) => void;
|
|
87
94
|
}
|
|
@@ -124,6 +131,11 @@ export async function runProviderColdBoot(opts: RunProviderColdBootOptions): Pro
|
|
|
124
131
|
setTokenReady(false);
|
|
125
132
|
|
|
126
133
|
try {
|
|
134
|
+
// Classification precedes every local-secret, shared-credential and identity
|
|
135
|
+
// probe. Unavailable registry metadata fails closed, including offline boot.
|
|
136
|
+
const lane = await (opts.resolveSessionLane?.() ?? resolveApplicationSessionLane(oxyServices, clientId));
|
|
137
|
+
await opts.onClassified?.(lane);
|
|
138
|
+
if (identityBound && lane !== 'device') return;
|
|
127
139
|
// The redirect transport's RETURN leg. An app reaches it whenever the browser
|
|
128
140
|
// BLOCKED the sign-in popup and `startWebOAuthSignIn` fell back to a
|
|
129
141
|
// full-page redirect (`popup-blocked` / `popup-navigation-failed`), so it is
|
|
@@ -136,7 +148,7 @@ export async function runProviderColdBoot(opts: RunProviderColdBootOptions): Pro
|
|
|
136
148
|
oxyServices,
|
|
137
149
|
clientId,
|
|
138
150
|
authRedirectUri,
|
|
139
|
-
commitSession: (input) => commitSession(input, { activate: true }),
|
|
151
|
+
commitSession: (input) => commitSession(input, { activate: true, oauth: true }),
|
|
140
152
|
});
|
|
141
153
|
if (oauthCompleted) {
|
|
142
154
|
setTokenReady(true);
|
|
@@ -144,6 +156,13 @@ export async function runProviderColdBoot(opts: RunProviderColdBootOptions): Pro
|
|
|
144
156
|
return;
|
|
145
157
|
}
|
|
146
158
|
|
|
159
|
+
if (lane === 'oauth') {
|
|
160
|
+
setTokenReady(true);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
await opts.beforeDeviceBoot?.();
|
|
165
|
+
|
|
147
166
|
// Best-effort connectivity probe up front: an EXPLICIT offline verdict skips
|
|
148
167
|
// the two doomed network steps so routing settles immediately instead of
|
|
149
168
|
// burning the overall deadline on a mint that cannot succeed. Any ambiguity
|
|
@@ -224,6 +243,7 @@ export async function runProviderColdBoot(opts: RunProviderColdBootOptions): Pro
|
|
|
224
243
|
}
|
|
225
244
|
},
|
|
226
245
|
});
|
|
246
|
+
await opts.afterDeviceBoot?.();
|
|
227
247
|
} catch (error) {
|
|
228
248
|
if (__DEV__) {
|
|
229
249
|
loggerUtil.error(
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { classifyApplicationSessionLane } from '../oauth/applicationSessionLane';
|
|
1
2
|
import type React from 'react';
|
|
2
3
|
import { useCallback, useState, useEffect, useRef } from 'react';
|
|
3
4
|
import { type ViewStyle, type TextStyle, type StyleProp, Platform } from 'react-native';
|
|
@@ -142,6 +143,8 @@ export interface OxySignInButtonProps {
|
|
|
142
143
|
* ```
|
|
143
144
|
*/
|
|
144
145
|
onOAuthResult?: (result: OxyOAuthResult) => void;
|
|
146
|
+
/** Opt in to the shared SDK native finalizer. Mutually exclusive with onOAuthResult. */
|
|
147
|
+
nativeOAuthCompletion?: 'sdk';
|
|
145
148
|
}
|
|
146
149
|
|
|
147
150
|
const ACTION_KEYS: Record<OxySignInButtonAction, string> = {
|
|
@@ -188,9 +191,10 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
|
|
|
188
191
|
showWhenAuthenticated = false,
|
|
189
192
|
oauthRedirectUri,
|
|
190
193
|
onOAuthResult,
|
|
194
|
+
nativeOAuthCompletion,
|
|
191
195
|
}) => {
|
|
192
196
|
const { t } = useI18n();
|
|
193
|
-
const { openAccountDialog, oxyServices, clientId, webAuthMode, startWebOAuthSignIn } = useOxy();
|
|
197
|
+
const { openAccountDialog, oxyServices, clientId, webAuthMode, startWebOAuthSignIn, startNativeOAuthSignIn } = useOxy();
|
|
194
198
|
const { isAuthenticated, isLoading } = useAuthStore(
|
|
195
199
|
useShallow((state) => ({ isAuthenticated: state.isAuthenticated, isLoading: state.isLoading }))
|
|
196
200
|
);
|
|
@@ -228,7 +232,7 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
|
|
|
228
232
|
if (cached && cached.clientId === clientId && cached.oxyServices === oxyServices) {
|
|
229
233
|
return cached.promise;
|
|
230
234
|
}
|
|
231
|
-
const promise = oxyServices.apps.getPublic(clientId).catch((error) => {
|
|
235
|
+
const promise = oxyServices.apps.getPublic(clientId, { cache: false }).catch((error) => {
|
|
232
236
|
// Only clear if this is still the live entry (a later resolve may have
|
|
233
237
|
// replaced it after a clientId/oxyServices change).
|
|
234
238
|
if (appResolutionRef.current?.promise === promise) {
|
|
@@ -308,6 +312,14 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
|
|
|
308
312
|
return;
|
|
309
313
|
}
|
|
310
314
|
|
|
315
|
+
if (nativeOAuthCompletion === 'sdk') {
|
|
316
|
+
if (onOAuthResult) { notifyNotConfigured(app.name); return; }
|
|
317
|
+
const result = await startNativeOAuthSignIn({ redirectUri: oauthRedirectUri });
|
|
318
|
+
if (result.status === 'failed') notifyFailed();
|
|
319
|
+
if (result.status === 'unsupported') notifyNotConfigured(app.name);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
311
323
|
// Native: open the in-app auth session, then hand the handshake to the
|
|
312
324
|
// RP so it can complete the token exchange from its deep-link callback.
|
|
313
325
|
const [pkce, state] = await Promise.all([generatePkcePair(), generateOAuthState()]);
|
|
@@ -332,6 +344,8 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
|
|
|
332
344
|
clientId,
|
|
333
345
|
oauthRedirectUri,
|
|
334
346
|
onOAuthResult,
|
|
347
|
+
nativeOAuthCompletion,
|
|
348
|
+
startNativeOAuthSignIn,
|
|
335
349
|
startOfficialSignIn,
|
|
336
350
|
startWebOAuthSignIn,
|
|
337
351
|
shouldPreOpenPopup,
|
|
@@ -357,7 +371,7 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
|
|
|
357
371
|
const resolving = resolvePublicApplication();
|
|
358
372
|
if (!resolving) {
|
|
359
373
|
closeOAuthPopup(popup);
|
|
360
|
-
|
|
374
|
+
notifyNotConfigured('this app');
|
|
361
375
|
return;
|
|
362
376
|
}
|
|
363
377
|
let app: PublicApplication;
|
|
@@ -373,7 +387,7 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
|
|
|
373
387
|
notifyFailed();
|
|
374
388
|
return;
|
|
375
389
|
}
|
|
376
|
-
if (app
|
|
390
|
+
if (classifyApplicationSessionLane(app) === 'oauth') {
|
|
377
391
|
await startThirdPartyOAuth(app, popup);
|
|
378
392
|
return;
|
|
379
393
|
}
|
|
@@ -392,6 +406,7 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
|
|
|
392
406
|
);
|
|
393
407
|
notifyFailed();
|
|
394
408
|
} finally {
|
|
409
|
+
appResolutionRef.current = null;
|
|
395
410
|
routingRef.current = false;
|
|
396
411
|
}
|
|
397
412
|
},
|
|
@@ -400,6 +415,7 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
|
|
|
400
415
|
startOfficialSignIn,
|
|
401
416
|
startThirdPartyOAuth,
|
|
402
417
|
clientId,
|
|
418
|
+
notifyNotConfigured,
|
|
403
419
|
notifyFailed,
|
|
404
420
|
],
|
|
405
421
|
);
|