@spfn/auth 0.3.0-beta.25 → 0.3.0-beta.27

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.
@@ -591,6 +591,12 @@ declare function completePasswordResetService(params: CompletePasswordResetParam
591
591
  * refuses the account's live records too, as `denied`, so they land in that row
592
592
  * of the table. See `denyAllActiveByUserId`; the three callers are the three
593
593
  * places that revoke every key at once.
594
+ *
595
+ * A poll may ask to wait (`waitMillis`). `waitForDeviceAuthAnswerService` then
596
+ * holds a pending record's request — before the route's transaction opens — until
597
+ * something answers it, the wait runs out, or the device hangs up, and only after
598
+ * that is the table above applied. A long poll answers exactly what a short poll
599
+ * sent at the same moment would, only sooner.
594
600
  */
595
601
 
596
602
  interface StartDeviceAuthParams {
@@ -645,10 +651,21 @@ interface PollDeviceAuthParams {
645
651
  * to run a WebAuthn ceremony in needs.
646
652
  */
647
653
  webProxy?: boolean;
654
+ /**
655
+ * How long this request already waited on the server, from the long-poll
656
+ * middleware. A pending answer subtracts it from the interval: the device
657
+ * has done that much of its waiting here.
658
+ */
659
+ waitedMillis?: number;
648
660
  }
649
661
  /** Nobody has answered yet. Not an error — the waiting device waits. */
650
662
  interface DeviceAuthPendingResult {
651
663
  status: 'pending';
664
+ /**
665
+ * How long to wait before polling again, less what the request already
666
+ * waited on the server — 0 once a long poll has waited at least the interval,
667
+ * so the device asks again at once.
668
+ */
652
669
  intervalMillis: number;
653
670
  }
654
671
  /**
@@ -3341,6 +3358,7 @@ declare const mainAuthRouter: _spfn_core_route.Router<{
3341
3358
  pollDeviceAuth: _spfn_core_route.RouteDef<{
3342
3359
  body: _sinclair_typebox.TObject<{
3343
3360
  deviceCode: _sinclair_typebox.TString;
3361
+ waitMillis: _sinclair_typebox.TOptional<_sinclair_typebox.TInteger>;
3344
3362
  }>;
3345
3363
  }, {}, {
3346
3364
  status: "pending";
@@ -577,9 +577,9 @@ var loginRegisterInterceptor = {
577
577
  ctx.body.oldKeyId = oldKeyId;
578
578
  }
579
579
  delete ctx.body.remember;
580
- ctx.metadata.privateKey = keyPair.privateKey;
581
- ctx.metadata.keyId = keyPair.keyId;
582
- ctx.metadata.algorithm = keyPair.algorithm;
580
+ ctx.metadata.newPrivateKey = keyPair.privateKey;
581
+ ctx.metadata.newKeyId = keyPair.keyId;
582
+ ctx.metadata.newAlgorithm = keyPair.algorithm;
583
583
  ctx.metadata.remember = remember;
584
584
  await next();
585
585
  },
@@ -598,9 +598,9 @@ var loginRegisterInterceptor = {
598
598
  const ttl = getSessionTtl(ctx.metadata.remember);
599
599
  const sessionData = {
600
600
  userId: userData.userId,
601
- privateKey: ctx.metadata.privateKey,
602
- keyId: ctx.metadata.keyId,
603
- algorithm: ctx.metadata.algorithm,
601
+ privateKey: ctx.metadata.newPrivateKey,
602
+ keyId: ctx.metadata.newKeyId,
603
+ algorithm: ctx.metadata.newAlgorithm,
604
604
  ...bindingSessionFields(userData, ctx.request.headers["user-agent"])
605
605
  };
606
606
  const sealed = await sealSession(sessionData, ttl);
@@ -617,7 +617,7 @@ var loginRegisterInterceptor = {
617
617
  });
618
618
  ctx.setCookies.push({
619
619
  name: COOKIE_NAMES.SESSION_KEY_ID,
620
- value: ctx.metadata.keyId,
620
+ value: ctx.metadata.newKeyId,
621
621
  options: {
622
622
  httpOnly: true,
623
623
  secure: cookieSecure,
@@ -626,7 +626,7 @@ var loginRegisterInterceptor = {
626
626
  path: "/"
627
627
  }
628
628
  });
629
- await pushCsrfCookie(ctx.setCookies, ctx.metadata.keyId, ttl);
629
+ await pushCsrfCookie(ctx.setCookies, ctx.metadata.newKeyId, ttl);
630
630
  } catch (error) {
631
631
  const err = error;
632
632
  authLogger.interceptor.login.error("Failed to save session", err);
@@ -693,11 +693,11 @@ function challengeSecretOf(body) {
693
693
  return typeof secret === "string" ? secret : void 0;
694
694
  }
695
695
  async function pendingKeyFor(ctx) {
696
- if (ctx.metadata.privateKey && ctx.metadata.keyId) {
696
+ if (ctx.metadata.newPrivateKey && ctx.metadata.newKeyId) {
697
697
  return {
698
- privateKey: ctx.metadata.privateKey,
699
- keyId: ctx.metadata.keyId,
700
- algorithm: ctx.metadata.algorithm
698
+ privateKey: ctx.metadata.newPrivateKey,
699
+ keyId: ctx.metadata.newKeyId,
700
+ algorithm: ctx.metadata.newAlgorithm
701
701
  };
702
702
  }
703
703
  const oauthPending = ctx.cookies.get(COOKIE_NAMES.OAUTH_PENDING);
@@ -825,6 +825,9 @@ function refuseAsContextChanged(ctx) {
825
825
  function isKeyExpiredRefusal(body) {
826
826
  return body?.__type === "KeyExpiredError";
827
827
  }
828
+ function sessionQueued(setCookies) {
829
+ return setCookies.some((cookie) => cookie.name === COOKIE_NAMES.SESSION);
830
+ }
828
831
  var generalAuthInterceptor = {
829
832
  pathPattern: "*",
830
833
  // Match all paths, filter by requiresAuth()
@@ -953,7 +956,7 @@ var generalAuthInterceptor = {
953
956
  }
954
957
  });
955
958
  pushCsrfCookieRemoval(ctx.setCookies);
956
- } else if (ctx.metadata.refreshSession && ctx.response.status === 200) {
959
+ } else if (ctx.metadata.refreshSession && ctx.response.status === 200 && !sessionQueued(ctx.setCookies)) {
957
960
  try {
958
961
  const sessionData = ctx.metadata.sessionData;
959
962
  const ttl = getSessionTtl();