@oxyhq/services 10.0.0 → 10.1.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 (38) hide show
  1. package/lib/commonjs/ui/components/SignInModal.js +12 -9
  2. package/lib/commonjs/ui/components/SignInModal.js.map +1 -1
  3. package/lib/commonjs/ui/context/OxyContext.js +5 -1
  4. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  5. package/lib/commonjs/ui/screens/OxyAuthScreen.js +51 -20
  6. package/lib/commonjs/ui/screens/OxyAuthScreen.js.map +1 -1
  7. package/lib/commonjs/utils/deviceFlowSignIn.js +55 -0
  8. package/lib/commonjs/utils/deviceFlowSignIn.js.map +1 -0
  9. package/lib/module/ui/components/SignInModal.js +12 -9
  10. package/lib/module/ui/components/SignInModal.js.map +1 -1
  11. package/lib/module/ui/context/OxyContext.js +5 -1
  12. package/lib/module/ui/context/OxyContext.js.map +1 -1
  13. package/lib/module/ui/screens/OxyAuthScreen.js +51 -20
  14. package/lib/module/ui/screens/OxyAuthScreen.js.map +1 -1
  15. package/lib/module/utils/deviceFlowSignIn.js +51 -0
  16. package/lib/module/utils/deviceFlowSignIn.js.map +1 -0
  17. package/lib/typescript/commonjs/ui/components/SignInModal.d.ts.map +1 -1
  18. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +1 -1
  19. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
  20. package/lib/typescript/commonjs/ui/hooks/mutations/useServicesMutations.d.ts +1 -1
  21. package/lib/typescript/commonjs/ui/hooks/mutations/useServicesMutations.d.ts.map +1 -1
  22. package/lib/typescript/commonjs/ui/screens/OxyAuthScreen.d.ts.map +1 -1
  23. package/lib/typescript/commonjs/utils/deviceFlowSignIn.d.ts +61 -0
  24. package/lib/typescript/commonjs/utils/deviceFlowSignIn.d.ts.map +1 -0
  25. package/lib/typescript/module/ui/components/SignInModal.d.ts.map +1 -1
  26. package/lib/typescript/module/ui/context/OxyContext.d.ts +1 -1
  27. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  28. package/lib/typescript/module/ui/hooks/mutations/useServicesMutations.d.ts +1 -1
  29. package/lib/typescript/module/ui/hooks/mutations/useServicesMutations.d.ts.map +1 -1
  30. package/lib/typescript/module/ui/screens/OxyAuthScreen.d.ts.map +1 -1
  31. package/lib/typescript/module/utils/deviceFlowSignIn.d.ts +61 -0
  32. package/lib/typescript/module/utils/deviceFlowSignIn.d.ts.map +1 -0
  33. package/package.json +1 -1
  34. package/src/ui/components/SignInModal.tsx +12 -9
  35. package/src/ui/context/OxyContext.tsx +8 -4
  36. package/src/ui/screens/OxyAuthScreen.tsx +52 -20
  37. package/src/utils/__tests__/deviceFlowSignIn.test.ts +104 -0
  38. package/src/utils/deviceFlowSignIn.ts +76 -0
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Shared, pure orchestration for completing the cross-app device-flow sign-in
3
+ * (the QR-code / "Open Oxy Auth" path used on native and web).
4
+ *
5
+ * THE BUG THIS FIXES (native): once another authenticated device approves the
6
+ * pending AuthSession, the originating client is notified (socket / poll /
7
+ * deep-link) with the authorized `sessionId`. Before any session-management
8
+ * code can use it, the client MUST exchange the secret 128-bit `sessionToken`
9
+ * (held only by this client, generated for THIS flow) for the first access
10
+ * token via `claimSessionByToken` — the device-flow equivalent of OAuth's
11
+ * code-for-token exchange (RFC 8628 §3.4).
12
+ *
13
+ * Skipping the claim leaves the SDK with NO bearer token, so the subsequent
14
+ * `switchSession` -> `getTokenBySession` (`GET /session/token/:id`) call 401s
15
+ * against the C1-hardened API: the session is authorized server-side but the
16
+ * app never becomes authenticated and the UI sits "Waiting for
17
+ * authorization..." forever. The web `SignInModal` already claimed first; the
18
+ * native `OxyAuthScreen` did not. Consolidating the claim→switch sequence here
19
+ * keeps both paths identical and unit-testable, and prevents future drift.
20
+ */
21
+
22
+ import type { User } from '@oxyhq/core';
23
+
24
+ /**
25
+ * The minimal `OxyServices` surface this orchestration needs. Kept as a
26
+ * structural type (rather than importing the full client) so the helper is
27
+ * trivially unit-testable with a stub and never pulls the RN/Expo runtime into
28
+ * a test bundle.
29
+ */
30
+ export interface DeviceFlowClient {
31
+ /**
32
+ * Exchange the device-flow `sessionToken` for the first access + refresh
33
+ * token, planting them on the client. Single-use; replay is rejected by the
34
+ * API. No bearer required — the high-entropy `sessionToken` IS the credential.
35
+ */
36
+ claimSessionByToken: (sessionToken: string) => Promise<unknown>;
37
+ }
38
+
39
+ export interface CompleteDeviceFlowSignInOptions {
40
+ /** The OxyServices client (or any object exposing `claimSessionByToken`). */
41
+ oxyServices: DeviceFlowClient;
42
+ /** The authorized device session id, delivered by the socket / poll / link. */
43
+ sessionId: string;
44
+ /**
45
+ * The secret `sessionToken` generated for THIS flow and registered via
46
+ * `POST /auth/session/create`. Required to claim the first access token.
47
+ */
48
+ sessionToken: string;
49
+ /**
50
+ * The session-management `switchSession` from `useOxy()`. Hydrates the
51
+ * activated session (validates, fetches the user, persists, updates state).
52
+ * Runs AFTER the bearer is planted so its bearer-protected calls succeed.
53
+ */
54
+ switchSession: (sessionId: string) => Promise<User>;
55
+ }
56
+
57
+ /**
58
+ * Complete a device-flow sign-in: claim the first access token with the secret
59
+ * `sessionToken` (planting the bearer), then hydrate the session via
60
+ * `switchSession`. Returns the authenticated user.
61
+ *
62
+ * Throws if either the claim or the switch fails; callers surface a retry UI.
63
+ */
64
+ export async function completeDeviceFlowSignIn({
65
+ oxyServices,
66
+ sessionId,
67
+ sessionToken,
68
+ switchSession,
69
+ }: CompleteDeviceFlowSignInOptions): Promise<User> {
70
+ // 1) Plant the bearer + refresh tokens. Without this the bearer-protected
71
+ // `getTokenBySession` inside `switchSession` 401s (the native regression).
72
+ await oxyServices.claimSessionByToken(sessionToken);
73
+
74
+ // 2) Bearer is now planted — hydrate the session through the normal path.
75
+ return switchSession(sessionId);
76
+ }