@lemondouble/lemon-auth 0.9.1 → 0.11.0

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.
@@ -8,7 +8,6 @@ var PROFILE_URL = `${AUTH_SERVER_URL}/api/user/me`;
8
8
  var PROFILE_PAGE_URL = `${AUTH_SERVER_URL}/profile`;
9
9
  var ACCESS_TOKEN_COOKIE = "lemon_access_token";
10
10
  var REFRESH_TOKEN_COOKIE = "lemon_refresh_token";
11
- var DEVICE_ID_COOKIE = "device_id";
12
11
 
13
12
  // src/mock.ts
14
13
  var LEMON_AUTH_MOCK_USER_ENV = "NEXT_PUBLIC_LEMON_AUTH_MOCK_USER";
@@ -109,12 +108,10 @@ export {
109
108
  LOGIN_URL,
110
109
  REFRESH_URL,
111
110
  LOGOUT_URL,
112
- PROFILE_URL,
113
111
  PROFILE_PAGE_URL,
114
112
  ACCESS_TOKEN_COOKIE,
115
113
  REFRESH_TOKEN_COOKIE,
116
- DEVICE_ID_COOKIE,
117
114
  isMockAuthEnabled,
118
115
  getMockAccessTokenClaims
119
116
  };
120
- //# sourceMappingURL=chunk-OX5MDVSR.js.map
117
+ //# sourceMappingURL=chunk-JIXVTKLG.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts","../src/mock.ts"],"sourcesContent":["export const AUTH_SERVER_URL = \"https://auth.lemondouble.com\";\nexport const JWKS_URL = `${AUTH_SERVER_URL}/.well-known/jwks.json`;\nexport const LOGIN_URL = `${AUTH_SERVER_URL}/api/oauth2/google/login`;\nexport const REFRESH_URL = `${AUTH_SERVER_URL}/api/token/refresh`;\nexport const LOGOUT_URL = `${AUTH_SERVER_URL}/api/token/logout`;\nexport const PROFILE_URL = `${AUTH_SERVER_URL}/api/user/me`;\nexport const PROFILE_PAGE_URL = `${AUTH_SERVER_URL}/profile`;\n\nexport const ACCESS_TOKEN_COOKIE = \"lemon_access_token\";\nexport const REFRESH_TOKEN_COOKIE = \"lemon_refresh_token\";\nexport const DEVICE_ID_COOKIE = \"device_id\";\n","import type { AccessTokenClaims, LemonUser } from \"./types.js\";\n\nexport const LEMON_AUTH_MOCK_USER_ENV =\n \"NEXT_PUBLIC_LEMON_AUTH_MOCK_USER\";\n\nexport function isMockAuthEnabled(): boolean {\n return !isProduction() && Boolean(readMockUserEnv());\n}\n\nexport function getMockUser(): LemonUser | null {\n const raw = readMockUserEnv();\n if (isProduction() || !raw) return null;\n return parseMockUser(raw);\n}\n\nexport function getMockAccessTokenClaims(): AccessTokenClaims | null {\n const user = getMockUser();\n if (!user) return null;\n\n const now = Math.floor(Date.now() / 1000);\n return {\n token_type: \"access\",\n sub: user.uid,\n nickname: user.nickname,\n profile_image_url: user.profileImageUrl,\n role: user.role,\n approved_clients: user.approvedClients,\n iss: \"lemon-auth-mock\",\n iat: now,\n exp: now + 60 * 60 * 24 * 365,\n };\n}\n\nfunction readMockUserEnv(): string | undefined {\n try {\n return process.env.NEXT_PUBLIC_LEMON_AUTH_MOCK_USER;\n } catch {\n return undefined;\n }\n}\n\nfunction isProduction(): boolean {\n try {\n return process.env.NODE_ENV === \"production\";\n } catch {\n return false;\n }\n}\n\nfunction parseMockUser(raw: string): LemonUser {\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch {\n throw new Error(\n `${LEMON_AUTH_MOCK_USER_ENV} must be a valid JSON object`\n );\n }\n\n if (!isRecord(data)) {\n throw new Error(`${LEMON_AUTH_MOCK_USER_ENV} must be a JSON object`);\n }\n\n return {\n uid: readOptionalString(data.uid, \"local-user\", \"uid\"),\n nickname: readOptionalString(data.nickname, \"Local User\", \"nickname\"),\n profileImageUrl: readOptionalString(\n data.profileImageUrl ?? data.profile_image_url,\n \"\",\n \"profileImageUrl\"\n ),\n role: readOptionalRole(data.role, \"admin\"),\n approvedClients: readOptionalStringArray(\n data.approvedClients ?? data.approved_clients,\n [\"*\"],\n \"approvedClients\"\n ),\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction readOptionalString(\n value: unknown,\n fallback: string,\n field: string\n): string {\n if (value === undefined || value === null) return fallback;\n if (typeof value === \"string\") return value;\n throw new Error(`${LEMON_AUTH_MOCK_USER_ENV}.${field} must be a string`);\n}\n\nfunction readOptionalRole(\n value: unknown,\n fallback: LemonUser[\"role\"]\n): LemonUser[\"role\"] {\n if (value === undefined || value === null) return fallback;\n if (value === \"user\" || value === \"admin\") return value;\n throw new Error(\n `${LEMON_AUTH_MOCK_USER_ENV}.role must be \"user\" or \"admin\"`\n );\n}\n\nfunction readOptionalStringArray(\n value: unknown,\n fallback: string[],\n field: string\n): string[] {\n if (value === undefined || value === null) return fallback;\n if (\n Array.isArray(value) &&\n value.every((item) => typeof item === \"string\")\n ) {\n return value;\n }\n throw new Error(\n `${LEMON_AUTH_MOCK_USER_ENV}.${field} must be a string array`\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/constants.ts","../src/mock.ts"],"sourcesContent":["export const AUTH_SERVER_URL = \"https://auth.lemondouble.com\";\nexport const JWKS_URL = `${AUTH_SERVER_URL}/.well-known/jwks.json`;\nexport const LOGIN_URL = `${AUTH_SERVER_URL}/api/oauth2/google/login`;\nexport const REFRESH_URL = `${AUTH_SERVER_URL}/api/token/refresh`;\nexport const LOGOUT_URL = `${AUTH_SERVER_URL}/api/token/logout`;\nexport const PROFILE_URL = `${AUTH_SERVER_URL}/api/user/me`;\nexport const PROFILE_PAGE_URL = `${AUTH_SERVER_URL}/profile`;\n\nexport const ACCESS_TOKEN_COOKIE = \"lemon_access_token\";\nexport const REFRESH_TOKEN_COOKIE = \"lemon_refresh_token\";\nexport const DEVICE_ID_COOKIE = \"device_id\";\n","import type { AccessTokenClaims, LemonUser } from \"./types.js\";\n\nexport const LEMON_AUTH_MOCK_USER_ENV =\n \"NEXT_PUBLIC_LEMON_AUTH_MOCK_USER\";\n\nexport function isMockAuthEnabled(): boolean {\n return !isProduction() && Boolean(readMockUserEnv());\n}\n\nexport function getMockUser(): LemonUser | null {\n const raw = readMockUserEnv();\n if (isProduction() || !raw) return null;\n return parseMockUser(raw);\n}\n\nexport function getMockAccessTokenClaims(): AccessTokenClaims | null {\n const user = getMockUser();\n if (!user) return null;\n\n const now = Math.floor(Date.now() / 1000);\n return {\n token_type: \"access\",\n sub: user.uid,\n nickname: user.nickname,\n profile_image_url: user.profileImageUrl,\n role: user.role,\n approved_clients: user.approvedClients,\n iss: \"lemon-auth-mock\",\n iat: now,\n exp: now + 60 * 60 * 24 * 365,\n };\n}\n\nfunction readMockUserEnv(): string | undefined {\n try {\n return process.env.NEXT_PUBLIC_LEMON_AUTH_MOCK_USER;\n } catch {\n return undefined;\n }\n}\n\nfunction isProduction(): boolean {\n try {\n return process.env.NODE_ENV === \"production\";\n } catch {\n return false;\n }\n}\n\nfunction parseMockUser(raw: string): LemonUser {\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch {\n throw new Error(\n `${LEMON_AUTH_MOCK_USER_ENV} must be a valid JSON object`\n );\n }\n\n if (!isRecord(data)) {\n throw new Error(`${LEMON_AUTH_MOCK_USER_ENV} must be a JSON object`);\n }\n\n return {\n uid: readOptionalString(data.uid, \"local-user\", \"uid\"),\n nickname: readOptionalString(data.nickname, \"Local User\", \"nickname\"),\n profileImageUrl: readOptionalString(\n data.profileImageUrl ?? data.profile_image_url,\n \"\",\n \"profileImageUrl\"\n ),\n role: readOptionalRole(data.role, \"admin\"),\n approvedClients: readOptionalStringArray(\n data.approvedClients ?? data.approved_clients,\n [\"*\"],\n \"approvedClients\"\n ),\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction readOptionalString(\n value: unknown,\n fallback: string,\n field: string\n): string {\n if (value === undefined || value === null) return fallback;\n if (typeof value === \"string\") return value;\n throw new Error(`${LEMON_AUTH_MOCK_USER_ENV}.${field} must be a string`);\n}\n\nfunction readOptionalRole(\n value: unknown,\n fallback: LemonUser[\"role\"]\n): LemonUser[\"role\"] {\n if (value === undefined || value === null) return fallback;\n if (value === \"user\" || value === \"admin\") return value;\n throw new Error(\n `${LEMON_AUTH_MOCK_USER_ENV}.role must be \"user\" or \"admin\"`\n );\n}\n\nfunction readOptionalStringArray(\n value: unknown,\n fallback: string[],\n field: string\n): string[] {\n if (value === undefined || value === null) return fallback;\n if (\n Array.isArray(value) &&\n value.every((item) => typeof item === \"string\")\n ) {\n return value;\n }\n throw new Error(\n `${LEMON_AUTH_MOCK_USER_ENV}.${field} must be a string array`\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,73 @@
1
+ import {
2
+ LOGIN_URL,
3
+ LOGOUT_URL,
4
+ PROFILE_PAGE_URL,
5
+ REFRESH_URL,
6
+ isMockAuthEnabled
7
+ } from "./chunk-JIXVTKLG.js";
8
+
9
+ // src/client/url.ts
10
+ function loginUrl(redirectUrl) {
11
+ if (isMockAuthEnabled()) return redirectUrl;
12
+ assertValidLoginRedirectUrl(redirectUrl);
13
+ return `${LOGIN_URL}?redirect_url=${encodeURIComponent(redirectUrl)}`;
14
+ }
15
+ function profileUrl(redirectUrl) {
16
+ if (isMockAuthEnabled()) return redirectUrl ?? "/";
17
+ if (redirectUrl) {
18
+ return `${PROFILE_PAGE_URL}?redirect_url=${encodeURIComponent(redirectUrl)}`;
19
+ }
20
+ return PROFILE_PAGE_URL;
21
+ }
22
+ async function logout() {
23
+ if (isMockAuthEnabled()) return true;
24
+ try {
25
+ const res = await fetch(LOGOUT_URL, {
26
+ method: "POST",
27
+ credentials: "include"
28
+ });
29
+ return res.ok;
30
+ } catch {
31
+ return false;
32
+ }
33
+ }
34
+ async function refreshToken() {
35
+ if (isMockAuthEnabled()) return true;
36
+ try {
37
+ const res = await fetch(REFRESH_URL, {
38
+ method: "POST",
39
+ credentials: "include"
40
+ });
41
+ return res.ok;
42
+ } catch {
43
+ return false;
44
+ }
45
+ }
46
+ function assertValidLoginRedirectUrl(redirectUrl) {
47
+ let url;
48
+ try {
49
+ url = new URL(redirectUrl);
50
+ } catch {
51
+ throw new Error("loginUrl redirectUrl must be a valid absolute URL");
52
+ }
53
+ if (url.protocol !== "https:") {
54
+ throw new Error("loginUrl redirectUrl must use https");
55
+ }
56
+ if (!isAllowedLoginRedirectHost(url.hostname)) {
57
+ throw new Error(
58
+ "loginUrl redirectUrl host must be lemondouble.com or a subdomain of lemondouble.com"
59
+ );
60
+ }
61
+ }
62
+ function isAllowedLoginRedirectHost(hostname) {
63
+ const normalized = hostname.toLowerCase();
64
+ return normalized === "lemondouble.com" || normalized.endsWith(".lemondouble.com");
65
+ }
66
+
67
+ export {
68
+ loginUrl,
69
+ profileUrl,
70
+ logout,
71
+ refreshToken
72
+ };
73
+ //# sourceMappingURL=chunk-TKMZEMW7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client/url.ts"],"sourcesContent":["import {\n LOGIN_URL,\n LOGOUT_URL,\n PROFILE_PAGE_URL,\n REFRESH_URL,\n} from \"../constants.js\";\nimport { isMockAuthEnabled } from \"../mock.js\";\n\nexport function loginUrl(redirectUrl: string): string {\n if (isMockAuthEnabled()) return redirectUrl;\n assertValidLoginRedirectUrl(redirectUrl);\n return `${LOGIN_URL}?redirect_url=${encodeURIComponent(redirectUrl)}`;\n}\n\nexport function profileUrl(redirectUrl?: string): string {\n if (isMockAuthEnabled()) return redirectUrl ?? \"/\";\n if (redirectUrl) {\n return `${PROFILE_PAGE_URL}?redirect_url=${encodeURIComponent(redirectUrl)}`;\n }\n return PROFILE_PAGE_URL;\n}\n\n/** 쿠키는 httpOnly 라 삭제는 응답의 Set-Cookie 가 한다. */\nexport async function logout(): Promise<boolean> {\n if (isMockAuthEnabled()) return true;\n\n try {\n const res = await fetch(LOGOUT_URL, {\n method: \"POST\",\n credentials: \"include\",\n });\n return res.ok;\n } catch {\n return false;\n }\n}\n\nexport async function refreshToken(): Promise<boolean> {\n if (isMockAuthEnabled()) return true;\n\n try {\n const res = await fetch(REFRESH_URL, {\n method: \"POST\",\n credentials: \"include\",\n });\n return res.ok;\n } catch {\n return false;\n }\n}\n\nfunction assertValidLoginRedirectUrl(redirectUrl: string): void {\n let url: URL;\n try {\n url = new URL(redirectUrl);\n } catch {\n throw new Error(\"loginUrl redirectUrl must be a valid absolute URL\");\n }\n\n if (url.protocol !== \"https:\") {\n throw new Error(\"loginUrl redirectUrl must use https\");\n }\n\n if (!isAllowedLoginRedirectHost(url.hostname)) {\n throw new Error(\n \"loginUrl redirectUrl host must be lemondouble.com or a subdomain of lemondouble.com\"\n );\n }\n}\n\nfunction isAllowedLoginRedirectHost(hostname: string): boolean {\n const normalized = hostname.toLowerCase();\n return (\n normalized === \"lemondouble.com\" ||\n normalized.endsWith(\".lemondouble.com\")\n );\n}\n"],"mappings":";;;;;;;;;AAQO,SAAS,SAAS,aAA6B;AACpD,MAAI,kBAAkB,EAAG,QAAO;AAChC,8BAA4B,WAAW;AACvC,SAAO,GAAG,SAAS,iBAAiB,mBAAmB,WAAW,CAAC;AACrE;AAEO,SAAS,WAAW,aAA8B;AACvD,MAAI,kBAAkB,EAAG,QAAO,eAAe;AAC/C,MAAI,aAAa;AACf,WAAO,GAAG,gBAAgB,iBAAiB,mBAAmB,WAAW,CAAC;AAAA,EAC5E;AACA,SAAO;AACT;AAGA,eAAsB,SAA2B;AAC/C,MAAI,kBAAkB,EAAG,QAAO;AAEhC,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,YAAY;AAAA,MAClC,QAAQ;AAAA,MACR,aAAa;AAAA,IACf,CAAC;AACD,WAAO,IAAI;AAAA,EACb,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,eAAiC;AACrD,MAAI,kBAAkB,EAAG,QAAO;AAEhC,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,aAAa;AAAA,MACnC,QAAQ;AAAA,MACR,aAAa;AAAA,IACf,CAAC;AACD,WAAO,IAAI;AAAA,EACb,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,4BAA4B,aAA2B;AAC9D,MAAI;AACJ,MAAI;AACF,UAAM,IAAI,IAAI,WAAW;AAAA,EAC3B,QAAQ;AACN,UAAM,IAAI,MAAM,mDAAmD;AAAA,EACrE;AAEA,MAAI,IAAI,aAAa,UAAU;AAC7B,UAAM,IAAI,MAAM,qCAAqC;AAAA,EACvD;AAEA,MAAI,CAAC,2BAA2B,IAAI,QAAQ,GAAG;AAC7C,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,2BAA2B,UAA2B;AAC7D,QAAM,aAAa,SAAS,YAAY;AACxC,SACE,eAAe,qBACf,WAAW,SAAS,kBAAkB;AAE1C;","names":[]}
@@ -1,7 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { a as LemonUser } from '../types-B5uRY2Si.js';
3
- import * as react from 'react';
4
- import 'jose';
2
+ import { a as LemonUser } from '../url-O6F1o0px.js';
3
+ export { l as loginUrl, b as logout, p as profileUrl, r as refreshToken } from '../url-O6F1o0px.js';
5
4
 
6
5
  interface AuthContextValue {
7
6
  user: LemonUser | null;
@@ -13,36 +12,4 @@ declare function AuthProvider({ user, children, }: {
13
12
  }): react_jsx_runtime.JSX.Element;
14
13
  declare function useAuth(): AuthContextValue;
15
14
 
16
- /**
17
- * 토큰을 갱신하는 유일한 주체. 프록시가 만료된 세션을 이리로 보낸다.
18
- *
19
- * 갱신을 서버(미들웨어)가 아니라 브라우저가 하는 이유는 두 가지다.
20
- *
21
- * 1. 미들웨어는 요청마다 별도 인스턴스로 떠서 서로를 모른다. 프리페치가
22
- * 깔리면 같은 refresh token 으로 갱신이 동시에 나가고, 회전 경쟁이 난다.
23
- * 브라우저의 복구 경로는 한 번에 하나만 뜬다.
24
- * 2. 브라우저 요청에는 진짜 UA 와 WAF 통과 쿠키가 실린다. 서버간 fetch 는
25
- * UA 가 런타임 기본값이라 봇 방어에 걸릴 수 있다.
26
- *
27
- * 끝나면 router 가 아니라 window.location 으로 next 에 간다. 소프트 네비는
28
- * 루트 레이아웃(과 AuthProvider)을 다시 렌더하지 않으므로, 옛 세션 스냅샷이
29
- * 남아 화면이 어긋나거나 라우터 캐시에 굳은 리다이렉트가 되살아난다.
30
- *
31
- * 실패해도 next 로 간다. 죽은 토큰이면 서버가 401 응답에서 세션 쿠키를
32
- * 지워 주므로(auth-server v2026.08.03.2+), 이후 판단 — 로그인으로 보낼지,
33
- * 익명으로 보여줄지 — 는 프록시와 DAL 이 알아서 한다.
34
- */
35
- declare function SessionRestore({ fallback }: {
36
- fallback: React.ReactNode;
37
- }): string | number | bigint | boolean | Iterable<react.ReactNode> | Promise<string | number | bigint | boolean | react.ReactPortal | react.ReactElement<unknown, string | react.JSXElementConstructor<any>> | Iterable<react.ReactNode> | null | undefined> | react_jsx_runtime.JSX.Element | null | undefined;
38
-
39
- declare function loginUrl(redirectUrl: string): string;
40
- declare function profileUrl(redirectUrl?: string): string;
41
- /**
42
- * 로그아웃. 쿠키 삭제는 응답의 Set-Cookie 가 한다 — 세션 쿠키는 httpOnly
43
- * 라서 JS 로는 지울 수 없다.
44
- */
45
- declare function logout(): Promise<boolean>;
46
- declare function refreshToken(): Promise<boolean>;
47
-
48
- export { AuthProvider, LemonUser, SessionRestore, loginUrl, logout, profileUrl, refreshToken, useAuth };
15
+ export { AuthProvider, LemonUser, useAuth };
@@ -1,11 +1,11 @@
1
1
  "use client";
2
2
  import {
3
- LOGIN_URL,
4
- LOGOUT_URL,
5
- PROFILE_PAGE_URL,
6
- REFRESH_URL,
7
- isMockAuthEnabled
8
- } from "../chunk-OX5MDVSR.js";
3
+ loginUrl,
4
+ logout,
5
+ profileUrl,
6
+ refreshToken
7
+ } from "../chunk-TKMZEMW7.js";
8
+ import "../chunk-JIXVTKLG.js";
9
9
 
10
10
  // src/client/context.tsx
11
11
  import { createContext, useContext } from "react";
@@ -23,124 +23,8 @@ function AuthProvider({
23
23
  function useAuth() {
24
24
  return useContext(AuthContext);
25
25
  }
26
-
27
- // src/client/session-restore.tsx
28
- import { useEffect, useState } from "react";
29
- import { useSearchParams } from "next/navigation";
30
-
31
- // src/client/url.ts
32
- function loginUrl(redirectUrl) {
33
- if (isMockAuthEnabled()) return redirectUrl;
34
- assertValidLoginRedirectUrl(redirectUrl);
35
- return `${LOGIN_URL}?redirect_url=${encodeURIComponent(redirectUrl)}`;
36
- }
37
- function profileUrl(redirectUrl) {
38
- if (isMockAuthEnabled()) return redirectUrl ?? "/";
39
- if (redirectUrl) {
40
- return `${PROFILE_PAGE_URL}?redirect_url=${encodeURIComponent(redirectUrl)}`;
41
- }
42
- return PROFILE_PAGE_URL;
43
- }
44
- async function logout() {
45
- if (isMockAuthEnabled()) return true;
46
- try {
47
- const res = await fetch(LOGOUT_URL, {
48
- method: "POST",
49
- credentials: "include"
50
- });
51
- return res.ok;
52
- } catch {
53
- return false;
54
- }
55
- }
56
- async function refreshSession() {
57
- if (isMockAuthEnabled()) return "ok";
58
- try {
59
- const res = await fetch(REFRESH_URL, {
60
- method: "POST",
61
- credentials: "include"
62
- });
63
- if (res.ok) return "ok";
64
- if (res.status === 401 || res.status === 403) return "unauthorized";
65
- return "unavailable";
66
- } catch {
67
- return "unavailable";
68
- }
69
- }
70
- async function refreshToken() {
71
- return await refreshSession() === "ok";
72
- }
73
- function assertValidLoginRedirectUrl(redirectUrl) {
74
- let url;
75
- try {
76
- url = new URL(redirectUrl);
77
- } catch {
78
- throw new Error("loginUrl redirectUrl must be a valid absolute URL");
79
- }
80
- if (url.protocol !== "https:") {
81
- throw new Error("loginUrl redirectUrl must use https");
82
- }
83
- if (!isAllowedLoginRedirectHost(url.hostname)) {
84
- throw new Error(
85
- "loginUrl redirectUrl host must be lemondouble.com or a subdomain of lemondouble.com"
86
- );
87
- }
88
- }
89
- function isAllowedLoginRedirectHost(hostname) {
90
- const normalized = hostname.toLowerCase();
91
- return normalized === "lemondouble.com" || normalized.endsWith(".lemondouble.com");
92
- }
93
-
94
- // src/client/session-restore.tsx
95
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
96
- function SessionRestore({ fallback }) {
97
- const searchParams = useSearchParams();
98
- const [unavailable, setUnavailable] = useState(false);
99
- useEffect(() => {
100
- let ignore = false;
101
- const next = safeNextPath(searchParams.get("next"));
102
- refreshSession().then((result) => {
103
- if (ignore) return;
104
- if (result === "unavailable") {
105
- setUnavailable(true);
106
- return;
107
- }
108
- window.location.replace(next);
109
- });
110
- return () => {
111
- ignore = true;
112
- };
113
- }, [searchParams]);
114
- if (unavailable) {
115
- return /* @__PURE__ */ jsxs(
116
- "div",
117
- {
118
- style: {
119
- minHeight: "100dvh",
120
- display: "flex",
121
- flexDirection: "column",
122
- alignItems: "center",
123
- justifyContent: "center",
124
- gap: 12
125
- },
126
- children: [
127
- /* @__PURE__ */ jsx2("p", { children: "\uC778\uC99D \uC11C\uBC84\uC5D0 \uC5F0\uACB0\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4." }),
128
- /* @__PURE__ */ jsx2("button", { type: "button", onClick: () => window.location.reload(), children: "\uB2E4\uC2DC \uC2DC\uB3C4" })
129
- ]
130
- }
131
- );
132
- }
133
- return fallback;
134
- }
135
- function safeNextPath(next) {
136
- if (!next) return "/";
137
- if (!next.startsWith("/")) return "/";
138
- if (next.startsWith("//")) return "/";
139
- return next;
140
- }
141
26
  export {
142
27
  AuthProvider,
143
- SessionRestore,
144
28
  loginUrl,
145
29
  logout,
146
30
  profileUrl,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/client/context.tsx","../../src/client/session-restore.tsx","../../src/client/url.ts"],"sourcesContent":["\"use client\";\n\nimport { createContext, useContext } from \"react\";\nimport type { LemonUser } from \"../types.js\";\n\ninterface AuthContextValue {\n user: LemonUser | null;\n isAuthenticated: boolean;\n}\n\nconst AuthContext = createContext<AuthContextValue>({\n user: null,\n isAuthenticated: false,\n});\n\nexport function AuthProvider({\n user,\n children,\n}: {\n user: LemonUser | null;\n children: React.ReactNode;\n}) {\n return (\n <AuthContext value={{ user, isAuthenticated: user !== null }}>\n {children}\n </AuthContext>\n );\n}\n\nexport function useAuth(): AuthContextValue {\n return useContext(AuthContext);\n}\n","\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { useSearchParams } from \"next/navigation\";\nimport { refreshSession } from \"./url.js\";\n\n/**\n * 토큰을 갱신하는 유일한 주체. 프록시가 만료된 세션을 이리로 보낸다.\n *\n * 갱신을 서버(미들웨어)가 아니라 브라우저가 하는 이유는 두 가지다.\n *\n * 1. 미들웨어는 요청마다 별도 인스턴스로 떠서 서로를 모른다. 프리페치가\n * 깔리면 같은 refresh token 으로 갱신이 동시에 나가고, 회전 경쟁이 난다.\n * 브라우저의 복구 경로는 한 번에 하나만 뜬다.\n * 2. 브라우저 요청에는 진짜 UA 와 WAF 통과 쿠키가 실린다. 서버간 fetch 는\n * UA 가 런타임 기본값이라 봇 방어에 걸릴 수 있다.\n *\n * 끝나면 router 가 아니라 window.location 으로 next 에 간다. 소프트 네비는\n * 루트 레이아웃(과 AuthProvider)을 다시 렌더하지 않으므로, 옛 세션 스냅샷이\n * 남아 화면이 어긋나거나 라우터 캐시에 굳은 리다이렉트가 되살아난다.\n *\n * 실패해도 next 로 간다. 죽은 토큰이면 서버가 401 응답에서 세션 쿠키를\n * 지워 주므로(auth-server v2026.08.03.2+), 이후 판단 — 로그인으로 보낼지,\n * 익명으로 보여줄지 — 는 프록시와 DAL 이 알아서 한다.\n */\nexport function SessionRestore({ fallback }: { fallback: React.ReactNode }) {\n const searchParams = useSearchParams();\n const [unavailable, setUnavailable] = useState(false);\n\n useEffect(() => {\n let ignore = false;\n const next = safeNextPath(searchParams.get(\"next\"));\n\n refreshSession().then((result) => {\n if (ignore) return;\n\n if (result === \"unavailable\") {\n // 서버 · 네트워크 사정이다. 토큰은 살아 있을 수 있으므로\n // 로그인으로 보내지 않고 재시도만 권한다.\n setUnavailable(true);\n return;\n }\n\n window.location.replace(next);\n });\n\n return () => {\n ignore = true;\n };\n }, [searchParams]);\n\n if (unavailable) {\n return (\n <div\n style={{\n minHeight: \"100dvh\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: 12,\n }}\n >\n <p>인증 서버에 연결하지 못했습니다.</p>\n <button type=\"button\" onClick={() => window.location.reload()}>\n 다시 시도\n </button>\n </div>\n );\n }\n\n return fallback;\n}\n\n/**\n * next 파라미터는 사용자가 조작할 수 있으므로 열린 리다이렉트가 되지 않게\n * 같은 출처의 경로만 허용한다. `//evil.com` 은 브라우저가 프로토콜 상대\n * URL 로 읽으므로 반드시 걸러야 한다.\n */\nfunction safeNextPath(next: string | null): string {\n if (!next) return \"/\";\n if (!next.startsWith(\"/\")) return \"/\";\n if (next.startsWith(\"//\")) return \"/\";\n return next;\n}\n","import {\n LOGIN_URL,\n LOGOUT_URL,\n PROFILE_PAGE_URL,\n REFRESH_URL,\n} from \"../constants.js\";\nimport { isMockAuthEnabled } from \"../mock.js\";\n\nexport function loginUrl(redirectUrl: string): string {\n if (isMockAuthEnabled()) return redirectUrl;\n assertValidLoginRedirectUrl(redirectUrl);\n return `${LOGIN_URL}?redirect_url=${encodeURIComponent(redirectUrl)}`;\n}\n\nexport function profileUrl(redirectUrl?: string): string {\n if (isMockAuthEnabled()) return redirectUrl ?? \"/\";\n if (redirectUrl) {\n return `${PROFILE_PAGE_URL}?redirect_url=${encodeURIComponent(redirectUrl)}`;\n }\n return PROFILE_PAGE_URL;\n}\n\n/**\n * 로그아웃. 쿠키 삭제는 응답의 Set-Cookie 가 한다 — 세션 쿠키는 httpOnly\n * 라서 JS 로는 지울 수 없다.\n */\nexport async function logout(): Promise<boolean> {\n if (isMockAuthEnabled()) return true;\n\n try {\n const res = await fetch(LOGOUT_URL, {\n method: \"POST\",\n credentials: \"include\",\n });\n return res.ok;\n } catch {\n return false;\n }\n}\n\nexport type RefreshSessionResult = \"ok\" | \"unauthorized\" | \"unavailable\";\n\n/**\n * 세션 갱신. 쿠키 반영은 응답의 Set-Cookie 가 한다.\n *\n * \"unauthorized\" 는 refresh token 이 죽은 것이고, 서버가 그 응답에서 세션\n * 쿠키를 함께 지워준다. \"unavailable\" 은 서버 · 네트워크 사정이므로 세션이\n * 죽었다고 판단하면 안 된다.\n */\nexport async function refreshSession(): Promise<RefreshSessionResult> {\n if (isMockAuthEnabled()) return \"ok\";\n\n try {\n const res = await fetch(REFRESH_URL, {\n method: \"POST\",\n credentials: \"include\",\n });\n if (res.ok) return \"ok\";\n if (res.status === 401 || res.status === 403) return \"unauthorized\";\n return \"unavailable\";\n } catch {\n return \"unavailable\";\n }\n}\n\nexport async function refreshToken(): Promise<boolean> {\n return (await refreshSession()) === \"ok\";\n}\n\nfunction assertValidLoginRedirectUrl(redirectUrl: string): void {\n let url: URL;\n try {\n url = new URL(redirectUrl);\n } catch {\n throw new Error(\"loginUrl redirectUrl must be a valid absolute URL\");\n }\n\n if (url.protocol !== \"https:\") {\n throw new Error(\"loginUrl redirectUrl must use https\");\n }\n\n if (!isAllowedLoginRedirectHost(url.hostname)) {\n throw new Error(\n \"loginUrl redirectUrl host must be lemondouble.com or a subdomain of lemondouble.com\"\n );\n }\n}\n\nfunction isAllowedLoginRedirectHost(hostname: string): boolean {\n const normalized = hostname.toLowerCase();\n return (\n normalized === \"lemondouble.com\" ||\n normalized.endsWith(\".lemondouble.com\")\n );\n}\n"],"mappings":"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","names":["jsx"]}
1
+ {"version":3,"sources":["../../src/client/context.tsx"],"sourcesContent":["\"use client\";\n\nimport { createContext, useContext } from \"react\";\nimport type { LemonUser } from \"../types.js\";\n\ninterface AuthContextValue {\n user: LemonUser | null;\n isAuthenticated: boolean;\n}\n\nconst AuthContext = createContext<AuthContextValue>({\n user: null,\n isAuthenticated: false,\n});\n\nexport function AuthProvider({\n user,\n children,\n}: {\n user: LemonUser | null;\n children: React.ReactNode;\n}) {\n return (\n <AuthContext value={{ user, isAuthenticated: user !== null }}>\n {children}\n </AuthContext>\n );\n}\n\nexport function useAuth(): AuthContextValue {\n return useContext(AuthContext);\n}\n"],"mappings":";;;;;;;;;AAEA,SAAS,eAAe,kBAAkB;AAqBtC;AAbJ,IAAM,cAAc,cAAgC;AAAA,EAClD,MAAM;AAAA,EACN,iBAAiB;AACnB,CAAC;AAEM,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AACF,GAGG;AACD,SACE,oBAAC,eAAY,OAAO,EAAE,MAAM,iBAAiB,SAAS,KAAK,GACxD,UACH;AAEJ;AAEO,SAAS,UAA4B;AAC1C,SAAO,WAAW,WAAW;AAC/B;","names":[]}
@@ -1,26 +1,14 @@
1
1
  import { NextRequest, NextResponse } from 'next/server';
2
2
 
3
- declare const DEFAULT_AUTH_BYPASS_PATHS: string[];
4
3
  declare const DEFAULT_API_PATHS: string[];
5
- /** 앱이 <SessionRestore /> 를 마운트해야 하는 기본 경로. */
6
- declare const DEFAULT_RESTORE_PATH = "/auth/restore";
7
- declare const PROXY_AUTH_ERROR: {
8
- readonly UNAUTHORIZED: "UNAUTHORIZED";
9
- readonly FORBIDDEN: "FORBIDDEN";
10
- };
11
- type ProxyAuthErrorCode = (typeof PROXY_AUTH_ERROR)[keyof typeof PROXY_AUTH_ERROR];
12
4
  type LoginRedirectUrl = string | ((request: NextRequest) => string);
13
5
  interface AuthProxyOptions {
14
6
  /** 로그인 없이 열어둘 경로. 익명이면 통과, 만료 세션이면 복구를 거친다. */
15
7
  publicPaths?: string[];
16
- /** 프록시 자체를 건너뛸 경로. 정적 파일 등. */
17
- bypassPaths?: string[];
18
8
  /** 리다이렉트 대신 401 JSON 을 돌려줄 경로. 기본 `/api/*`. */
19
9
  apiPaths?: string[];
20
10
  loginRedirectUrl?: LoginRedirectUrl;
21
- /** <SessionRestore /> 를 마운트한 경로. 기본 `/auth/restore`. */
22
- restorePath?: string;
23
11
  }
24
12
  declare function createAuthProxy(options?: AuthProxyOptions): (request: NextRequest) => Promise<NextResponse>;
25
13
 
26
- export { type AuthProxyOptions, DEFAULT_API_PATHS, DEFAULT_AUTH_BYPASS_PATHS, DEFAULT_RESTORE_PATH, type LoginRedirectUrl, PROXY_AUTH_ERROR, type ProxyAuthErrorCode, createAuthProxy };
14
+ export { type AuthProxyOptions, DEFAULT_API_PATHS, type LoginRedirectUrl, createAuthProxy };