mustChangePasswordGate.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. import { sanitizeNext } from "@/lib/frontend/authRedirect";
  2. export const MUST_CHANGE_PASSWORD_GATE_PARAM = "mustChangePasswordGate";
  3. export const MUST_CHANGE_PASSWORD_GATE_VALUE = "1";
  4. export const MUST_CHANGE_PASSWORD_NEXT_PARAM = "next";
  5. export const PROFILE_PATH = "/profile";
  6. function normalizePathname(pathname) {
  7. if (typeof pathname !== "string") return "/";
  8. const trimmed = pathname.trim();
  9. if (!trimmed) return "/";
  10. return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
  11. }
  12. function readParam(searchParams, key) {
  13. if (!searchParams) return null;
  14. if (typeof searchParams.get === "function") {
  15. const value = searchParams.get(key);
  16. return typeof value === "string" ? value : null;
  17. }
  18. const raw = searchParams[key];
  19. if (Array.isArray(raw)) {
  20. return typeof raw[0] === "string" ? raw[0] : null;
  21. }
  22. return typeof raw === "string" ? raw : null;
  23. }
  24. function extractPathname(path) {
  25. if (typeof path !== "string" || !path.trim()) return null;
  26. try {
  27. return new URL(path, "http://localhost").pathname;
  28. } catch {
  29. return null;
  30. }
  31. }
  32. export function isProfilePath(pathname) {
  33. const normalizedPathname = normalizePathname(pathname);
  34. return (
  35. normalizedPathname === PROFILE_PATH ||
  36. normalizedPathname.startsWith(`${PROFILE_PATH}/`)
  37. );
  38. }
  39. export function sanitizeMustChangePasswordNext(nextValue) {
  40. const safeNext = sanitizeNext(nextValue);
  41. if (!safeNext) return null;
  42. const nextPathname = extractPathname(safeNext);
  43. if (!nextPathname) return null;
  44. if (isProfilePath(nextPathname)) return null;
  45. return safeNext;
  46. }
  47. export function shouldRedirectToProfileForPasswordChange({
  48. pathname,
  49. mustChangePassword,
  50. }) {
  51. return mustChangePassword === true && !isProfilePath(pathname);
  52. }
  53. export function buildMustChangePasswordRedirectUrl(nextValue) {
  54. const params = new URLSearchParams();
  55. params.set(MUST_CHANGE_PASSWORD_GATE_PARAM, MUST_CHANGE_PASSWORD_GATE_VALUE);
  56. const safeNext = sanitizeMustChangePasswordNext(nextValue);
  57. if (safeNext) {
  58. params.set(MUST_CHANGE_PASSWORD_NEXT_PARAM, safeNext);
  59. }
  60. return `${PROFILE_PATH}?${params.toString()}`;
  61. }
  62. export function parseMustChangePasswordGateParams(searchParams) {
  63. const gateMarker = readParam(searchParams, MUST_CHANGE_PASSWORD_GATE_PARAM);
  64. const rawNext = readParam(searchParams, MUST_CHANGE_PASSWORD_NEXT_PARAM);
  65. return {
  66. isGateMarker: gateMarker === MUST_CHANGE_PASSWORD_GATE_VALUE,
  67. next: sanitizeMustChangePasswordNext(rawNext),
  68. };
  69. }
  70. export function resolveMustChangePasswordResumePath({
  71. pathname,
  72. searchParams,
  73. mustChangePassword,
  74. }) {
  75. if (mustChangePassword === true) return null;
  76. if (!isProfilePath(pathname)) return null;
  77. const parsed = parseMustChangePasswordGateParams(searchParams);
  78. if (!parsed.isGateMarker) return null;
  79. if (!parsed.next) return null;
  80. return parsed.next;
  81. }