@oxyhq/services 22.12.3 → 22.12.5

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 (40) hide show
  1. package/lib/commonjs/ui/components/oauthNavigation.js +11 -1
  2. package/lib/commonjs/ui/components/oauthNavigation.js.map +1 -1
  3. package/lib/commonjs/ui/screens/CreateAccountScreen.js +5 -0
  4. package/lib/commonjs/ui/screens/CreateAccountScreen.js.map +1 -1
  5. package/lib/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.js +14 -3
  6. package/lib/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.js.map +1 -1
  7. package/lib/commonjs/ui/utils/crossOriginRestore.js +5 -1
  8. package/lib/commonjs/ui/utils/crossOriginRestore.js.map +1 -1
  9. package/lib/commonjs/ui/utils/oauthReturn.js +50 -4
  10. package/lib/commonjs/ui/utils/oauthReturn.js.map +1 -1
  11. package/lib/module/ui/components/oauthNavigation.js +12 -2
  12. package/lib/module/ui/components/oauthNavigation.js.map +1 -1
  13. package/lib/module/ui/screens/CreateAccountScreen.js +5 -0
  14. package/lib/module/ui/screens/CreateAccountScreen.js.map +1 -1
  15. package/lib/module/ui/screens/fileManagement/hooks/useFileUploadState.js +14 -3
  16. package/lib/module/ui/screens/fileManagement/hooks/useFileUploadState.js.map +1 -1
  17. package/lib/module/ui/utils/crossOriginRestore.js +5 -1
  18. package/lib/module/ui/utils/crossOriginRestore.js.map +1 -1
  19. package/lib/module/ui/utils/oauthReturn.js +50 -5
  20. package/lib/module/ui/utils/oauthReturn.js.map +1 -1
  21. package/lib/typescript/commonjs/ui/components/oauthNavigation.d.ts +7 -0
  22. package/lib/typescript/commonjs/ui/components/oauthNavigation.d.ts.map +1 -1
  23. package/lib/typescript/commonjs/ui/screens/CreateAccountScreen.d.ts.map +1 -1
  24. package/lib/typescript/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.d.ts.map +1 -1
  25. package/lib/typescript/commonjs/ui/utils/crossOriginRestore.d.ts.map +1 -1
  26. package/lib/typescript/commonjs/ui/utils/oauthReturn.d.ts +19 -0
  27. package/lib/typescript/commonjs/ui/utils/oauthReturn.d.ts.map +1 -1
  28. package/lib/typescript/module/ui/components/oauthNavigation.d.ts +7 -0
  29. package/lib/typescript/module/ui/components/oauthNavigation.d.ts.map +1 -1
  30. package/lib/typescript/module/ui/screens/CreateAccountScreen.d.ts.map +1 -1
  31. package/lib/typescript/module/ui/screens/fileManagement/hooks/useFileUploadState.d.ts.map +1 -1
  32. package/lib/typescript/module/ui/utils/crossOriginRestore.d.ts.map +1 -1
  33. package/lib/typescript/module/ui/utils/oauthReturn.d.ts +19 -0
  34. package/lib/typescript/module/ui/utils/oauthReturn.d.ts.map +1 -1
  35. package/package.json +5 -5
  36. package/src/ui/components/oauthNavigation.ts +12 -2
  37. package/src/ui/screens/CreateAccountScreen.tsx +5 -0
  38. package/src/ui/screens/fileManagement/hooks/useFileUploadState.ts +15 -3
  39. package/src/ui/utils/crossOriginRestore.ts +5 -1
  40. package/src/ui/utils/oauthReturn.ts +51 -4
@@ -1,6 +1,7 @@
1
1
  import type { OxyServices } from '@oxyhq/core';
2
2
  import {
3
3
  clearOAuthHandshake,
4
+ consumeOAuthReturnPath,
4
5
  logger,
5
6
  normalizeOAuthRedirectUri,
6
7
  readOAuthHandshake,
@@ -53,8 +54,10 @@ export async function tryCompleteOAuthReturn(opts: {
53
54
  logger.warn('OAuth return ignored: missing or mismatched handshake', {
54
55
  component: 'oauthReturn',
55
56
  });
56
- clearOAuthHandshake();
57
+ // Consume the return path before clearing the handshake — clearOAuthHandshake
58
+ // drops the persisted path along with the PKCE keys.
57
59
  stripOAuthParamsFromUrl();
60
+ clearOAuthHandshake();
58
61
  return false;
59
62
  }
60
63
 
@@ -70,8 +73,9 @@ export async function tryCompleteOAuthReturn(opts: {
70
73
  codeVerifier: handshake.codeVerifier,
71
74
  });
72
75
  // Strip OAuth params before commit so a stale `?code=` cannot re-enter the exchange loop.
73
- clearOAuthHandshake();
76
+ // Read the return path first — clearOAuthHandshake drops it with the PKCE keys.
74
77
  stripOAuthParamsFromUrl();
78
+ clearOAuthHandshake();
75
79
  await opts.commitSession({
76
80
  sessionId: result.sessionId,
77
81
  accessToken: result.accessToken,
@@ -84,12 +88,55 @@ export async function tryCompleteOAuthReturn(opts: {
84
88
  return true;
85
89
  } catch (error) {
86
90
  logger.warn('OAuth return exchange failed', { component: 'oauthReturn' }, error);
87
- clearOAuthHandshake();
88
91
  stripOAuthParamsFromUrl();
92
+ clearOAuthHandshake();
89
93
  return false;
90
94
  }
91
95
  }
92
96
 
97
+ /**
98
+ * Rewrite the address bar after an authorize round trip, and tell the router.
99
+ *
100
+ * Prefers the page the visit started on (see `persistOAuthReturnPath`) over the
101
+ * bare origin the IdP redirected to, falling back to `fallbackUrl` when nothing
102
+ * was recorded.
103
+ *
104
+ * The `popstate` dispatch is the load-bearing part. `history.replaceState`
105
+ * fires no event, and cold boot runs *inside* the mounted app — by the time
106
+ * this executes, a history-based router (React Router, Vue Router, …) has
107
+ * already read `location` and rendered the route for `/`. Without a
108
+ * notification the address bar would show the restored deep link while the page
109
+ * still displayed the home page. `popstate` is the event the History API
110
+ * defines for exactly this, and it is what those routers subscribe to.
111
+ *
112
+ * Only dispatched when the URL actually changed, so the common no-op case does
113
+ * not push a spurious event at every listener on the page.
114
+ */
115
+ export function replaceUrlAfterOAuthReturn(fallbackUrl: string): void {
116
+ const location = (globalThis as { location?: Location }).location;
117
+ const history = (globalThis as { history?: History }).history;
118
+ if (!location || !history?.replaceState) return;
119
+
120
+ const current = `${location.pathname}${location.search}${location.hash}`;
121
+ const target = consumeOAuthReturnPath() ?? fallbackUrl;
122
+ history.replaceState(history.state, '', target);
123
+ if (target === current) return;
124
+
125
+ const dispatch = (globalThis as { dispatchEvent?: (event: Event) => boolean }).dispatchEvent;
126
+ if (typeof dispatch !== 'function') return;
127
+ try {
128
+ // `PopStateEvent` is web-only; plain `Event` is a good enough carrier for
129
+ // any host that lacks it (routers read `location`, not `event.state`).
130
+ const event =
131
+ typeof PopStateEvent === 'function'
132
+ ? new PopStateEvent('popstate', { state: history.state })
133
+ : new Event('popstate');
134
+ dispatch.call(globalThis, event);
135
+ } catch (error) {
136
+ logger.warn('Could not notify router of restored URL', { component: 'oauthReturn' }, error);
137
+ }
138
+ }
139
+
93
140
  function stripOAuthParamsFromUrl(): void {
94
141
  const location = (globalThis as { location?: Location; history?: History }).location;
95
142
  const history = (globalThis as { history?: History }).history;
@@ -100,7 +147,7 @@ function stripOAuthParamsFromUrl(): void {
100
147
  url.searchParams.delete('error');
101
148
  url.searchParams.delete('error_description');
102
149
  url.searchParams.delete('hub_sync');
103
- history.replaceState(history.state, '', `${url.pathname}${url.search}${url.hash}`);
150
+ replaceUrlAfterOAuthReturn(`${url.pathname}${url.search}${url.hash}`);
104
151
  }
105
152
 
106
153
  /**