@spfn/auth 0.3.0-beta.16 → 0.3.0-beta.17

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/server.js CHANGED
@@ -10641,6 +10641,9 @@ async function changePasswordService(params) {
10641
10641
  import { env as env9 } from "@spfn/auth/config";
10642
10642
  import { InvalidSignupLinkError, InvalidSignupSetupSessionError } from "@spfn/auth/errors";
10643
10643
  init_repositories();
10644
+
10645
+ // src/lib/return-path.ts
10646
+ var URL_STRIPPED_CHARACTER = /[\t\n\r]/;
10644
10647
  function isSafeReturnPath(returnPath) {
10645
10648
  if (!returnPath.startsWith("/")) {
10646
10649
  return false;
@@ -10648,11 +10651,13 @@ function isSafeReturnPath(returnPath) {
10648
10651
  if (returnPath.startsWith("//") || returnPath.includes("\\")) {
10649
10652
  return false;
10650
10653
  }
10651
- if (returnPath.includes("..")) {
10654
+ if (returnPath.includes("..") || URL_STRIPPED_CHARACTER.test(returnPath)) {
10652
10655
  return false;
10653
10656
  }
10654
10657
  return !/^\/[^/?#]*:/.test(returnPath);
10655
10658
  }
10659
+
10660
+ // src/server/services/signup-link.service.ts
10656
10661
  async function requestSignupLinkService(params) {
10657
10662
  const email = params.email.trim();
10658
10663
  const returnPath = params.returnPath;
@@ -13070,7 +13075,11 @@ async function oauthCallbackService(params) {
13070
13075
  const redirectUrl = buildRedirectUrl(callbackUrl, {
13071
13076
  userId: String(userId),
13072
13077
  keyId: stateData.keyId,
13073
- returnUrl: stateData.returnUrl,
13078
+ // Defence in depth: the state is sealed, so this value was already checked
13079
+ // where it was sealed. Checking it again costs one comparison and keeps a
13080
+ // destination that leaves the app out of the callback URL no matter which
13081
+ // seam sealed it.
13082
+ returnUrl: isSafeReturnPath(stateData.returnUrl) ? stateData.returnUrl : "/",
13074
13083
  isNewUser: String(isNewUser)
13075
13084
  });
13076
13085
  const user = await usersRepository.findById(userId);
@@ -16312,6 +16321,9 @@ var oauthStart = route6.post("/_auth/oauth/start").input({
16312
16321
  })
16313
16322
  }).use([rateLimitPolicy6("oauth-start", { limit: 20, windowMs: 6e4 })]).skip(["auth"]).handler(async (c) => {
16314
16323
  const { body } = await c.data();
16324
+ if (!isSafeReturnPath(body.returnUrl)) {
16325
+ throw new ValidationError13({ message: "returnUrl must be a relative path within the app" });
16326
+ }
16315
16327
  const nonce = generateOAuthNonce();
16316
16328
  setCookie(c.raw, COOKIE_NAMES.OAUTH_CSRF, nonce, {
16317
16329
  httpOnly: true,
@@ -16360,6 +16372,9 @@ var oauthFinalize = route6.post("/_auth/oauth/finalize").input({
16360
16372
  })
16361
16373
  }).use([rateLimitPolicy6("oauth-start", { limit: 20, windowMs: 6e4 })]).skip(["auth"]).handler(async (c) => {
16362
16374
  const { body } = await c.data();
16375
+ if (body.returnUrl && !isSafeReturnPath(body.returnUrl)) {
16376
+ throw new ValidationError13({ message: "returnUrl must be a relative path within the app" });
16377
+ }
16363
16378
  return {
16364
16379
  success: true,
16365
16380
  userId: body.userId,