@open-webapp/drive-sync 0.5.0 → 0.5.2
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/dist/testing/gisFake.d.ts +6 -0
- package/dist/testing/gisFake.js +17 -0
- package/dist/token.js +33 -3
- package/package.json +1 -1
|
@@ -56,6 +56,12 @@ export interface GisFake {
|
|
|
56
56
|
* for a blocked or dismissed popup, which never reaches `callback`.
|
|
57
57
|
*/
|
|
58
58
|
queuePopupError(type: string): void;
|
|
59
|
+
/**
|
|
60
|
+
* Queue a `popup_closed` error_callback that is followed, after `delayMs`,
|
|
61
|
+
* by a successful token `callback` — reproducing the real GIS race where
|
|
62
|
+
* the popup-closed poll fires before the success message is delivered.
|
|
63
|
+
*/
|
|
64
|
+
queuePopupClosedRace(response: GisTokenResponse, delayMs: number): void;
|
|
59
65
|
/** Stub `window.google.accounts.oauth2.initTokenClient` with this fake. */
|
|
60
66
|
install(): void;
|
|
61
67
|
/** Remove the stub installed by `install()`, restoring prior state. */
|
package/dist/testing/gisFake.js
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
export function createGisFake() {
|
|
17
17
|
const responseQueue = [];
|
|
18
18
|
const popupErrorQueue = [];
|
|
19
|
+
const popupClosedRaceQueue = [];
|
|
19
20
|
const calls = [];
|
|
20
21
|
let previousGoogle;
|
|
21
22
|
let hadGoogle = false;
|
|
@@ -33,6 +34,18 @@ export function createGisFake() {
|
|
|
33
34
|
const hint = overrideConfig?.hint ?? config.hint;
|
|
34
35
|
const scope = overrideConfig?.scope ?? config.scope ?? '';
|
|
35
36
|
calls.push({ prompt, hint, scope });
|
|
37
|
+
const popupClosedRace = popupClosedRaceQueue.shift();
|
|
38
|
+
if (popupClosedRace) {
|
|
39
|
+
const errorCallback = config.error_callback;
|
|
40
|
+
const callback = config.callback;
|
|
41
|
+
queueMicrotask(() => {
|
|
42
|
+
errorCallback?.({ type: 'popup_closed' });
|
|
43
|
+
});
|
|
44
|
+
setTimeout(() => {
|
|
45
|
+
callback?.(popupClosedRace.response);
|
|
46
|
+
}, popupClosedRace.delayMs);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
36
49
|
const popupError = popupErrorQueue.shift();
|
|
37
50
|
if (popupError) {
|
|
38
51
|
const errorCallback = config.error_callback;
|
|
@@ -66,6 +79,9 @@ export function createGisFake() {
|
|
|
66
79
|
queuePopupError(type) {
|
|
67
80
|
popupErrorQueue.push(type);
|
|
68
81
|
},
|
|
82
|
+
queuePopupClosedRace(response, delayMs) {
|
|
83
|
+
popupClosedRaceQueue.push({ response, delayMs });
|
|
84
|
+
},
|
|
69
85
|
install() {
|
|
70
86
|
const w = globalThis;
|
|
71
87
|
hadGoogle = Object.prototype.hasOwnProperty.call(w, 'google');
|
|
@@ -93,6 +109,7 @@ export function createGisFake() {
|
|
|
93
109
|
reset() {
|
|
94
110
|
responseQueue.length = 0;
|
|
95
111
|
popupErrorQueue.length = 0;
|
|
112
|
+
popupClosedRaceQueue.length = 0;
|
|
96
113
|
calls.length = 0;
|
|
97
114
|
},
|
|
98
115
|
};
|
package/dist/token.js
CHANGED
|
@@ -2,6 +2,14 @@ import { setToken, getToken } from './storage.js';
|
|
|
2
2
|
import { waitForGoogleIdentityServices } from './gis.js';
|
|
3
3
|
import { NeedsReauthError } from './errors.js';
|
|
4
4
|
import { createBroadcast } from './broadcast.js';
|
|
5
|
+
/**
|
|
6
|
+
* How long to wait, after GIS reports `popup_closed`, for the success
|
|
7
|
+
* `callback` to still win the race before treating it as a real failure.
|
|
8
|
+
* 300ms proved too tight in production: on a real network round-trip the
|
|
9
|
+
* success token can arrive well after GIS's popup-closed poll fires,
|
|
10
|
+
* causing genuinely successful sign-ins to be reported as NeedsReauthError.
|
|
11
|
+
*/
|
|
12
|
+
const POPUP_CLOSED_GRACE_MS = 2000;
|
|
5
13
|
/**
|
|
6
14
|
* Persists a freshly-acquired GIS token response as a StoredToken, deriving
|
|
7
15
|
* expiresAt from the response's own expires_in (never hardcoded) and
|
|
@@ -104,10 +112,14 @@ async function acquireTokenUncoalesced(opts) {
|
|
|
104
112
|
// These resolve/reject are captured in THIS call's closure only, never
|
|
105
113
|
// stored on a module-level variable, so a second concurrent call cannot
|
|
106
114
|
// clobber the first caller's promise.
|
|
115
|
+
let settled = false;
|
|
107
116
|
const client = initTokenClient({
|
|
108
117
|
client_id: opts.clientId,
|
|
109
118
|
scope: opts.scopes.join(' '),
|
|
110
119
|
callback: (res) => {
|
|
120
|
+
if (settled)
|
|
121
|
+
return;
|
|
122
|
+
settled = true;
|
|
111
123
|
if (res.error) {
|
|
112
124
|
reject(new Error(`GIS token request failed: ${res.error}`));
|
|
113
125
|
return;
|
|
@@ -119,11 +131,29 @@ async function acquireTokenUncoalesced(opts) {
|
|
|
119
131
|
// the promise below would stay pending forever and every awaiting
|
|
120
132
|
// Drive call would hang until the caller's own timeout (if any).
|
|
121
133
|
error_callback: (err) => {
|
|
134
|
+
if (settled)
|
|
135
|
+
return;
|
|
136
|
+
if (err?.type === 'popup_closed') {
|
|
137
|
+
// GIS closes the popup itself at the end of a SUCCESSFUL flow too,
|
|
138
|
+
// and its popup-closed poll can win the race against delivery of
|
|
139
|
+
// the success token, firing this error_callback even though the
|
|
140
|
+
// token is already on its way via `callback`. Give `callback` a
|
|
141
|
+
// brief grace window to settle the promise first, so a completed
|
|
142
|
+
// OAuth flow doesn't get reported as a failed one.
|
|
143
|
+
setTimeout(() => {
|
|
144
|
+
if (settled)
|
|
145
|
+
return;
|
|
146
|
+
settled = true;
|
|
147
|
+
reject(new NeedsReauthError('Google sign-in popup was closed before completing', {
|
|
148
|
+
reason: 'popup_closed',
|
|
149
|
+
}));
|
|
150
|
+
}, POPUP_CLOSED_GRACE_MS);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
settled = true;
|
|
122
154
|
reject(new NeedsReauthError(err?.type === 'popup_failed_to_open'
|
|
123
155
|
? 'Google sign-in popup was blocked by the browser'
|
|
124
|
-
: err?.type
|
|
125
|
-
? 'Google sign-in popup was closed before completing'
|
|
126
|
-
: `Google sign-in failed: ${err?.type ?? 'unknown error'}`, { reason: err?.type ?? 'gis_error' }));
|
|
156
|
+
: `Google sign-in failed: ${err?.type ?? 'unknown error'}`, { reason: err?.type ?? 'gis_error' }));
|
|
127
157
|
},
|
|
128
158
|
});
|
|
129
159
|
opts.logger?.debug('drive-sync: requesting access token', {
|