@oxyhq/services 18.1.0 → 19.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/lib/commonjs/ui/context/OxyContext.js +49 -93
  2. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  3. package/lib/commonjs/ui/context/hooks/useAuthOperations.js +15 -29
  4. package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
  5. package/lib/commonjs/ui/session/authStore.js +12 -50
  6. package/lib/commonjs/ui/session/authStore.js.map +1 -1
  7. package/lib/commonjs/ui/session/createSessionClient.js +6 -11
  8. package/lib/commonjs/ui/session/createSessionClient.js.map +1 -1
  9. package/lib/commonjs/ui/session/tokenTransport.js +3 -4
  10. package/lib/commonjs/ui/session/tokenTransport.js.map +1 -1
  11. package/lib/module/ui/context/OxyContext.js +49 -93
  12. package/lib/module/ui/context/OxyContext.js.map +1 -1
  13. package/lib/module/ui/context/hooks/useAuthOperations.js +15 -29
  14. package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
  15. package/lib/module/ui/session/authStore.js +13 -51
  16. package/lib/module/ui/session/authStore.js.map +1 -1
  17. package/lib/module/ui/session/createSessionClient.js +6 -11
  18. package/lib/module/ui/session/createSessionClient.js.map +1 -1
  19. package/lib/module/ui/session/tokenTransport.js +3 -4
  20. package/lib/module/ui/session/tokenTransport.js.map +1 -1
  21. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +15 -6
  22. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
  23. package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts +4 -5
  24. package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  25. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  26. package/lib/typescript/commonjs/ui/session/authStore.d.ts +11 -15
  27. package/lib/typescript/commonjs/ui/session/authStore.d.ts.map +1 -1
  28. package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts +6 -10
  29. package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts.map +1 -1
  30. package/lib/typescript/commonjs/ui/session/tokenTransport.d.ts +3 -4
  31. package/lib/typescript/commonjs/ui/session/tokenTransport.d.ts.map +1 -1
  32. package/lib/typescript/module/ui/context/OxyContext.d.ts +15 -6
  33. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  34. package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts +4 -5
  35. package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  36. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  37. package/lib/typescript/module/ui/session/authStore.d.ts +11 -15
  38. package/lib/typescript/module/ui/session/authStore.d.ts.map +1 -1
  39. package/lib/typescript/module/ui/session/createSessionClient.d.ts +6 -10
  40. package/lib/typescript/module/ui/session/createSessionClient.d.ts.map +1 -1
  41. package/lib/typescript/module/ui/session/tokenTransport.d.ts +3 -4
  42. package/lib/typescript/module/ui/session/tokenTransport.d.ts.map +1 -1
  43. package/package.json +4 -4
  44. package/src/ui/context/OxyContext.tsx +57 -100
  45. package/src/ui/context/__tests__/OxyContext.coldBoot.test.tsx +13 -12
  46. package/src/ui/context/hooks/useAuthOperations.ts +18 -27
  47. package/src/ui/session/__tests__/createSessionClient.test.ts +3 -1
  48. package/src/ui/session/__tests__/tokenTransport.test.ts +6 -5
  49. package/src/ui/session/authStore.ts +12 -55
  50. package/src/ui/session/createSessionClient.ts +5 -10
  51. package/src/ui/session/tokenTransport.ts +3 -4
@@ -2,6 +2,7 @@ import type React from 'react';
2
2
  import { type ReactNode } from 'react';
3
3
  import { OxyServices } from '@oxyhq/core';
4
4
  import type { User, ApiError, SessionLoginResponse, AccountNode, CreateAccountInput, ClientSession, AccountDialogController, AccountDialogView } from '@oxyhq/core';
5
+ import type { SecurityAlert } from '@oxyhq/contracts';
5
6
  import type { UseFollowHook } from '../hooks/useFollow.types.js';
6
7
  import { useLanguageManagement } from '../hooks/useLanguageManagement.js';
7
8
  import type { RouteName } from '../navigation/routes.js';
@@ -40,7 +41,7 @@ export interface OxyContextState {
40
41
  *
41
42
  * Commits a successful session into context state through the SAME path the
42
43
  * QR device-flow and cold boot use (so `isAuthenticated` / `user` update and
43
- * the rotating refresh family is persisted). Returns a discriminated result
44
+ * the zero-cookie device credential is persisted). Returns a discriminated result
44
45
  * so the caller can branch on the two-factor-required case — which creates NO
45
46
  * session; the caller completes the 2FA challenge with the returned
46
47
  * `loginToken` via {@link OxyContextState.completeTwoFactorSignIn}.
@@ -52,18 +53,22 @@ export interface OxyContextState {
52
53
  /**
53
54
  * Complete a 2FA-gated password sign-in started by {@link signInWithPassword}.
54
55
  * Presents the short-lived `loginToken` with a TOTP `token` or a `backupCode`;
55
- * on success the session is committed exactly like a one-step sign-in.
56
+ * on success the session is committed exactly like a one-step sign-in. Returns
57
+ * any `securityAlert` the server attached so the caller can show the same
58
+ * "New sign-in detected" acknowledgement as the one-step path.
56
59
  */
57
60
  completeTwoFactorSignIn: (params: {
58
61
  loginToken: string;
59
62
  token?: string;
60
63
  backupCode?: string;
61
64
  deviceName?: string;
62
- }) => Promise<void>;
65
+ }) => Promise<{
66
+ securityAlert?: SecurityAlert;
67
+ }>;
63
68
  /**
64
69
  * Commit a session obtained out-of-band (the "Sign in with Oxy" QR device
65
- * flow). Plants tokens, persists the rotating refresh family, registers the
66
- * account into the device set, and hydrates the full user profile.
70
+ * flow). Plants tokens, persists the zero-cookie device credential, registers
71
+ * the account into the device set, and hydrates the full user profile.
67
72
  */
68
73
  handleWebSession: (session: SessionLoginResponse) => Promise<void>;
69
74
  logout: (targetSessionId?: string) => Promise<void>;
@@ -134,7 +139,10 @@ export interface OxyContextState {
134
139
  * Result of {@link OxyContextState.signInWithPassword}.
135
140
  *
136
141
  * `'ok'` — the password was accepted and the session committed (so
137
- * `isAuthenticated` / `user` are updated and the refresh family persisted).
142
+ * `isAuthenticated` / `user` are updated and the device credential persisted).
143
+ * `securityAlert` is present when the server flagged this sign-in as anomalous
144
+ * (new device / location) — the caller shows a "New sign-in detected"
145
+ * acknowledgement before proceeding; the session is already committed.
138
146
  *
139
147
  * `'2fa_required'` — the account has two-factor auth enabled, so NO session was
140
148
  * created. Complete the challenge with the returned short-lived `loginToken`
@@ -142,6 +150,7 @@ export interface OxyContextState {
142
150
  */
143
151
  export type PasswordSignInResult = {
144
152
  status: 'ok';
153
+ securityAlert?: SecurityAlert;
145
154
  } | {
146
155
  status: '2fa_required';
147
156
  loginToken: string;
@@ -1 +1 @@
1
- {"version":3,"file":"OxyContext.d.ts","sourceRoot":"","sources":["../../../../../src/ui/context/OxyContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,EAAE,WAAW,EAAa,MAAM,aAAa,CAAC;AACrD,OAAO,KAAK,EACV,IAAI,EACJ,QAAQ,EACR,oBAAoB,EACpB,WAAW,EACX,kBAAkB,EAClB,aAAa,EAGb,uBAAuB,EACvB,iBAAiB,EAClB,MAAM,aAAa,CAAC;AAerB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA0B,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAgC,CAAC;AAKvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAsB,CAAC;AAgBtD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,eAAe,EAAE,OAAO,CAAC;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,OAAO,CAAC;IACtB,cAAc,EAAE,OAAO,CAAC;IACxB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,mBAAmB,EAAE,OAAO,CAAC;IAC7B;;;;;;;;;OASG;IACH,cAAc,EAAE,OAAO,CAAC;IACxB,cAAc,EAAE,OAAO,CAAC;IACxB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,uBAAuB,EAAE,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAC,UAAU,CAAC,CAAC;IAC9E,mBAAmB,EAAE,MAAM,CAAC;IAC5B,yBAAyB,EAAE,MAAM,CAAC;IAGlC,WAAW,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACpC,YAAY,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAG3C,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAElE;;;;;;;;;OASG;IACH,kBAAkB,EAAE,CAClB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;KAAE,KACvD,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAEnC;;;;OAIG;IACH,uBAAuB,EAAE,CAAC,MAAM,EAAE;QAChC,UAAU,EAAE,MAAM,CAAC;QACnB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpB;;;;OAIG;IACH,gBAAgB,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAGnE,MAAM,EAAE,CAAC,eAAe,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,eAAe,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,WAAW,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,iBAAiB,EAAE,MAAM,OAAO,CAC9B,KAAK,CAAC;QACJ,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC,CACH,CAAC;IACF,uBAAuB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,gBAAgB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,mBAAmB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;;;;;;OAQG;IACH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,WAAW,CAAC;IACzB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,eAAe,CAAC,EAAE,CAAC,cAAc,EAAE,SAAS,GAAG;QAAE,MAAM,EAAE,SAAS,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,KAAK,IAAI,CAAC;IAC/G,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAK7B,sFAAsF;IACtF,uBAAuB,EAAE,uBAAuB,GAAG,IAAI,CAAC;IACxD,iEAAiE;IACjE,mBAAmB,EAAE,OAAO,CAAC;IAC7B;;;;OAIG;IACH,iBAAiB,EAAE,CAAC,IAAI,CAAC,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACtD,gFAAgF;IAChF,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAS/B,0GAA0G;IAC1G,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB;;;;;;;;;;OAUG;IACH,eAAe,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,eAAe,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,aAAa,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;CACnE;AAID;;;;;;;;;GASG;AACH,MAAM,MAAM,oBAAoB,GAC5B;IAAE,MAAM,EAAE,IAAI,CAAA;CAAE,GAChB;IAAE,MAAM,EAAE,cAAc,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnD,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,SAAS,CAAC;IACpB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;CACrC;AA+FD,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CAq+BzD,CAAC;AAEF,eAAO,MAAM,kBAAkB,mCAAc,CAAC;AA8D9C,eAAO,MAAM,MAAM,QAAO,eAMzB,CAAC"}
1
+ {"version":3,"file":"OxyContext.d.ts","sourceRoot":"","sources":["../../../../../src/ui/context/OxyContext.tsx"],"names":[],"mappings":"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"}
@@ -9,10 +9,9 @@ export interface UseAuthOperationsOptions {
9
9
  /**
10
10
  * The device-first persisted auth-state store. On EXPLICIT full sign-out the
11
11
  * session blob is cleared (`store.clear()`) so a reload's cold boot finds no
12
- * refresh family to restore; on sign-in a returned rotating refresh token is
13
- * persisted so the next boot warm-restores without a redirect. The long-lived
14
- * device token SURVIVES sign-out (the device is unchanged), so it is never
15
- * cleared here.
12
+ * device credential to restore; on sign-in the returned zero-cookie device
13
+ * credential (`deviceId` + `deviceSecret`) is persisted so the next boot
14
+ * warm-restores without a redirect.
16
15
  */
17
16
  store: AuthStateStore;
18
17
  activeSessionId: string | null;
@@ -51,7 +50,7 @@ export interface UseAuthOperationsResult {
51
50
  logoutAll: () => Promise<void>;
52
51
  }
53
52
  /**
54
- * Clear the persisted refresh-token family on an explicit full sign-out.
53
+ * Clear the persisted device credential on an explicit full sign-out.
55
54
  * Best-effort and non-blocking: sign-out must never fail because a storage
56
55
  * write threw. Exported so `OxyContext`'s zero-account branch (a REMOTE full
57
56
  * sign-out pushed over the socket) runs the EXACT same cleanup as the local
@@ -1 +1 @@
1
- {"version":3,"file":"useAuthOperations.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/context/hooks/useAuthOperations.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAwB,MAAM,aAAa,CAAC;AAIvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+BAA4B,CAAC;AACnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjC;;;;;;;OAOG;IACH,KAAK,EAAE,cAAc,CAAC;IACtB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACvD,cAAc,EAAE,CAAC,QAAQ,EAAE,aAAa,EAAE,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;IACnF,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,0JAA0J;IAC1J,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD;;;;;OAKG;IACH,aAAa,EAAE,aAAa,CAAC;IAC7B,+MAA+M;IAC/M,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,uBAAuB,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;IACpC,YAAY,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACnC,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,YAAY,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC,KAAK,IAAI,CAAC;IAClD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;CACrD;AAED,MAAM,WAAW,uBAAuB;IACtC,uCAAuC;IACvC,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,kCAAkC;IAClC,MAAM,EAAE,CAAC,eAAe,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,+BAA+B;IAC/B,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAChC;AAMD;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,cAAc,EACrB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,GAClD,IAAI,CAIN;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,wRAmB/B,wBAAwB,KAAG,uBA6R7B,CAAC"}
1
+ {"version":3,"file":"useAuthOperations.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/context/hooks/useAuthOperations.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAwB,MAAM,aAAa,CAAC;AAIvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+BAA4B,CAAC;AACnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjC;;;;;;OAMG;IACH,KAAK,EAAE,cAAc,CAAC;IACtB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACvD,cAAc,EAAE,CAAC,QAAQ,EAAE,aAAa,EAAE,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;IACnF,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,0JAA0J;IAC1J,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD;;;;;OAKG;IACH,aAAa,EAAE,aAAa,CAAC;IAC7B,+MAA+M;IAC/M,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,uBAAuB,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;IACpC,YAAY,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACnC,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,YAAY,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC,KAAK,IAAI,CAAC;IAClD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;CACrD;AAED,MAAM,WAAW,uBAAuB;IACtC,uCAAuC;IACvC,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,kCAAkC;IAClC,MAAM,EAAE,CAAC,eAAe,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,+BAA+B;IAC/B,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAChC;AAMD;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,cAAc,EACrB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,GAClD,IAAI,CAIN;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,wRAmB/B,wBAAwB,KAAG,uBAqR7B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useAccountMutations.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/hooks/mutations/useAccountMutations.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useAccountMutations.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/hooks/mutations/useAccountMutations.ts"],"names":[],"mappings":"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"}
@@ -1,18 +1,16 @@
1
1
  /**
2
2
  * Platform `AuthStateStore` for `@oxyhq/services`.
3
3
  *
4
- * The device-first session model persists the rotating refresh-token family
5
- * head per origin (web) / per device (native). `@oxyhq/core` owns the store
6
- * shape + logic (`createWebAuthStateStore` / `createNativeAuthStateStore`); this
7
- * module only supplies the platform storage seam and wires the native device
8
- * token to the SHARED keychain so every native Oxy app joins one DeviceSession:
4
+ * The zero-cookie device model persists the durable device credential
5
+ * (`deviceId` + `deviceSecret`) per origin (web) / per device (native), and the
6
+ * SDK re-mints the access token from that credential on cold boot. `@oxyhq/core`
7
+ * owns the store shape + logic (`createWebAuthStateStore` /
8
+ * `createNativeAuthStateStore`); this module only supplies the platform storage
9
+ * seam:
9
10
  *
10
11
  * - web → `createWebAuthStateStore()` (localStorage, in-memory fallback).
11
- * - native → `createNativeAuthStateStore(secureKV)` for the SESSION blob
12
- * (refresh token) over `expo-secure-store` (AsyncStorage fallback), with the
13
- * long-lived DEVICE token delegated to `KeyManager`'s shared keychain
14
- * (`group.so.oxy.shared`) instead of per-app storage — so an in-app login on
15
- * any native Oxy app attributes to the SAME server-side DeviceSession.
12
+ * - native → `createNativeAuthStateStore(secureKV)` for the SESSION blob over
13
+ * `expo-secure-store` (AsyncStorage fallback).
16
14
  *
17
15
  * `expo-secure-store` is loaded via a runtime-computed dynamic import (the same
18
16
  * optional-native-module pattern `OxyProvider` uses for netinfo / keyboard
@@ -23,11 +21,9 @@ import { type AuthStateStore } from '@oxyhq/core';
23
21
  /**
24
22
  * Build the platform {@link AuthStateStore} for this runtime.
25
23
  *
26
- * Native additionally routes the device token through the shared keychain so
27
- * every native Oxy app shares one server-side DeviceSession; the session blob
28
- * (refresh token) stays per-app in SecureStore. Best-effort keychain access —
29
- * a locked/failed shared-keychain read degrades to "no device token" rather
30
- * than throwing out of cold boot / refresh.
24
+ * Native persists the SESSION blob (`deviceId` + `deviceSecret` + cached access
25
+ * token) per-app in SecureStore; the SDK re-mints the access token from the
26
+ * device credential on the next cold boot.
31
27
  */
32
28
  export declare function createPlatformAuthStateStore(): AuthStateStore;
33
29
  //# sourceMappingURL=authStore.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"authStore.d.ts","sourceRoot":"","sources":["../../../../../src/ui/session/authStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAIL,KAAK,cAAc,EAGpB,MAAM,aAAa,CAAC;AAmFrB;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,IAAI,cAAc,CA2C7D"}
1
+ {"version":3,"file":"authStore.d.ts","sourceRoot":"","sources":["../../../../../src/ui/session/authStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAGL,KAAK,cAAc,EAEpB,MAAM,aAAa,CAAC;AAmFrB;;;;;;GAMG;AACH,wBAAgB,4BAA4B,IAAI,cAAc,CAM7D"}
@@ -1,4 +1,4 @@
1
- import { SessionClient, createSessionClientHost, type AuthStateStore, type OxyServices, type SessionClientOptions } from '@oxyhq/core';
1
+ import { SessionClient, createSessionClientHost, type AuthStateStore, type OxyServices } from '@oxyhq/core';
2
2
  /**
3
3
  * Wire a `SessionClient` for `@oxyhq/services`.
4
4
  *
@@ -11,22 +11,18 @@ import { SessionClient, createSessionClientHost, type AuthStateStore, type OxySe
11
11
  * core's lazy dynamic import of a bare specifier — bundler-fragile in
12
12
  * Metro/Expo-web against the published core dist.
13
13
  * - the device-first {@link createTokenTransport}, which mints a fallback token
14
- * by rotating the persisted refresh family (`store`).
14
+ * from the persisted device credential (`store` → `deviceId` + `deviceSecret`).
15
15
  *
16
16
  * `onUnauthenticated` fires when an applied device state has zero accounts (a
17
17
  * device signout-all): the provider clears the persisted store + local state so
18
18
  * a reload does not try to restore a dead session. The host is returned
19
19
  * alongside the client so the caller can call `host.setCurrentAccountId(...)`
20
20
  * as the active account changes.
21
+ *
22
+ * The realtime socket is bearer-only: a signed-out tab cannot join any device
23
+ * room, so there is no signed-out socket wiring.
21
24
  */
22
- export declare function createSessionClient(oxyServices: OxyServices, store: AuthStateStore, onUnauthenticated?: () => void,
23
- /**
24
- * Optional signed-out realtime wiring: `signedOutSocketAuth` (open the socket
25
- * while signed out — web returns `true` to ride the `oxy_device` cookie,
26
- * native returns the shared device token) and `onSessionAppeared` (self-acquire
27
- * when a sibling signs in on this device).
28
- */
29
- extra?: Pick<SessionClientOptions, 'signedOutSocketAuth' | 'onSessionAppeared'>): {
25
+ export declare function createSessionClient(oxyServices: OxyServices, store: AuthStateStore, onUnauthenticated?: () => void): {
30
26
  client: SessionClient;
31
27
  host: ReturnType<typeof createSessionClientHost>;
32
28
  };
@@ -1 +1 @@
1
- {"version":3,"file":"createSessionClient.d.ts","sourceRoot":"","sources":["../../../../../src/ui/session/createSessionClient.ts"],"names":[],"mappings":"AACA,OAAO,EACL,aAAa,EACb,uBAAuB,EACvB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EAC1B,MAAM,aAAa,CAAC;AAGrB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,WAAW,EACxB,KAAK,EAAE,cAAc,EACrB,iBAAiB,CAAC,EAAE,MAAM,IAAI;AAC9B;;;;;GAKG;AACH,KAAK,CAAC,EAAE,IAAI,CAAC,oBAAoB,EAAE,qBAAqB,GAAG,mBAAmB,CAAC,GAC9E;IACD,MAAM,EAAE,aAAa,CAAC;IACtB,IAAI,EAAE,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAC;CAClD,CAKA"}
1
+ {"version":3,"file":"createSessionClient.d.ts","sourceRoot":"","sources":["../../../../../src/ui/session/createSessionClient.ts"],"names":[],"mappings":"AACA,OAAO,EACL,aAAa,EACb,uBAAuB,EACvB,KAAK,cAAc,EACnB,KAAK,WAAW,EACjB,MAAM,aAAa,CAAC;AAGrB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,WAAW,EACxB,KAAK,EAAE,cAAc,EACrB,iBAAiB,CAAC,EAAE,MAAM,IAAI,GAC7B;IACD,MAAM,EAAE,aAAa,CAAC;IACtB,IAAI,EAAE,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAC;CAClD,CAKA"}
@@ -5,10 +5,9 @@ import { type AuthStateStore, type OxyServices, type TokenTransport } from '@oxy
5
5
  * `ensureActiveToken` is the fallback the client uses when a `session_state`
6
6
  * push arrived WITHOUT an embedded `activeToken` and the app currently holds no
7
7
  * bearer. It mints one through the ONE unified refresh path
8
- * (`refreshPersistedSession`): rotate the persisted refresh-token family
9
- * (`POST /auth/refresh-token`) and, on native, fall back to the shared-keychain
10
- * re-mint. There is no FedCM/silent-iframe arm anymore — the per-origin
11
- * persisted refresh token is the durable web credential.
8
+ * (`refreshPersistedSession`), which presents the persisted zero-cookie device
9
+ * credential (`deviceId` + `deviceSecret`) at `POST /session/device/token`. The
10
+ * persisted device credential is the sole durable restore credential.
12
11
  *
13
12
  * Concurrent pushes coalesce onto one in-flight mint. A failure is logged and
14
13
  * swallowed: this method must never throw out (it runs inside the socket state
@@ -1 +1 @@
1
- {"version":3,"file":"tokenTransport.d.ts","sourceRoot":"","sources":["../../../../../src/ui/session/tokenTransport.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,cAAc,EACpB,MAAM,aAAa,CAAC;AAGrB;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,WAAW,EACxB,KAAK,EAAE,cAAc,GACpB,cAAc,CAsChB"}
1
+ {"version":3,"file":"tokenTransport.d.ts","sourceRoot":"","sources":["../../../../../src/ui/session/tokenTransport.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,cAAc,EACpB,MAAM,aAAa,CAAC;AAGrB;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,WAAW,EACxB,KAAK,EAAE,cAAc,GACpB,cAAc,CAsChB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/services",
3
- "version": "18.1.0",
3
+ "version": "19.0.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",
@@ -106,10 +106,10 @@
106
106
  }
107
107
  },
108
108
  "dependencies": {
109
- "@oxyhq/contracts": "0.12.0"
109
+ "@oxyhq/contracts": "0.13.0"
110
110
  },
111
111
  "devDependencies": {
112
- "@oxyhq/core": "8.1.0",
112
+ "@oxyhq/core": "9.0.0",
113
113
  "nativewind": "5.0.0-preview.3",
114
114
  "react-native-css": "^3.0.0",
115
115
  "@react-native-async-storage/async-storage": "^2.0.0",
@@ -153,7 +153,7 @@
153
153
  "peerDependencies": {
154
154
  "@expo/vector-icons": "^15.0.3",
155
155
  "@oxyhq/bloom": ">=0.16.0",
156
- "@oxyhq/core": "^8.0.0",
156
+ "@oxyhq/core": "^9.0.0",
157
157
  "@react-native-async-storage/async-storage": "^2.0.0",
158
158
  "@react-native-community/netinfo": "^11.4.1",
159
159
  "@tanstack/query-async-storage-persister": "^5.101",
@@ -31,6 +31,7 @@ import {
31
31
  createAccountDialogController,
32
32
  logger as loggerUtil,
33
33
  } from '@oxyhq/core';
34
+ import type { SecurityAlert } from '@oxyhq/contracts';
34
35
  import {
35
36
  registerAccountDialogControls,
36
37
  notifyAccountDialogVisibility,
@@ -99,7 +100,7 @@ export interface OxyContextState {
99
100
  *
100
101
  * Commits a successful session into context state through the SAME path the
101
102
  * QR device-flow and cold boot use (so `isAuthenticated` / `user` update and
102
- * the rotating refresh family is persisted). Returns a discriminated result
103
+ * the zero-cookie device credential is persisted). Returns a discriminated result
103
104
  * so the caller can branch on the two-factor-required case — which creates NO
104
105
  * session; the caller completes the 2FA challenge with the returned
105
106
  * `loginToken` via {@link OxyContextState.completeTwoFactorSignIn}.
@@ -113,19 +114,21 @@ export interface OxyContextState {
113
114
  /**
114
115
  * Complete a 2FA-gated password sign-in started by {@link signInWithPassword}.
115
116
  * Presents the short-lived `loginToken` with a TOTP `token` or a `backupCode`;
116
- * on success the session is committed exactly like a one-step sign-in.
117
+ * on success the session is committed exactly like a one-step sign-in. Returns
118
+ * any `securityAlert` the server attached so the caller can show the same
119
+ * "New sign-in detected" acknowledgement as the one-step path.
117
120
  */
118
121
  completeTwoFactorSignIn: (params: {
119
122
  loginToken: string;
120
123
  token?: string;
121
124
  backupCode?: string;
122
125
  deviceName?: string;
123
- }) => Promise<void>;
126
+ }) => Promise<{ securityAlert?: SecurityAlert }>;
124
127
 
125
128
  /**
126
129
  * Commit a session obtained out-of-band (the "Sign in with Oxy" QR device
127
- * flow). Plants tokens, persists the rotating refresh family, registers the
128
- * account into the device set, and hydrates the full user profile.
130
+ * flow). Plants tokens, persists the zero-cookie device credential, registers
131
+ * the account into the device set, and hydrates the full user profile.
129
132
  */
130
133
  handleWebSession: (session: SessionLoginResponse) => Promise<void>;
131
134
 
@@ -212,14 +215,17 @@ const OxyContext = createContext<OxyContextState | null>(null);
212
215
  * Result of {@link OxyContextState.signInWithPassword}.
213
216
  *
214
217
  * `'ok'` — the password was accepted and the session committed (so
215
- * `isAuthenticated` / `user` are updated and the refresh family persisted).
218
+ * `isAuthenticated` / `user` are updated and the device credential persisted).
219
+ * `securityAlert` is present when the server flagged this sign-in as anomalous
220
+ * (new device / location) — the caller shows a "New sign-in detected"
221
+ * acknowledgement before proceeding; the session is already committed.
216
222
  *
217
223
  * `'2fa_required'` — the account has two-factor auth enabled, so NO session was
218
224
  * created. Complete the challenge with the returned short-lived `loginToken`
219
225
  * via {@link OxyContextState.completeTwoFactorSignIn}.
220
226
  */
221
227
  export type PasswordSignInResult =
222
- | { status: 'ok' }
228
+ | { status: 'ok'; securityAlert?: SecurityAlert }
223
229
  | { status: '2fa_required'; loginToken: string };
224
230
 
225
231
  export interface OxyContextProviderProps {
@@ -272,16 +278,15 @@ const DEFAULT_SESSION_VALIDITY_MS = 7 * 24 * 60 * 60 * 1000;
272
278
  */
273
279
  const SESSION_HANDOFF_DEADLINE = 6000;
274
280
 
275
- /** The internal commit input — a session plus the device-first extras that are
276
- * not on the public `SessionLoginResponse` (a rotating refresh token, a device
277
- * token). All extras optional so `SessionLoginResponse` is assignable. */
281
+ /** The internal commit input — a session plus the zero-cookie device credential
282
+ * (`deviceId` + `deviceSecret`) that is not on the public `SessionLoginResponse`.
283
+ * All extras optional so `SessionLoginResponse` is assignable. */
278
284
  interface CommitInput {
279
285
  sessionId: string;
280
286
  accessToken?: string;
281
- refreshToken?: string;
282
287
  deviceId?: string;
283
- /** Rotating device secret (phase 2c — zero-cookie transport); persisted with
284
- * `deviceId` so the cold boot can mint via `POST /session/device/token`. */
288
+ /** Rotating device secret (zero-cookie transport); persisted with `deviceId`
289
+ * so the cold boot can mint via `POST /session/device/token`. */
285
290
  deviceSecret?: string;
286
291
  expiresAt?: string;
287
292
  userId?: string;
@@ -371,9 +376,9 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
371
376
  }
372
377
  const oxyServices = oxyServicesRef.current;
373
378
 
374
- // The device-first persisted auth-state store (per-origin refresh family on
375
- // web; SecureStore session blob + shared-keychain device token on native).
376
- // Built ONCE per provider mount.
379
+ // The device-first persisted auth-state store (per-origin zero-cookie device
380
+ // credential on web; SecureStore session blob on native). Built ONCE per
381
+ // provider mount.
377
382
  const authStoreRef = useRef<AuthStateStore | null>(null);
378
383
  if (!authStoreRef.current) {
379
384
  authStoreRef.current = createPlatformAuthStateStore();
@@ -548,19 +553,13 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
548
553
  const markAuthResolvedRef = useRef(markAuthResolved);
549
554
  markAuthResolvedRef.current = markAuthResolved;
550
555
 
551
- // Re-boot indirection: the SessionClient is built in the ref initializer below
552
- // (before `runColdBoot` is defined), but its `onSessionAppeared` must re-run the
553
- // cold boot to self-acquire when a sibling signs in on this device. A ref bridges
554
- // the ordering; it is assigned right after `runColdBoot` is declared.
555
- const runColdBootRef = useRef<(() => Promise<void>) | null>(null);
556
-
557
556
  // Server-authoritative device session client. Built ONCE per `oxyServices`
558
- // instance. `onUnauthenticated` (a device signout-all pushed over the socket,
559
- // or bootstrapped as zero-account) clears the persisted store + local state so
560
- // a reload does not try to restore a session the device no longer has.
561
- // `signedOutSocketAuth` + `onSessionAppeared` power the signed-out sync channel:
562
- // an idle tab joins its `device:<id>` room (web via the `oxy_device` cookie,
563
- // native via the shared device token) and self-acquires on a sibling sign-in.
557
+ // instance. `onUnauthenticated` (a device signout-all pushed over the
558
+ // bearer-authenticated socket, or bootstrapped as zero-account) clears the
559
+ // persisted store + local state so a reload does not try to restore a session
560
+ // the device no longer has. The realtime socket is bearer-only now: a
561
+ // signed-out tab cannot join any device room, so there is no signed-out sync
562
+ // wiring here.
564
563
  const sessionClientPairRef = useRef<ReturnType<typeof createSessionClient> | null>(null);
565
564
  if (!sessionClientPairRef.current) {
566
565
  sessionClientPairRef.current = createSessionClient(
@@ -572,22 +571,6 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
572
571
  logger('Failed to clear local state on remote sign-out', clearError);
573
572
  });
574
573
  },
575
- {
576
- signedOutSocketAuth: async () => {
577
- // Web (`*.oxy.so`): the first-party `oxy_device` cookie rides the
578
- // same-site handshake automatically — just connect.
579
- if (isWebBrowser()) return true;
580
- // Native (no cookie jar): present the shared device token if we have one.
581
- try {
582
- return (await authStore.loadDeviceToken()) ?? false;
583
- } catch {
584
- return false;
585
- }
586
- },
587
- onSessionAppeared: () => {
588
- void runColdBootRef.current?.();
589
- },
590
- },
591
574
  );
592
575
  }
593
576
  const { client: sessionClient, host: sessionClientHost } = sessionClientPairRef.current;
@@ -681,7 +664,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
681
664
  }
682
665
  }
683
666
  await clearSessionState();
684
- // Explicit FULL wipe: also drop the persisted refresh family so a reload
667
+ // Explicit FULL wipe: also drop the persisted device credential so a reload
685
668
  // finds nothing to restore.
686
669
  await authStore.clear();
687
670
  useAccountStore.getState().reset();
@@ -739,10 +722,10 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
739
722
  }, [logger, oxyServices]);
740
723
 
741
724
  // Unified in-session refresh (SDK-owned; every RP inherits it). Installs the
742
- // ONE core refresh handler (rotate the persisted refresh family; native falls
743
- // back to the shared keychain) plus the proactive scheduler that refreshes
744
- // ~60s before expiry and on tab-focus. Replaces the deleted services-local
745
- // `inSessionTokenRefresh` module.
725
+ // ONE core refresh handler (re-mint from the persisted zero-cookie device
726
+ // credential via `POST /session/device/token`) plus the proactive scheduler
727
+ // that refreshes ~60s before expiry and on tab-focus. Replaces the deleted
728
+ // services-local `inSessionTokenRefresh` module.
746
729
  useEffect(() => {
747
730
  const dispose = installAuthRefreshHandler({ oxy: oxyServices, store: authStore });
748
731
  const scheduler = startTokenRefreshScheduler(oxyServices);
@@ -754,32 +737,27 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
754
737
 
755
738
  // ── Session commit funnel ────────────────────────────────────────────────
756
739
  // The single place a session becomes committed: plant tokens, persist the
757
- // rotating refresh family, register the account into the server-authoritative
758
- // device set, and hydrate the full user. Used by the QR device flow
759
- // (`handleWebSession`), password sign-in, 2FA completion, and the cold boot.
740
+ // zero-cookie device credential (`deviceId` + `deviceSecret`), register the
741
+ // account into the server-authoritative device set, and hydrate the full user.
742
+ // Used by the QR device flow (`handleWebSession`), password sign-in, 2FA
743
+ // completion, and the cold boot.
760
744
  const commitSession = useCallback(
761
745
  async (input: CommitInput, options: { activate: boolean }): Promise<void> => {
762
746
  if (input.accessToken) {
763
747
  oxyServices.setTokens(input.accessToken);
764
748
  }
765
749
 
766
- // Persist the durable blob when a rotating refresh token is present
767
- // (trusted-lane sign-ins). The cold boot has usually persisted already;
768
- // this is idempotent. A missing refresh token means the session survives
769
- // reload via the device cookie (web) / shared keychain (native) instead.
770
- if (input.refreshToken && input.userId) {
750
+ // Persist the durable blob when the zero-cookie device credential
751
+ // (`deviceId` + `deviceSecret`) is present. The cold boot has usually
752
+ // persisted already; this is idempotent. Without the credential there is
753
+ // nothing durable to persist and the session lives only for this runtime.
754
+ if (input.deviceId && input.deviceSecret && input.userId) {
771
755
  try {
772
- const deviceToken = (await authStore.loadDeviceToken()) ?? undefined;
773
756
  const next: PersistedAuthState = {
774
757
  sessionId: input.sessionId,
775
- refreshToken: input.refreshToken,
776
758
  userId: input.userId,
777
- ...(deviceToken ? { deviceToken } : {}),
778
- // Phase 2c: persist the deviceId + rotating deviceSecret so the next
779
- // cold boot can restore via the zero-cookie mint. Both are additive —
780
- // absent on cookie-lane sign-ins, which fall back to the refresh family.
781
- ...(input.deviceId ? { deviceId: input.deviceId } : {}),
782
- ...(input.deviceSecret ? { deviceSecret: input.deviceSecret } : {}),
759
+ deviceId: input.deviceId,
760
+ deviceSecret: input.deviceSecret,
783
761
  ...(input.accessToken ? { accessToken: input.accessToken } : {}),
784
762
  ...(input.expiresAt ? { expiresAt: input.expiresAt } : {}),
785
763
  };
@@ -867,9 +845,8 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
867
845
  {
868
846
  sessionId: session.sessionId,
869
847
  accessToken: session.accessToken,
870
- // deviceFlow threads a rotating refresh token + deviceSecret on the
871
- // runtime object even though `SessionLoginResponse` does not type them.
872
- refreshToken: (session as { refreshToken?: string }).refreshToken,
848
+ // deviceFlow threads the rotating deviceSecret on the runtime object
849
+ // even though `SessionLoginResponse` does not type it.
873
850
  deviceSecret: (session as { deviceSecret?: string }).deviceSecret,
874
851
  deviceId: session.deviceId,
875
852
  expiresAt: session.expiresAt,
@@ -944,9 +921,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
944
921
  password: string,
945
922
  opts?: { deviceName?: string; deviceFingerprint?: string },
946
923
  ): Promise<PasswordSignInResult> => {
947
- const deviceToken = (await authStore.loadDeviceToken()) ?? undefined;
948
924
  const result = await oxyServices.passwordSignIn(identifier, password, {
949
- deviceToken,
950
925
  deviceName: opts?.deviceName,
951
926
  deviceFingerprint: opts?.deviceFingerprint,
952
927
  });
@@ -958,7 +933,6 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
958
933
  {
959
934
  sessionId: result.sessionId,
960
935
  accessToken: result.accessToken,
961
- refreshToken: result.refreshToken,
962
936
  deviceSecret: result.deviceSecret,
963
937
  deviceId: result.deviceId,
964
938
  expiresAt: result.expiresAt,
@@ -967,9 +941,9 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
967
941
  },
968
942
  { activate: true },
969
943
  );
970
- return { status: 'ok' };
944
+ return { status: 'ok', ...(result.securityAlert ? { securityAlert: result.securityAlert } : {}) };
971
945
  },
972
- [oxyServices, authStore, commitSession],
946
+ [oxyServices, commitSession],
973
947
  );
974
948
 
975
949
  const completeTwoFactorSignIn = useCallback(
@@ -978,20 +952,17 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
978
952
  token?: string;
979
953
  backupCode?: string;
980
954
  deviceName?: string;
981
- }): Promise<void> => {
982
- const deviceToken = (await authStore.loadDeviceToken()) ?? undefined;
955
+ }): Promise<{ securityAlert?: SecurityAlert }> => {
983
956
  const result = await oxyServices.completeTwoFactorSignIn({
984
957
  loginToken: params.loginToken,
985
958
  token: params.token,
986
959
  backupCode: params.backupCode,
987
- deviceToken,
988
960
  deviceName: params.deviceName,
989
961
  });
990
962
  await commitSession(
991
963
  {
992
964
  sessionId: result.sessionId,
993
965
  accessToken: result.accessToken,
994
- refreshToken: result.refreshToken,
995
966
  deviceSecret: result.deviceSecret,
996
967
  deviceId: result.deviceId,
997
968
  expiresAt: result.expiresAt,
@@ -1000,8 +971,9 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
1000
971
  },
1001
972
  { activate: true },
1002
973
  );
974
+ return { securityAlert: result.securityAlert };
1003
975
  },
1004
- [oxyServices, authStore, commitSession],
976
+ [oxyServices, commitSession],
1005
977
  );
1006
978
 
1007
979
  // ── Cold boot ────────────────────────────────────────────────────────────
@@ -1042,19 +1014,9 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
1042
1014
  markAuthResolvedRef.current();
1043
1015
  },
1044
1016
  onSignedOut: () => {
1017
+ // The realtime socket is bearer-only: a signed-out tab cannot join any
1018
+ // device room, so there is no signed-out socket to start here.
1045
1019
  markAuthResolvedRef.current();
1046
- // Open the signed-out realtime channel so this idle tab joins its
1047
- // `device:<id>` room (web `oxy_device` cookie / native device token)
1048
- // and self-acquires when a sibling signs in. Idempotent, best-effort.
1049
- void sessionClient.start().catch((startError) => {
1050
- if (__DEV__) {
1051
- loggerUtil.debug(
1052
- 'signed-out socket start failed (non-fatal)',
1053
- { component: 'OxyContext', method: 'runColdBoot' },
1054
- startError as unknown,
1055
- );
1056
- }
1057
- });
1058
1020
  },
1059
1021
  onStepError: (id, error) => {
1060
1022
  if (__DEV__) {
@@ -1078,20 +1040,16 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
1078
1040
  // Backstop: resolve on every exit path so the gate can never hang.
1079
1041
  markAuthResolvedRef.current();
1080
1042
  }
1081
- }, [oxyServices, authStore, sessionClient]);
1082
- // Bridge for `onSessionAppeared` (SessionClient) → re-run the cold boot to
1083
- // self-acquire when a sibling signs in on this device while this tab is idle.
1084
- runColdBootRef.current = runColdBoot;
1043
+ }, [oxyServices, authStore]);
1085
1044
 
1086
1045
  useEffect(() => {
1087
1046
  if (initialized) {
1088
1047
  return;
1089
1048
  }
1090
1049
  // IdP mode (`coldBoot={false}`): this provider is NOT the ecosystem session
1091
- // authority, so it never runs the device-first restore and never opens the
1092
- // signed-out device-state socket (`runColdBoot` `onSignedOut` is the sole
1093
- // place that socket starts). Resolve auth immediately as signed out so there
1094
- // is no boot spinner; a deliberate sign-in still commits a normal session.
1050
+ // authority, so it never runs the device-first restore. Resolve auth
1051
+ // immediately as signed out so there is no boot spinner; a deliberate sign-in
1052
+ // still commits a normal session.
1095
1053
  if (!coldBoot) {
1096
1054
  setInitialized(true);
1097
1055
  markAuthResolved();
@@ -1215,7 +1173,6 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
1215
1173
  {
1216
1174
  sessionId: result.sessionId,
1217
1175
  accessToken: result.accessToken,
1218
- refreshToken: (result as { refreshToken?: string }).refreshToken,
1219
1176
  deviceSecret: (result as { deviceSecret?: string }).deviceSecret,
1220
1177
  deviceId: result.deviceId,
1221
1178
  expiresAt: result.expiresAt,
@@ -1379,7 +1336,7 @@ const LOADING_STATE: OxyContextState = {
1379
1336
  getPublicKey: () => Promise.resolve(null),
1380
1337
  signIn: () => rejectMissingProvider<User>(),
1381
1338
  signInWithPassword: () => rejectMissingProvider<PasswordSignInResult>(),
1382
- completeTwoFactorSignIn: () => rejectMissingProvider<void>(),
1339
+ completeTwoFactorSignIn: () => rejectMissingProvider<{ securityAlert?: SecurityAlert }>(),
1383
1340
  handleWebSession: () => rejectMissingProvider<void>(),
1384
1341
  logout: () => rejectMissingProvider<void>(),
1385
1342
  logoutAll: () => rejectMissingProvider<void>(),