@thinkingcat/auth-utils 1.0.17 → 1.0.19

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/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { JWT } from "next-auth/jwt";
1
+ import type { JWT } from "next-auth/jwt";
2
2
  import type { Session } from "next-auth";
3
- import { NextResponse, NextRequest } from 'next/server';
3
+ import type { NextResponse, NextRequest } from 'next/server';
4
4
  export interface ResponseLike {
5
5
  cookies: {
6
6
  delete(name: string): void;
@@ -239,7 +239,7 @@ export declare function verifyAndRefreshToken(req: NextRequest, secret: string,
239
239
  * @param errorPath 에러 페이지 경로 (기본값: '/error')
240
240
  * @returns NextResponse 리다이렉트 응답
241
241
  */
242
- export declare function redirectToError(req: NextRequest, code: string, message: string, errorPath?: string): NextResponse;
242
+ export declare function redirectToError(req: NextRequest, code: string, message: string, errorPath?: string): Promise<NextResponse>;
243
243
  /**
244
244
  * 인증 쿠키를 삭제하는 헬퍼 함수
245
245
  * @param response NextResponse 객체
@@ -491,7 +491,7 @@ export declare function checkRoleAccess(pathname: string, role: string, roleConf
491
491
  * @param ssoBaseURL SSO 서버 기본 URL (필수)
492
492
  * @returns NextResponse 리다이렉트 응답
493
493
  */
494
- export declare function redirectToSSOLogin(req: NextRequest, serviceId: string, ssoBaseURL: string): NextResponse;
494
+ export declare function redirectToSSOLogin(req: NextRequest, serviceId: string, ssoBaseURL: string): Promise<NextResponse>;
495
495
  /**
496
496
  * 역할별 대시보드 경로로 리다이렉트하는 헬퍼 함수
497
497
  * @param req NextRequest 객체
@@ -500,7 +500,7 @@ export declare function redirectToSSOLogin(req: NextRequest, serviceId: string,
500
500
  * @param defaultPath 기본 경로 (기본값: '/admin')
501
501
  * @returns NextResponse 리다이렉트 응답
502
502
  */
503
- export declare function redirectToRoleDashboard(req: NextRequest, role: string, rolePaths: Record<string, string>, defaultPath?: string): NextResponse;
503
+ export declare function redirectToRoleDashboard(req: NextRequest, role: string, rolePaths: Record<string, string>, defaultPath?: string): Promise<NextResponse>;
504
504
  /**
505
505
  * 토큰이 만료되었는지 확인하는 함수
506
506
  * @param token NextAuth JWT 객체
package/dist/index.js CHANGED
@@ -71,9 +71,7 @@ exports.checkAuthentication = checkAuthentication;
71
71
  exports.verifyAndRefreshTokenWithNextAuth = verifyAndRefreshTokenWithNextAuth;
72
72
  exports.createMiddlewareConfig = createMiddlewareConfig;
73
73
  exports.handleMiddleware = handleMiddleware;
74
- const jwt_1 = require("next-auth/jwt");
75
74
  const jose_1 = require("jose");
76
- const server_1 = require("next/server");
77
75
  // ============================================================================
78
76
  // UTILITY FUNCTIONS
79
77
  // ============================================================================
@@ -100,6 +98,12 @@ async function createHashSHA256(data) {
100
98
  const hashArray = Array.from(new Uint8Array(hashBuffer));
101
99
  return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
102
100
  }
101
+ /**
102
+ * Edge Runtime 호환을 위해 next/server를 동적 import
103
+ */
104
+ async function getNextServer() {
105
+ return await Promise.resolve().then(() => __importStar(require('next/server')));
106
+ }
103
107
  /**
104
108
  * NextAuth 세션 토큰 쿠키를 삭제하는 헬퍼 함수
105
109
  */
@@ -208,8 +212,9 @@ function createNextAuthJWT(payload, serviceId) {
208
212
  */
209
213
  async function encodeNextAuthToken(jwt, secret, maxAge = 30 * 24 * 60 * 60) {
210
214
  try {
211
- // next-auth/jwt의 encode 함수 사용 (Node.js 런타임에서만 작동)
212
- return await (0, jwt_1.encode)({
215
+ // next-auth/jwt의 encode 함수를 동적 import로 사용 (Edge Runtime 호환)
216
+ const { encode } = await Promise.resolve().then(() => __importStar(require('next-auth/jwt')));
217
+ return await encode({
213
218
  token: jwt,
214
219
  secret: secret,
215
220
  maxAge: maxAge,
@@ -428,7 +433,8 @@ async function createAuthResponse(accessToken, secret, options) {
428
433
  ? createRedirectHTML(redirectPath, displayText)
429
434
  : createRedirectHTML('', displayText).replace("window.location.href = ''", "window.location.reload()");
430
435
  // 6. Response 생성
431
- const response = new server_1.NextResponse(html, {
436
+ const { NextResponse: NextResponseClass } = await getNextServer();
437
+ const response = new NextResponseClass(html, {
432
438
  status: 200,
433
439
  headers: {
434
440
  'Content-Type': 'text/html',
@@ -617,14 +623,16 @@ async function verifyAndRefreshToken(req, secret, options) {
617
623
  }
618
624
  else {
619
625
  debugLog('verifyAndRefreshToken', 'Refresh failed, clearing all cookies');
620
- const response = server_1.NextResponse.next();
626
+ const { NextResponse: NextResponseClass } = await getNextServer();
627
+ const response = NextResponseClass.next();
621
628
  clearAllAuthCookies(response, options.cookiePrefix, options.isProduction);
622
629
  return { isValid: false, response, error: 'REFRESH_FAILED' };
623
630
  }
624
631
  }
625
632
  catch (error) {
626
633
  debugError('verifyAndRefreshToken', 'Token refresh error:', error);
627
- const response = server_1.NextResponse.next();
634
+ const { NextResponse: NextResponseClass } = await getNextServer();
635
+ const response = NextResponseClass.next();
628
636
  clearAllAuthCookies(response, options.cookiePrefix, options.isProduction);
629
637
  return { isValid: false, response, error: 'REFRESH_ERROR' };
630
638
  }
@@ -642,11 +650,12 @@ async function verifyAndRefreshToken(req, secret, options) {
642
650
  * @param errorPath 에러 페이지 경로 (기본값: '/error')
643
651
  * @returns NextResponse 리다이렉트 응답
644
652
  */
645
- function redirectToError(req, code, message, errorPath = '/error') {
653
+ async function redirectToError(req, code, message, errorPath = '/error') {
646
654
  const url = new URL(errorPath, req.url);
647
655
  url.searchParams.set('code', code);
648
656
  url.searchParams.set('message', message);
649
- return server_1.NextResponse.redirect(url);
657
+ const { NextResponse: NextResponseClass } = await getNextServer();
658
+ return NextResponseClass.redirect(url);
650
659
  }
651
660
  /**
652
661
  * 인증 쿠키를 삭제하는 헬퍼 함수
@@ -971,8 +980,9 @@ function checkRoleAccess(pathname, role, roleConfig) {
971
980
  * @param ssoBaseURL SSO 서버 기본 URL (필수)
972
981
  * @returns NextResponse 리다이렉트 응답
973
982
  */
974
- function redirectToSSOLogin(req, serviceId, ssoBaseURL) {
975
- return server_1.NextResponse.redirect(new URL(`${ssoBaseURL}/auth/login?serviceId=${serviceId}`, req.url));
983
+ async function redirectToSSOLogin(req, serviceId, ssoBaseURL) {
984
+ const { NextResponse: NextResponseClass } = await getNextServer();
985
+ return NextResponseClass.redirect(new URL(`${ssoBaseURL}/auth/login?serviceId=${serviceId}`, req.url));
976
986
  }
977
987
  /**
978
988
  * 역할별 대시보드 경로로 리다이렉트하는 헬퍼 함수
@@ -982,9 +992,10 @@ function redirectToSSOLogin(req, serviceId, ssoBaseURL) {
982
992
  * @param defaultPath 기본 경로 (기본값: '/admin')
983
993
  * @returns NextResponse 리다이렉트 응답
984
994
  */
985
- function redirectToRoleDashboard(req, role, rolePaths, defaultPath = '/admin') {
995
+ async function redirectToRoleDashboard(req, role, rolePaths, defaultPath = '/admin') {
986
996
  const redirectPath = rolePaths[role] || defaultPath;
987
- return server_1.NextResponse.redirect(new URL(redirectPath, req.url));
997
+ const { NextResponse: NextResponseClass } = await getNextServer();
998
+ return NextResponseClass.redirect(new URL(redirectPath, req.url));
988
999
  }
989
1000
  // ============================================================================
990
1001
  // TOKEN VALIDATION UTILITIES
@@ -1250,6 +1261,8 @@ function createMiddlewareConfig(config, defaults) {
1250
1261
  * @returns NextResponse 또는 null (다음 미들웨어로 진행)
1251
1262
  */
1252
1263
  async function handleMiddleware(req, config, options) {
1264
+ // Edge Runtime 호환을 위해 next/server를 한 번만 import
1265
+ const { NextResponse: NextResponseClass } = await getNextServer();
1253
1266
  try {
1254
1267
  const pathname = req.nextUrl.pathname;
1255
1268
  const { secret, isProduction, cookieDomain, getNextAuthToken, licenseKey } = options;
@@ -1282,10 +1295,10 @@ async function handleMiddleware(req, config, options) {
1282
1295
  // 1. API 요청 처리
1283
1296
  if (pathname.startsWith('/api/')) {
1284
1297
  if (config.authApiPaths.includes(pathname)) {
1285
- return server_1.NextResponse.next();
1298
+ return NextResponseClass.next();
1286
1299
  }
1287
1300
  if (config.subscriptionExemptApiPaths.some((path) => pathname.startsWith(path))) {
1288
- return server_1.NextResponse.next();
1301
+ return NextResponseClass.next();
1289
1302
  }
1290
1303
  const authCheck = await verifyAndRefreshTokenWithNextAuth(req, token, secret, {
1291
1304
  cookiePrefix,
@@ -1301,10 +1314,10 @@ async function handleMiddleware(req, config, options) {
1301
1314
  return authCheck.response;
1302
1315
  }
1303
1316
  if (!authCheck.isValid) {
1304
- const response = redirectToError(req, 'UNAUTHORIZED', '인증이 필요합니다.', config.errorPath);
1317
+ const response = await redirectToError(req, 'UNAUTHORIZED', '인증이 필요합니다.', config.errorPath);
1305
1318
  return clearAuthCookies(response, cookiePrefix);
1306
1319
  }
1307
- return server_1.NextResponse.next();
1320
+ return NextResponseClass.next();
1308
1321
  }
1309
1322
  // 2. 루트 경로 처리 - SSO 토큰 처리 (인증 체크보다 먼저!)
1310
1323
  if (pathname === '/') {
@@ -1347,26 +1360,26 @@ async function handleMiddleware(req, config, options) {
1347
1360
  catch (error) {
1348
1361
  debugError('handleMiddleware', 'Error processing token:', error);
1349
1362
  const ssoBaseURL = options.ssoBaseURL;
1350
- return redirectToSSOLogin(req, serviceId, ssoBaseURL);
1363
+ return await redirectToSSOLogin(req, serviceId, ssoBaseURL);
1351
1364
  }
1352
1365
  }
1353
1366
  // 토큰이 없고 이미 인증된 경우 역할별 대시보드로 리다이렉트
1354
1367
  if (token && effectiveRole) {
1355
- return redirectToRoleDashboard(req, effectiveRole, config.rolePaths);
1368
+ return await redirectToRoleDashboard(req, effectiveRole, config.rolePaths);
1356
1369
  }
1357
1370
  // 인증되지 않은 경우 SSO 로그인 페이지로 리다이렉트
1358
1371
  const ssoBaseURL = options.ssoBaseURL;
1359
- return redirectToSSOLogin(req, serviceId, ssoBaseURL);
1372
+ return await redirectToSSOLogin(req, serviceId, ssoBaseURL);
1360
1373
  }
1361
1374
  // 3. 공개 경로 처리
1362
1375
  if (config.publicPaths.some((path) => pathname === path || pathname.startsWith(path))) {
1363
1376
  if (pathname === '/error' || pathname === '/verification') {
1364
- return server_1.NextResponse.next();
1377
+ return NextResponseClass.next();
1365
1378
  }
1366
1379
  if (token && effectiveRole) {
1367
- return redirectToRoleDashboard(req, effectiveRole, config.rolePaths);
1380
+ return await redirectToRoleDashboard(req, effectiveRole, config.rolePaths);
1368
1381
  }
1369
- return server_1.NextResponse.next();
1382
+ return NextResponseClass.next();
1370
1383
  }
1371
1384
  // 4. 인증 체크
1372
1385
  const authCheck = await verifyAndRefreshTokenWithNextAuth(req, token, secret, {
@@ -1384,7 +1397,7 @@ async function handleMiddleware(req, config, options) {
1384
1397
  }
1385
1398
  if (!authCheck.isValid) {
1386
1399
  const ssoBaseURL = options.ssoBaseURL;
1387
- return redirectToSSOLogin(req, serviceId, ssoBaseURL);
1400
+ return await redirectToSSOLogin(req, serviceId, ssoBaseURL);
1388
1401
  }
1389
1402
  // 5. 토큰 확인 및 변환
1390
1403
  let finalToken = token;
@@ -1413,24 +1426,24 @@ async function handleMiddleware(req, config, options) {
1413
1426
  }
1414
1427
  if (!finalToken) {
1415
1428
  const ssoBaseURL = options.ssoBaseURL;
1416
- return redirectToSSOLogin(req, serviceId, ssoBaseURL);
1429
+ return await redirectToSSOLogin(req, serviceId, ssoBaseURL);
1417
1430
  }
1418
1431
  // 6. 토큰 에러 체크
1419
1432
  if (finalToken.error === "RefreshAccessTokenError") {
1420
1433
  const ssoBaseURL = options.ssoBaseURL;
1421
- return redirectToSSOLogin(req, serviceId, ssoBaseURL);
1434
+ return await redirectToSSOLogin(req, serviceId, ssoBaseURL);
1422
1435
  }
1423
1436
  // 7. 토큰 유효성 체크
1424
1437
  if (!finalToken.role || !finalToken.email) {
1425
1438
  const ssoBaseURL = options.ssoBaseURL;
1426
- return redirectToSSOLogin(req, serviceId, ssoBaseURL);
1439
+ return await redirectToSSOLogin(req, serviceId, ssoBaseURL);
1427
1440
  }
1428
1441
  // 8. 역할 기반 접근 제어
1429
1442
  const finalEffectiveRole = finalToken.role || getEffectiveRole(finalToken, serviceId) || effectiveRole || '';
1430
1443
  if (config.roleAccessConfig && Object.keys(config.roleAccessConfig).length > 0 && finalEffectiveRole) {
1431
1444
  const roleCheck = checkRoleAccess(pathname, finalEffectiveRole, config.roleAccessConfig);
1432
1445
  if (!roleCheck.allowed) {
1433
- return redirectToError(req, 'ACCESS_DENIED', roleCheck.message || '접근 권한이 없습니다.', config.errorPath);
1446
+ return await redirectToError(req, 'ACCESS_DENIED', roleCheck.message || '접근 권한이 없습니다.', config.errorPath);
1434
1447
  }
1435
1448
  }
1436
1449
  // 9. 구독 상태 확인 (시스템 관리자 제외)
@@ -1442,13 +1455,13 @@ async function handleMiddleware(req, config, options) {
1442
1455
  }
1443
1456
  const subscriptionCheck = validateServiceSubscription(services, serviceId, ssoBaseURL);
1444
1457
  if (!subscriptionCheck.isValid) {
1445
- return server_1.NextResponse.redirect(subscriptionCheck.redirectUrl);
1458
+ return NextResponseClass.redirect(subscriptionCheck.redirectUrl);
1446
1459
  }
1447
1460
  }
1448
1461
  return null;
1449
1462
  }
1450
1463
  catch (error) {
1451
1464
  debugError('handleMiddleware', 'Middleware error:', error);
1452
- return redirectToError(req, 'INTERNAL_ERROR', '서버 오류가 발생했습니다.', config.errorPath);
1465
+ return await redirectToError(req, 'INTERNAL_ERROR', '서버 오류가 발생했습니다.', config.errorPath);
1453
1466
  }
1454
1467
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thinkingcat/auth-utils",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
4
4
  "description": "Authentication utilities for ThinkingCat SSO services with conditional logging",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -40,6 +40,3 @@
40
40
  ".env.example"
41
41
  ]
42
42
  }
43
-
44
-
45
-