@lemondouble/lemon-auth 0.3.2 → 0.4.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/README.md +10 -10
- package/dist/client/index.d.ts +2 -1
- package/dist/client/index.js +11 -3
- package/dist/client/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -288,7 +288,9 @@ export default async function RootLayout({ children }: { children: React.ReactNo
|
|
|
288
288
|
<html lang="ko">
|
|
289
289
|
<body>
|
|
290
290
|
<AuthProvider user={user}>
|
|
291
|
-
<AutoTokenRefresh
|
|
291
|
+
<AutoTokenRefresh fallback={<p>세션 복원 중...</p>}>
|
|
292
|
+
<p>로그인이 필요합니다</p>
|
|
293
|
+
</AutoTokenRefresh>
|
|
292
294
|
{children}
|
|
293
295
|
</AuthProvider>
|
|
294
296
|
</body>
|
|
@@ -297,21 +299,19 @@ export default async function RootLayout({ children }: { children: React.ReactNo
|
|
|
297
299
|
}
|
|
298
300
|
```
|
|
299
301
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
<p>로그인 정보를 확인하는 중...</p>
|
|
305
|
-
</AutoTokenRefresh>
|
|
306
|
-
```
|
|
302
|
+
| prop | 타입 | 필수 | 설명 |
|
|
303
|
+
|------|------|------|------|
|
|
304
|
+
| `fallback` | `React.ReactNode` | **필수** | 토큰 갱신 시도 중 렌더할 UI (스피너 등) |
|
|
305
|
+
| `children` | `React.ReactNode` | 선택 | 갱신 실패 시 렌더할 UI (로그인 버튼 등) |
|
|
307
306
|
|
|
308
307
|
**동작 방식:**
|
|
309
308
|
|
|
310
309
|
| 상황 | user | AutoTokenRefresh |
|
|
311
310
|
|------|------|-----------------|
|
|
312
311
|
| 정상 요청 (proxy 실행됨) | 있음 | 아무것도 안 함 (skip) |
|
|
312
|
+
| PWA 캐시 히트 + 갱신 시도 중 | 없음 | `fallback` 렌더 |
|
|
313
313
|
| PWA 캐시 히트 + 유효한 refresh_token | 없음 | 갱신 → `router.refresh()` |
|
|
314
|
-
| PWA 캐시 히트 + 만료된 refresh_token | 없음 | 갱신 실패 → children 렌더 |
|
|
314
|
+
| PWA 캐시 히트 + 만료된 refresh_token | 없음 | 갱신 실패 → `children` 렌더 |
|
|
315
315
|
|
|
316
316
|
### `loginUrl(redirectUrl)`
|
|
317
317
|
|
|
@@ -465,7 +465,7 @@ export default async function RootLayout({ children }: { children: React.ReactNo
|
|
|
465
465
|
<html lang="ko">
|
|
466
466
|
<body>
|
|
467
467
|
<AuthProvider user={user}>
|
|
468
|
-
<AutoTokenRefresh />
|
|
468
|
+
<AutoTokenRefresh fallback={<p>세션 복원 중...</p>} />
|
|
469
469
|
{children}
|
|
470
470
|
</AuthProvider>
|
|
471
471
|
</body>
|
package/dist/client/index.d.ts
CHANGED
|
@@ -13,8 +13,9 @@ declare function AuthProvider({ user, children, }: {
|
|
|
13
13
|
}): react_jsx_runtime.JSX.Element;
|
|
14
14
|
declare function useAuth(): AuthContextValue;
|
|
15
15
|
|
|
16
|
-
declare function AutoTokenRefresh({ children }: {
|
|
16
|
+
declare function AutoTokenRefresh({ children, fallback, }: {
|
|
17
17
|
children?: React.ReactNode;
|
|
18
|
+
fallback: React.ReactNode;
|
|
18
19
|
}): string | number | bigint | boolean | react.ReactElement<unknown, string | react.JSXElementConstructor<any>> | Iterable<react.ReactNode> | Promise<string | number | bigint | boolean | react.ReactPortal | react.ReactElement<unknown, string | react.JSXElementConstructor<any>> | Iterable<react.ReactNode> | null | undefined> | null;
|
|
19
20
|
|
|
20
21
|
declare function loginUrl(redirectUrl: string): string;
|
package/dist/client/index.js
CHANGED
|
@@ -24,7 +24,7 @@ function useAuth() {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
// src/client/auto-token-refresh.tsx
|
|
27
|
-
import { useEffect } from "react";
|
|
27
|
+
import { useEffect, useState } from "react";
|
|
28
28
|
import { useRouter } from "next/navigation";
|
|
29
29
|
|
|
30
30
|
// src/client/url.ts
|
|
@@ -52,15 +52,22 @@ async function refreshToken() {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
// src/client/auto-token-refresh.tsx
|
|
55
|
-
function AutoTokenRefresh({
|
|
55
|
+
function AutoTokenRefresh({
|
|
56
|
+
children,
|
|
57
|
+
fallback
|
|
58
|
+
}) {
|
|
56
59
|
const { isAuthenticated } = useAuth();
|
|
57
60
|
const router = useRouter();
|
|
61
|
+
const [status, setStatus] = useState("loading");
|
|
58
62
|
useEffect(() => {
|
|
59
63
|
if (isAuthenticated) return;
|
|
60
64
|
let ignore = false;
|
|
61
65
|
refreshToken().then((ok) => {
|
|
62
|
-
if (
|
|
66
|
+
if (ignore) return;
|
|
67
|
+
if (ok) {
|
|
63
68
|
router.refresh();
|
|
69
|
+
} else {
|
|
70
|
+
setStatus("failed");
|
|
64
71
|
}
|
|
65
72
|
});
|
|
66
73
|
return () => {
|
|
@@ -68,6 +75,7 @@ function AutoTokenRefresh({ children }) {
|
|
|
68
75
|
};
|
|
69
76
|
}, [isAuthenticated, router]);
|
|
70
77
|
if (isAuthenticated) return null;
|
|
78
|
+
if (status === "loading") return fallback ?? null;
|
|
71
79
|
return children ?? null;
|
|
72
80
|
}
|
|
73
81
|
export {
|
package/dist/client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/context.tsx","../../src/client/auto-token-refresh.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 } from \"react\";\nimport { useRouter } from \"next/navigation\";\nimport { useAuth } from \"./context.js\";\nimport { refreshToken } from \"./url.js\";\n\nexport function AutoTokenRefresh({
|
|
1
|
+
{"version":3,"sources":["../../src/client/context.tsx","../../src/client/auto-token-refresh.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 { useRouter } from \"next/navigation\";\nimport { useAuth } from \"./context.js\";\nimport { refreshToken } from \"./url.js\";\n\nexport function AutoTokenRefresh({\n children,\n fallback,\n}: {\n children?: React.ReactNode;\n fallback: React.ReactNode;\n}) {\n const { isAuthenticated } = useAuth();\n const router = useRouter();\n const [status, setStatus] = useState<\"loading\" | \"failed\">(\"loading\");\n\n useEffect(() => {\n if (isAuthenticated) return;\n\n let ignore = false;\n\n refreshToken().then((ok) => {\n if (ignore) return;\n if (ok) {\n router.refresh();\n } else {\n setStatus(\"failed\");\n }\n });\n\n return () => {\n ignore = true;\n };\n }, [isAuthenticated, router]);\n\n if (isAuthenticated) return null;\n if (status === \"loading\") return fallback ?? null;\n return children ?? null;\n}\n","import {\n LOGIN_URL,\n LOGOUT_URL,\n PROFILE_PAGE_URL,\n REFRESH_URL,\n} from \"../constants.js\";\n\nexport function loginUrl(redirectUrl: string): string {\n return `${LOGIN_URL}?redirect_url=${encodeURIComponent(redirectUrl)}`;\n}\n\nexport function profileUrl(redirectUrl?: string): string {\n if (redirectUrl) {\n return `${PROFILE_PAGE_URL}?redirect_url=${encodeURIComponent(redirectUrl)}`;\n }\n return PROFILE_PAGE_URL;\n}\n\nexport async function logout(): Promise<void> {\n await fetch(LOGOUT_URL, {\n method: \"POST\",\n credentials: \"include\",\n });\n}\n\nexport async function refreshToken(): Promise<boolean> {\n const res = await fetch(REFRESH_URL, {\n method: \"POST\",\n credentials: \"include\",\n });\n return res.ok;\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;;;AC7BA,SAAS,WAAW,gBAAgB;AACpC,SAAS,iBAAiB;;;ACInB,SAAS,SAAS,aAA6B;AACpD,SAAO,GAAG,SAAS,iBAAiB,mBAAmB,WAAW,CAAC;AACrE;AAEO,SAAS,WAAW,aAA8B;AACvD,MAAI,aAAa;AACf,WAAO,GAAG,gBAAgB,iBAAiB,mBAAmB,WAAW,CAAC;AAAA,EAC5E;AACA,SAAO;AACT;AAEA,eAAsB,SAAwB;AAC5C,QAAM,MAAM,YAAY;AAAA,IACtB,QAAQ;AAAA,IACR,aAAa;AAAA,EACf,CAAC;AACH;AAEA,eAAsB,eAAiC;AACrD,QAAM,MAAM,MAAM,MAAM,aAAa;AAAA,IACnC,QAAQ;AAAA,IACR,aAAa;AAAA,EACf,CAAC;AACD,SAAO,IAAI;AACb;;;ADxBO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AACF,GAGG;AACD,QAAM,EAAE,gBAAgB,IAAI,QAAQ;AACpC,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,QAAQ,SAAS,IAAI,SAA+B,SAAS;AAEpE,YAAU,MAAM;AACd,QAAI,gBAAiB;AAErB,QAAI,SAAS;AAEb,iBAAa,EAAE,KAAK,CAAC,OAAO;AAC1B,UAAI,OAAQ;AACZ,UAAI,IAAI;AACN,eAAO,QAAQ;AAAA,MACjB,OAAO;AACL,kBAAU,QAAQ;AAAA,MACpB;AAAA,IACF,CAAC;AAED,WAAO,MAAM;AACX,eAAS;AAAA,IACX;AAAA,EACF,GAAG,CAAC,iBAAiB,MAAM,CAAC;AAE5B,MAAI,gBAAiB,QAAO;AAC5B,MAAI,WAAW,UAAW,QAAO,YAAY;AAC7C,SAAO,YAAY;AACrB;","names":[]}
|