@youkno/edge-cli 1.21.290 → 1.21.292
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/lib/auth.js +39 -2
- package/package.json +1 -1
- package/src/lib/auth.ts +40 -2
package/dist/lib/auth.js
CHANGED
|
@@ -151,6 +151,38 @@ async function authRefreshToken(cfg, refreshToken) {
|
|
|
151
151
|
async function authLogoutRefreshToken(cfg, refreshToken) {
|
|
152
152
|
await requestTokenEndpoint(cfg, "logout", { refreshToken });
|
|
153
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Turn whatever the console callback handed us into first-party tokens.
|
|
156
|
+
*
|
|
157
|
+
* The console redirects with `?jwt=<firebase id token>`, and parseCallbackQuery reads `jwt` as
|
|
158
|
+
* accessToken -- so the callback never populates `firebaseToken` and we used to store that Firebase
|
|
159
|
+
* token raw. Two consequences, both bad: a Firebase ID token lives exactly one hour and Google
|
|
160
|
+
* decides that, not us; and the callback carries no refresh token, so every refresh path in
|
|
161
|
+
* resolveAccessToken is guarded off and the session cannot recover. The CLI simply died after an
|
|
162
|
+
* hour with "Firebase ID token has expired" and the only cure was logging in again.
|
|
163
|
+
*
|
|
164
|
+
* Exchanging it gets a first-party access token plus a refresh token (30 days by default), which
|
|
165
|
+
* resolveAccessToken already rotates automatically.
|
|
166
|
+
*
|
|
167
|
+
* Falls back to storing the token as-is when the exchange is unavailable -- an older server without
|
|
168
|
+
* /auth/exchange, or one with auth.issueFirstPartyTokens disabled -- so login still works there,
|
|
169
|
+
* with the same one-hour life it always had.
|
|
170
|
+
*/
|
|
171
|
+
async function exchangeCallbackToken(cfg, callbackToken) {
|
|
172
|
+
try {
|
|
173
|
+
return await authExchangeFirebaseToken(cfg, callbackToken);
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
177
|
+
process.stdout.write(`Warning: could not exchange for a first-party token (${reason}).\n` +
|
|
178
|
+
`Falling back to the raw callback token, which expires in about an hour and cannot be refreshed.\n`);
|
|
179
|
+
return {
|
|
180
|
+
accessToken: callbackToken,
|
|
181
|
+
accessTokenExpiresInSec: 3600,
|
|
182
|
+
refreshTokenExpiresInSec: 0
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
}
|
|
154
186
|
async function logout(cfg, email) {
|
|
155
187
|
const db = readDb();
|
|
156
188
|
const scope = getScope(db, cfg);
|
|
@@ -344,14 +376,19 @@ async function login(cfg) {
|
|
|
344
376
|
if (parsed.firebaseToken) {
|
|
345
377
|
tokenResp = await authExchangeFirebaseToken(cfg, parsed.firebaseToken);
|
|
346
378
|
}
|
|
347
|
-
else if (parsed.accessToken) {
|
|
379
|
+
else if (parsed.accessToken && parsed.refreshToken) {
|
|
380
|
+
// Already a first-party pair; nothing to exchange.
|
|
348
381
|
tokenResp = {
|
|
349
382
|
accessToken: parsed.accessToken,
|
|
350
383
|
refreshToken: parsed.refreshToken,
|
|
351
384
|
accessTokenExpiresInSec: 3600,
|
|
352
|
-
refreshTokenExpiresInSec:
|
|
385
|
+
refreshTokenExpiresInSec: 2_592_000
|
|
353
386
|
};
|
|
354
387
|
}
|
|
388
|
+
else if (parsed.accessToken) {
|
|
389
|
+
// No refresh token alongside it means this is the console's `?jwt=` Firebase token.
|
|
390
|
+
tokenResp = await exchangeCallbackToken(cfg, parsed.accessToken);
|
|
391
|
+
}
|
|
355
392
|
if (!tokenResp?.accessToken) {
|
|
356
393
|
throw new Error("Authentication callback did not include usable token data.");
|
|
357
394
|
}
|
package/package.json
CHANGED
package/src/lib/auth.ts
CHANGED
|
@@ -187,6 +187,40 @@ async function authLogoutRefreshToken(cfg: EffectiveConfig, refreshToken: string
|
|
|
187
187
|
await requestTokenEndpoint(cfg, "logout", { refreshToken });
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
+
/**
|
|
191
|
+
* Turn whatever the console callback handed us into first-party tokens.
|
|
192
|
+
*
|
|
193
|
+
* The console redirects with `?jwt=<firebase id token>`, and parseCallbackQuery reads `jwt` as
|
|
194
|
+
* accessToken -- so the callback never populates `firebaseToken` and we used to store that Firebase
|
|
195
|
+
* token raw. Two consequences, both bad: a Firebase ID token lives exactly one hour and Google
|
|
196
|
+
* decides that, not us; and the callback carries no refresh token, so every refresh path in
|
|
197
|
+
* resolveAccessToken is guarded off and the session cannot recover. The CLI simply died after an
|
|
198
|
+
* hour with "Firebase ID token has expired" and the only cure was logging in again.
|
|
199
|
+
*
|
|
200
|
+
* Exchanging it gets a first-party access token plus a refresh token (30 days by default), which
|
|
201
|
+
* resolveAccessToken already rotates automatically.
|
|
202
|
+
*
|
|
203
|
+
* Falls back to storing the token as-is when the exchange is unavailable -- an older server without
|
|
204
|
+
* /auth/exchange, or one with auth.issueFirstPartyTokens disabled -- so login still works there,
|
|
205
|
+
* with the same one-hour life it always had.
|
|
206
|
+
*/
|
|
207
|
+
async function exchangeCallbackToken(cfg: EffectiveConfig, callbackToken: string): Promise<TokenResponse> {
|
|
208
|
+
try {
|
|
209
|
+
return await authExchangeFirebaseToken(cfg, callbackToken);
|
|
210
|
+
} catch (err) {
|
|
211
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
212
|
+
process.stdout.write(
|
|
213
|
+
`Warning: could not exchange for a first-party token (${reason}).\n` +
|
|
214
|
+
`Falling back to the raw callback token, which expires in about an hour and cannot be refreshed.\n`
|
|
215
|
+
);
|
|
216
|
+
return {
|
|
217
|
+
accessToken: callbackToken,
|
|
218
|
+
accessTokenExpiresInSec: 3600,
|
|
219
|
+
refreshTokenExpiresInSec: 0
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
190
224
|
export async function logout(cfg: EffectiveConfig, email?: string): Promise<string> {
|
|
191
225
|
const db = readDb();
|
|
192
226
|
const scope = getScope(db, cfg);
|
|
@@ -404,13 +438,17 @@ export async function login(cfg: EffectiveConfig): Promise<string> {
|
|
|
404
438
|
let tokenResp: TokenResponse | undefined;
|
|
405
439
|
if (parsed.firebaseToken) {
|
|
406
440
|
tokenResp = await authExchangeFirebaseToken(cfg, parsed.firebaseToken);
|
|
407
|
-
} else if (parsed.accessToken) {
|
|
441
|
+
} else if (parsed.accessToken && parsed.refreshToken) {
|
|
442
|
+
// Already a first-party pair; nothing to exchange.
|
|
408
443
|
tokenResp = {
|
|
409
444
|
accessToken: parsed.accessToken,
|
|
410
445
|
refreshToken: parsed.refreshToken,
|
|
411
446
|
accessTokenExpiresInSec: 3600,
|
|
412
|
-
refreshTokenExpiresInSec:
|
|
447
|
+
refreshTokenExpiresInSec: 2_592_000
|
|
413
448
|
};
|
|
449
|
+
} else if (parsed.accessToken) {
|
|
450
|
+
// No refresh token alongside it means this is the console's `?jwt=` Firebase token.
|
|
451
|
+
tokenResp = await exchangeCallbackToken(cfg, parsed.accessToken);
|
|
414
452
|
}
|
|
415
453
|
|
|
416
454
|
if (!tokenResp?.accessToken) {
|