@lemondouble/lemon-auth 0.10.0 → 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.
- package/LICENSE +21 -0
- package/README.md +75 -463
- package/dist/{chunk-ICPDIWYK.js → chunk-TKMZEMW7.js} +5 -11
- package/dist/chunk-TKMZEMW7.js.map +1 -0
- package/dist/client/index.d.ts +3 -27
- package/dist/client/index.js +1 -53
- package/dist/client/index.js.map +1 -1
- package/dist/proxy/index.js +225 -16
- package/dist/proxy/index.js.map +1 -1
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/{url-qhLR3uNu.d.ts → url-O6F1o0px.d.ts} +1 -4
- package/package.json +1 -1
- package/dist/chunk-ICPDIWYK.js.map +0 -1
|
@@ -31,23 +31,18 @@ async function logout() {
|
|
|
31
31
|
return false;
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
-
async function
|
|
35
|
-
if (isMockAuthEnabled()) return
|
|
34
|
+
async function refreshToken() {
|
|
35
|
+
if (isMockAuthEnabled()) return true;
|
|
36
36
|
try {
|
|
37
37
|
const res = await fetch(REFRESH_URL, {
|
|
38
38
|
method: "POST",
|
|
39
39
|
credentials: "include"
|
|
40
40
|
});
|
|
41
|
-
|
|
42
|
-
if (res.status === 401 || res.status === 403) return "unauthorized";
|
|
43
|
-
return "unavailable";
|
|
41
|
+
return res.ok;
|
|
44
42
|
} catch {
|
|
45
|
-
return
|
|
43
|
+
return false;
|
|
46
44
|
}
|
|
47
45
|
}
|
|
48
|
-
async function refreshToken() {
|
|
49
|
-
return await refreshSession() === "ok";
|
|
50
|
-
}
|
|
51
46
|
function assertValidLoginRedirectUrl(redirectUrl) {
|
|
52
47
|
let url;
|
|
53
48
|
try {
|
|
@@ -73,7 +68,6 @@ export {
|
|
|
73
68
|
loginUrl,
|
|
74
69
|
profileUrl,
|
|
75
70
|
logout,
|
|
76
|
-
refreshSession,
|
|
77
71
|
refreshToken
|
|
78
72
|
};
|
|
79
|
-
//# sourceMappingURL=chunk-
|
|
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":[]}
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { a as LemonUser } from '../url-
|
|
3
|
-
export { l as loginUrl, b as logout, p as profileUrl, r as refreshToken } from '../url-
|
|
4
|
-
import * as react from 'react';
|
|
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,27 +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
|
-
export { AuthProvider, LemonUser, SessionRestore, useAuth };
|
|
15
|
+
export { AuthProvider, LemonUser, useAuth };
|
package/dist/client/index.js
CHANGED
|
@@ -3,9 +3,8 @@ import {
|
|
|
3
3
|
loginUrl,
|
|
4
4
|
logout,
|
|
5
5
|
profileUrl,
|
|
6
|
-
refreshSession,
|
|
7
6
|
refreshToken
|
|
8
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-TKMZEMW7.js";
|
|
9
8
|
import "../chunk-JIXVTKLG.js";
|
|
10
9
|
|
|
11
10
|
// src/client/context.tsx
|
|
@@ -24,59 +23,8 @@ function AuthProvider({
|
|
|
24
23
|
function useAuth() {
|
|
25
24
|
return useContext(AuthContext);
|
|
26
25
|
}
|
|
27
|
-
|
|
28
|
-
// src/client/session-restore.tsx
|
|
29
|
-
import { useEffect, useState } from "react";
|
|
30
|
-
import { useSearchParams } from "next/navigation";
|
|
31
|
-
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
32
|
-
function SessionRestore({ fallback }) {
|
|
33
|
-
const searchParams = useSearchParams();
|
|
34
|
-
const [unavailable, setUnavailable] = useState(false);
|
|
35
|
-
useEffect(() => {
|
|
36
|
-
let ignore = false;
|
|
37
|
-
const next = safeNextPath(searchParams.get("next"));
|
|
38
|
-
refreshSession().then((result) => {
|
|
39
|
-
if (ignore) return;
|
|
40
|
-
if (result === "unavailable") {
|
|
41
|
-
setUnavailable(true);
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
window.location.replace(next);
|
|
45
|
-
});
|
|
46
|
-
return () => {
|
|
47
|
-
ignore = true;
|
|
48
|
-
};
|
|
49
|
-
}, [searchParams]);
|
|
50
|
-
if (unavailable) {
|
|
51
|
-
return /* @__PURE__ */ jsxs(
|
|
52
|
-
"div",
|
|
53
|
-
{
|
|
54
|
-
style: {
|
|
55
|
-
minHeight: "100dvh",
|
|
56
|
-
display: "flex",
|
|
57
|
-
flexDirection: "column",
|
|
58
|
-
alignItems: "center",
|
|
59
|
-
justifyContent: "center",
|
|
60
|
-
gap: 12
|
|
61
|
-
},
|
|
62
|
-
children: [
|
|
63
|
-
/* @__PURE__ */ jsx2("p", { children: "\uC778\uC99D \uC11C\uBC84\uC5D0 \uC5F0\uACB0\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4." }),
|
|
64
|
-
/* @__PURE__ */ jsx2("button", { type: "button", onClick: () => window.location.reload(), children: "\uB2E4\uC2DC \uC2DC\uB3C4" })
|
|
65
|
-
]
|
|
66
|
-
}
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
return fallback;
|
|
70
|
-
}
|
|
71
|
-
function safeNextPath(next) {
|
|
72
|
-
if (!next) return "/";
|
|
73
|
-
if (!next.startsWith("/")) return "/";
|
|
74
|
-
if (next.startsWith("//")) return "/";
|
|
75
|
-
return next;
|
|
76
|
-
}
|
|
77
26
|
export {
|
|
78
27
|
AuthProvider,
|
|
79
|
-
SessionRestore,
|
|
80
28
|
loginUrl,
|
|
81
29
|
logout,
|
|
82
30
|
profileUrl,
|
package/dist/client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/context.tsx"
|
|
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":[]}
|