@thinkingcat/auth-utils 1.0.46 → 1.0.48

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.
Files changed (2) hide show
  1. package/dist/index.js +68 -130
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,77 +1,4 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.verifyToken = verifyToken;
37
- exports.extractRoleFromPayload = extractRoleFromPayload;
38
- exports.createNextAuthJWT = createNextAuthJWT;
39
- exports.encodeNextAuthToken = encodeNextAuthToken;
40
- exports.setCustomTokens = setCustomTokens;
41
- exports.setNextAuthToken = setNextAuthToken;
42
- exports.createRedirectHTML = createRedirectHTML;
43
- exports.createAuthResponse = createAuthResponse;
44
- exports.validateServiceSubscription = validateServiceSubscription;
45
- exports.refreshSSOToken = refreshSSOToken;
46
- exports.getRefreshTokenFromSSO = getRefreshTokenFromSSO;
47
- exports.verifyAndRefreshToken = verifyAndRefreshToken;
48
- exports.redirectToError = redirectToError;
49
- exports.clearAuthCookies = clearAuthCookies;
50
- exports.getEffectiveRole = getEffectiveRole;
51
- exports.requiresSubscription = requiresSubscription;
52
- exports.createNextAuthCookies = createNextAuthCookies;
53
- exports.createNextAuthBaseConfig = createNextAuthBaseConfig;
54
- exports.createInitialJWTToken = createInitialJWTToken;
55
- exports.createEmptySession = createEmptySession;
56
- exports.mapTokenToSession = mapTokenToSession;
57
- exports.handleJWTCallback = handleJWTCallback;
58
- exports.getJWTFromCustomTokenCookie = getJWTFromCustomTokenCookie;
59
- exports.checkLicenseKey = checkLicenseKey;
60
- exports.checkRoleAccess = checkRoleAccess;
61
- exports.redirectToSSOLogin = redirectToSSOLogin;
62
- exports.redirectToRoleDashboard = redirectToRoleDashboard;
63
- exports.isTokenExpired = isTokenExpired;
64
- exports.isValidToken = isValidToken;
65
- exports.hasRole = hasRole;
66
- exports.hasAnyRole = hasAnyRole;
67
- exports.isPublicPath = isPublicPath;
68
- exports.isApiPath = isApiPath;
69
- exports.isProtectedApiPath = isProtectedApiPath;
70
- exports.checkAuthentication = checkAuthentication;
71
- exports.verifyAndRefreshTokenWithNextAuth = verifyAndRefreshTokenWithNextAuth;
72
- exports.createMiddlewareConfig = createMiddlewareConfig;
73
- exports.handleMiddleware = handleMiddleware;
74
- const jose_1 = require("jose");
1
+ import { jwtVerify, EncryptJWT } from 'jose';
75
2
  // ============================================================================
76
3
  // UTILITY FUNCTIONS
77
4
  // ============================================================================
@@ -102,7 +29,7 @@ async function createHashSHA256(data) {
102
29
  * Edge Runtime 호환을 위해 next/server를 동적 import
103
30
  */
104
31
  async function getNextServer() {
105
- return await Promise.resolve().then(() => __importStar(require('next/server')));
32
+ return await import('next/server');
106
33
  }
107
34
  /**
108
35
  * NextAuth 세션 토큰 쿠키를 삭제하는 헬퍼 함수
@@ -129,10 +56,10 @@ function clearAllAuthCookies(response, cookiePrefix, isProduction) {
129
56
  * @param secret JWT 서명에 사용할 secret key
130
57
  * @returns 검증된 payload 또는 null
131
58
  */
132
- async function verifyToken(accessToken, secret) {
59
+ export async function verifyToken(accessToken, secret) {
133
60
  try {
134
61
  const secretBytes = new TextEncoder().encode(secret);
135
- const { payload } = await (0, jose_1.jwtVerify)(accessToken, secretBytes);
62
+ const { payload } = await jwtVerify(accessToken, secretBytes);
136
63
  if (payload && typeof payload === 'object' && payload.email) {
137
64
  return { payload: payload };
138
65
  }
@@ -149,7 +76,7 @@ async function verifyToken(accessToken, secret) {
149
76
  * @param defaultRole 기본 역할 (기본값: 'ADMIN')
150
77
  * @returns 추출된 역할
151
78
  */
152
- function extractRoleFromPayload(payload, serviceId, defaultRole = 'ADMIN') {
79
+ export function extractRoleFromPayload(payload, serviceId, defaultRole = 'ADMIN') {
153
80
  const services = payload.services || [];
154
81
  const service = services.find((s) => s.serviceId === serviceId);
155
82
  return service?.role || payload.role || defaultRole;
@@ -160,7 +87,7 @@ function extractRoleFromPayload(payload, serviceId, defaultRole = 'ADMIN') {
160
87
  * @param serviceId 서비스 ID (필수)
161
88
  * @returns NextAuth JWT 객체
162
89
  */
163
- function createNextAuthJWT(payload, serviceId) {
90
+ export function createNextAuthJWT(payload, serviceId) {
164
91
  const services = payload.services || [];
165
92
  const service = services.find((s) => s.serviceId === serviceId);
166
93
  const effectiveRole = service?.role || payload.role || 'ADMIN';
@@ -216,10 +143,10 @@ function createNextAuthJWT(payload, serviceId) {
216
143
  * @param maxAge 토큰 유효 기간 (초, 기본값: 30일)
217
144
  * @returns 인코딩된 세션 토큰
218
145
  */
219
- async function encodeNextAuthToken(jwt, secret, maxAge = 30 * 24 * 60 * 60) {
146
+ export async function encodeNextAuthToken(jwt, secret, maxAge = 30 * 24 * 60 * 60) {
220
147
  // NextAuth의 encode() 함수를 우선적으로 사용 (가장 호환성 좋음)
221
148
  try {
222
- const { encode } = await Promise.resolve().then(() => __importStar(require('next-auth/jwt')));
149
+ const { encode } = await import('next-auth/jwt');
223
150
  debugLog('encodeNextAuthToken', 'Using next-auth/jwt encode');
224
151
  const encoded = await encode({
225
152
  token: jwt,
@@ -244,7 +171,7 @@ async function encodeNextAuthToken(jwt, secret, maxAge = 30 * 24 * 60 * 60) {
244
171
  // EncryptJWT를 사용하여 JWE 토큰 생성
245
172
  // NextAuth는 'dir' 키 관리와 'A256GCM' 암호화를 사용
246
173
  try {
247
- const token = await new jose_1.EncryptJWT(jwt)
174
+ const token = await new EncryptJWT(jwt)
248
175
  .setProtectedHeader({
249
176
  alg: 'dir', // Direct key agreement
250
177
  enc: 'A256GCM' // AES-256-GCM encryption
@@ -262,7 +189,7 @@ async function encodeNextAuthToken(jwt, secret, maxAge = 30 * 24 * 60 * 60) {
262
189
  }
263
190
  }
264
191
  }
265
- function setCustomTokens(response, accessToken, optionsOrRefreshToken, options) {
192
+ export function setCustomTokens(response, accessToken, optionsOrRefreshToken, options) {
266
193
  // 옵션 파라미터 처리: refreshToken과 options를 분리
267
194
  let refreshTokenValue;
268
195
  let cookiePrefix;
@@ -335,7 +262,7 @@ function setCustomTokens(response, accessToken, optionsOrRefreshToken, options)
335
262
  * @param options.isProduction 프로덕션 환경 여부 (기본값: false)
336
263
  * @param options.cookieDomain 쿠키 도메인 (선택)
337
264
  */
338
- function setNextAuthToken(response, sessionToken, options = {}) {
265
+ export function setNextAuthToken(response, sessionToken, options = {}) {
339
266
  const { isProduction = false, cookieDomain, } = options;
340
267
  // createNextAuthCookies와 동일한 로직 사용
341
268
  const cookies = createNextAuthCookies({ isProduction, cookieDomain });
@@ -353,7 +280,7 @@ function setNextAuthToken(response, sessionToken, options = {}) {
353
280
  * @param text 표시할 텍스트 (필수)
354
281
  * @returns HTML 문자열
355
282
  */
356
- function createRedirectHTML(redirectPath, text) {
283
+ export function createRedirectHTML(redirectPath, text) {
357
284
  return `
358
285
  <!DOCTYPE html>
359
286
  <html>
@@ -437,7 +364,7 @@ function createRedirectHTML(redirectPath, text) {
437
364
  * @param options.licenseKey 라이센스 키 (필수)
438
365
  * @returns NextResponse 객체 (리다이렉트 또는 JSON 응답)
439
366
  */
440
- async function createAuthResponse(accessToken, secret, options) {
367
+ export async function createAuthResponse(accessToken, secret, options) {
441
368
  await checkLicenseKey(options.licenseKey);
442
369
  const { req, refreshToken, redirectPath, text, cookiePrefix, isProduction = false, cookieDomain, serviceId, } = options;
443
370
  // 1. 토큰 검증
@@ -515,7 +442,7 @@ async function createAuthResponse(accessToken, secret, options) {
515
442
  * @param ssoBaseURL SSO 서버 기본 URL (필수)
516
443
  * @returns 구독 유효성 결과
517
444
  */
518
- function validateServiceSubscription(services, serviceId, ssoBaseURL) {
445
+ export function validateServiceSubscription(services, serviceId, ssoBaseURL) {
519
446
  const filteredServices = services.filter(service => service.serviceId === serviceId);
520
447
  if (filteredServices.length === 0) {
521
448
  return {
@@ -544,7 +471,7 @@ function validateServiceSubscription(services, serviceId, ssoBaseURL) {
544
471
  * @param options.authServiceKey 인증 서비스 키 (기본값: 환경 변수)
545
472
  * @returns SSO refresh token 응답
546
473
  */
547
- async function refreshSSOToken(refreshToken, options) {
474
+ export async function refreshSSOToken(refreshToken, options) {
548
475
  const { ssoBaseURL, authServiceKey } = options;
549
476
  if (!authServiceKey) {
550
477
  throw new Error('AUTH_SERVICE_SECRET_KEY not configured');
@@ -568,7 +495,7 @@ async function refreshSSOToken(refreshToken, options) {
568
495
  * @param options.authServiceKey 인증 서비스 키 (기본값: 환경 변수)
569
496
  * @returns refresh token 또는 null
570
497
  */
571
- async function getRefreshTokenFromSSO(userId, accessToken, options) {
498
+ export async function getRefreshTokenFromSSO(userId, accessToken, options) {
572
499
  const { ssoBaseURL, authServiceKey } = options;
573
500
  if (!authServiceKey) {
574
501
  return null;
@@ -598,7 +525,7 @@ async function getRefreshTokenFromSSO(userId, accessToken, options) {
598
525
  // ============================================================================
599
526
  // TOKEN REFRESH & VERIFICATION FUNCTIONS
600
527
  // ============================================================================
601
- async function verifyAndRefreshToken(req, secret, options) {
528
+ export async function verifyAndRefreshToken(req, secret, options) {
602
529
  const { cookiePrefix, serviceId, isProduction, cookieDomain, text, ssoBaseURL, authServiceKey, forceRefresh = false, } = options;
603
530
  // 1. access_token 쿠키 확인
604
531
  // forceRefresh가 true이면 access token이 있어도 refresh를 시도
@@ -607,7 +534,7 @@ async function verifyAndRefreshToken(req, secret, options) {
607
534
  if (accessToken && !forceRefresh) {
608
535
  try {
609
536
  const secretBytes = new TextEncoder().encode(secret);
610
- const { payload } = await (0, jose_1.jwtVerify)(accessToken, secretBytes);
537
+ const { payload } = await jwtVerify(accessToken, secretBytes);
611
538
  if (payload && typeof payload === 'object' && payload.email) {
612
539
  return { isValid: true, payload: payload };
613
540
  }
@@ -644,7 +571,7 @@ async function verifyAndRefreshToken(req, secret, options) {
644
571
  let payload;
645
572
  try {
646
573
  const secretBytes = new TextEncoder().encode(secret);
647
- const { payload: tokenPayload } = await (0, jose_1.jwtVerify)(refreshResult.accessToken, secretBytes);
574
+ const { payload: tokenPayload } = await jwtVerify(refreshResult.accessToken, secretBytes);
648
575
  if (tokenPayload && typeof tokenPayload === 'object' && tokenPayload.email) {
649
576
  payload = tokenPayload;
650
577
  }
@@ -663,12 +590,23 @@ async function verifyAndRefreshToken(req, secret, options) {
663
590
  }
664
591
  jwt.accessTokenExpires = Date.now() + (15 * 60 * 1000);
665
592
  // NextAuth 세션 쿠키 생성
666
- // 주의: NextAuth 세션 쿠키를 자동으로 관리하므로, 직접 설정하는 것이 문제를 일으킬 수 있습니다.
667
- // 따라서 커스텀 토큰만 설정하고, NextAuth 세션은 JWT 콜백에서 처리하도록 합니다.
668
- debugLog('verifyAndRefreshToken', 'Skipping NextAuth session cookie - will be handled by NextAuth JWT callback', {
669
- hasJWT: !!jwt,
670
- jwtId: jwt?.id,
671
- });
593
+ // 미들웨어에서는 NextAuth JWT callback이 실행되지 않으므로,
594
+ // refresh NextAuth 세션 쿠키를 직접 설정해야 합니다.
595
+ try {
596
+ const encodedSessionToken = await encodeNextAuthToken(jwt, secret, 30 * 24 * 60 * 60);
597
+ setNextAuthToken(response, encodedSessionToken, {
598
+ isProduction,
599
+ cookieDomain,
600
+ });
601
+ debugLog('verifyAndRefreshToken', 'NextAuth session cookie set successfully', {
602
+ hasJWT: !!jwt,
603
+ jwtId: jwt?.id,
604
+ });
605
+ }
606
+ catch (error) {
607
+ debugError('verifyAndRefreshToken', 'Failed to set NextAuth session cookie:', error);
608
+ // NextAuth 세션 쿠키 설정 실패해도 커스텀 토큰은 설정하므로 계속 진행
609
+ }
672
610
  // 커스텀 토큰 쿠키 설정
673
611
  if (newRefreshToken) {
674
612
  setCustomTokens(response, refreshResult.accessToken, newRefreshToken, {
@@ -721,7 +659,7 @@ async function verifyAndRefreshToken(req, secret, options) {
721
659
  * @param errorPath 에러 페이지 경로 (기본값: '/error')
722
660
  * @returns NextResponse 리다이렉트 응답
723
661
  */
724
- async function redirectToError(req, code, message, errorPath = '/error') {
662
+ export async function redirectToError(req, code, message, errorPath = '/error') {
725
663
  const url = new URL(errorPath, req.url);
726
664
  url.searchParams.set('code', code);
727
665
  url.searchParams.set('message', message);
@@ -734,7 +672,7 @@ async function redirectToError(req, code, message, errorPath = '/error') {
734
672
  * @param cookiePrefix 쿠키 이름 접두사 (필수)
735
673
  * @returns NextResponse 객체
736
674
  */
737
- function clearAuthCookies(response, cookiePrefix) {
675
+ export function clearAuthCookies(response, cookiePrefix) {
738
676
  response.cookies.delete(`${cookiePrefix}_access_token`);
739
677
  response.cookies.delete(`${cookiePrefix}_refresh_token`);
740
678
  return response;
@@ -745,7 +683,7 @@ function clearAuthCookies(response, cookiePrefix) {
745
683
  * @param serviceId 서비스 ID (필수)
746
684
  * @returns 추출된 역할 또는 undefined
747
685
  */
748
- function getEffectiveRole(token, serviceId) {
686
+ export function getEffectiveRole(token, serviceId) {
749
687
  if (!token)
750
688
  return undefined;
751
689
  // token이 이미 JWT 객체인 경우 role을 직접 사용
@@ -765,7 +703,7 @@ function getEffectiveRole(token, serviceId) {
765
703
  * @param systemAdminRole 시스템 관리자 역할 (기본값: 'SYSTEM_ADMIN')
766
704
  * @returns 구독이 필요한지 여부
767
705
  */
768
- function requiresSubscription(pathname, role, subscriptionRequiredPaths, systemAdminRole = 'SYSTEM_ADMIN') {
706
+ export function requiresSubscription(pathname, role, subscriptionRequiredPaths, systemAdminRole = 'SYSTEM_ADMIN') {
769
707
  // 시스템 관리자는 구독 확인 제외
770
708
  if (role === systemAdminRole) {
771
709
  return false;
@@ -787,7 +725,7 @@ const VALID_LICENSE_KEY_HASHES = new Set([
787
725
  * @param options.cookieDomain 쿠키 도메인 (선택)
788
726
  * @returns NextAuth 쿠키 설정 객체
789
727
  */
790
- function createNextAuthCookies(options) {
728
+ export function createNextAuthCookies(options) {
791
729
  const { isProduction = false, cookieDomain } = options;
792
730
  const isSecure = isProduction;
793
731
  // cookieDomain이 설정되어 있으면 같은 도메인/서브도메인 간 쿠키 공유를 위해 'lax' 사용
@@ -839,7 +777,7 @@ function createNextAuthCookies(options) {
839
777
  * @param options.jwtMaxAge JWT 최대 유지 시간 (초, 기본값: 30일)
840
778
  * @returns NextAuth 기본 설정 객체
841
779
  */
842
- function createNextAuthBaseConfig(options) {
780
+ export function createNextAuthBaseConfig(options) {
843
781
  const { secret, isProduction = false, cookieDomain, signInPath = '/login', errorPath = '/login', nextAuthUrl, sessionMaxAge = 30 * 24 * 60 * 60, // 30일
844
782
  jwtMaxAge = 30 * 24 * 60 * 60, // 30일
845
783
  } = options;
@@ -868,7 +806,7 @@ function createNextAuthBaseConfig(options) {
868
806
  * @param account 계정 정보
869
807
  * @returns 업데이트된 JWT 토큰
870
808
  */
871
- function createInitialJWTToken(token, user, account) {
809
+ export function createInitialJWTToken(token, user, account) {
872
810
  return {
873
811
  ...token,
874
812
  id: user.id,
@@ -891,7 +829,7 @@ function createInitialJWTToken(token, user, account) {
891
829
  * @param session 기존 세션
892
830
  * @returns 빈 세션 객체
893
831
  */
894
- function createEmptySession(session) {
832
+ export function createEmptySession(session) {
895
833
  return {
896
834
  ...session,
897
835
  user: {
@@ -909,7 +847,7 @@ function createEmptySession(session) {
909
847
  * @param token JWT 토큰
910
848
  * @returns 업데이트된 세션
911
849
  */
912
- function mapTokenToSession(session, token) {
850
+ export function mapTokenToSession(session, token) {
913
851
  if (!session.user) {
914
852
  return session;
915
853
  }
@@ -941,7 +879,7 @@ function mapTokenToSession(session, token) {
941
879
  * @param options.debug 디버깅 로그 출력 여부 (기본값: false)
942
880
  * @returns 업데이트된 JWT 토큰
943
881
  */
944
- async function handleJWTCallback(token, user, account, options) {
882
+ export async function handleJWTCallback(token, user, account, options) {
945
883
  const { secret, licenseKey, serviceId, cookieName, debug = false, ssoBaseURL, authServiceKey, } = options || {};
946
884
  // 디버깅 로그
947
885
  if (debug) {
@@ -1052,10 +990,10 @@ async function handleJWTCallback(token, user, account, options) {
1052
990
  * @param licenseKey 라이센스 키 (필수)
1053
991
  * @returns NextAuth JWT 객체 또는 null
1054
992
  */
1055
- async function getJWTFromCustomTokenCookie(cookieName, secret, serviceId, licenseKey) {
993
+ export async function getJWTFromCustomTokenCookie(cookieName, secret, serviceId, licenseKey) {
1056
994
  debugLog('getJWTFromCustomTokenCookie', `Reading cookie: ${cookieName}`);
1057
995
  try {
1058
- const { cookies } = await Promise.resolve().then(() => __importStar(require('next/headers')));
996
+ const { cookies } = await import('next/headers');
1059
997
  const cookieStore = await cookies();
1060
998
  const accessToken = cookieStore.get(cookieName)?.value;
1061
999
  if (!accessToken) {
@@ -1092,7 +1030,7 @@ async function getJWTFromCustomTokenCookie(cookieName, secret, serviceId, licens
1092
1030
  // ============================================================================
1093
1031
  // LICENSE & AUTHORIZATION FUNCTIONS
1094
1032
  // ============================================================================
1095
- async function checkLicenseKey(licenseKey) {
1033
+ export async function checkLicenseKey(licenseKey) {
1096
1034
  if (!licenseKey || licenseKey.length < 10) {
1097
1035
  throw new Error('License key is required');
1098
1036
  }
@@ -1101,7 +1039,7 @@ async function checkLicenseKey(licenseKey) {
1101
1039
  throw new Error('Invalid license key');
1102
1040
  }
1103
1041
  }
1104
- function checkRoleAccess(pathname, role, roleConfig) {
1042
+ export function checkRoleAccess(pathname, role, roleConfig) {
1105
1043
  // 각 역할 설정을 확인
1106
1044
  for (const [configRole, config] of Object.entries(roleConfig)) {
1107
1045
  const isPathMatch = config.paths.some(path => pathname.startsWith(path));
@@ -1127,7 +1065,7 @@ function checkRoleAccess(pathname, role, roleConfig) {
1127
1065
  * @param ssoBaseURL SSO 서버 기본 URL (필수)
1128
1066
  * @returns NextResponse 리다이렉트 응답
1129
1067
  */
1130
- async function redirectToSSOLogin(req, serviceId, ssoBaseURL) {
1068
+ export async function redirectToSSOLogin(req, serviceId, ssoBaseURL) {
1131
1069
  const { NextResponse: NextResponseClass } = await getNextServer();
1132
1070
  return NextResponseClass.redirect(new URL(`${ssoBaseURL}/auth/login?serviceId=${serviceId}`, req.url));
1133
1071
  }
@@ -1139,7 +1077,7 @@ async function redirectToSSOLogin(req, serviceId, ssoBaseURL) {
1139
1077
  * @param defaultPath 기본 경로 (기본값: '/admin')
1140
1078
  * @returns NextResponse 리다이렉트 응답
1141
1079
  */
1142
- async function redirectToRoleDashboard(req, role, rolePaths, defaultPath = '/admin') {
1080
+ export async function redirectToRoleDashboard(req, role, rolePaths, defaultPath = '/admin') {
1143
1081
  const redirectPath = rolePaths[role] || defaultPath;
1144
1082
  const { NextResponse: NextResponseClass } = await getNextServer();
1145
1083
  return NextResponseClass.redirect(new URL(redirectPath, req.url));
@@ -1152,7 +1090,7 @@ async function redirectToRoleDashboard(req, role, rolePaths, defaultPath = '/adm
1152
1090
  * @param token NextAuth JWT 객체
1153
1091
  * @returns 만료 여부
1154
1092
  */
1155
- function isTokenExpired(token) {
1093
+ export function isTokenExpired(token) {
1156
1094
  if (!token)
1157
1095
  return true;
1158
1096
  if (token.exp && typeof token.exp === 'number' && token.exp < Math.floor(Date.now() / 1000)) {
@@ -1165,7 +1103,7 @@ function isTokenExpired(token) {
1165
1103
  * @param token NextAuth JWT 객체
1166
1104
  * @returns 유효성 여부
1167
1105
  */
1168
- function isValidToken(token) {
1106
+ export function isValidToken(token) {
1169
1107
  if (!token)
1170
1108
  return false;
1171
1109
  if (isTokenExpired(token))
@@ -1181,7 +1119,7 @@ function isValidToken(token) {
1181
1119
  * @param serviceId 서비스 ID (필수)
1182
1120
  * @returns 역할 보유 여부
1183
1121
  */
1184
- function hasRole(token, role, serviceId) {
1122
+ export function hasRole(token, role, serviceId) {
1185
1123
  if (!token)
1186
1124
  return false;
1187
1125
  const effectiveRole = getEffectiveRole(token, serviceId);
@@ -1194,7 +1132,7 @@ function hasRole(token, role, serviceId) {
1194
1132
  * @param serviceId 서비스 ID (필수)
1195
1133
  * @returns 역할 보유 여부
1196
1134
  */
1197
- function hasAnyRole(token, roles, serviceId) {
1135
+ export function hasAnyRole(token, roles, serviceId) {
1198
1136
  if (!token)
1199
1137
  return false;
1200
1138
  const effectiveRole = getEffectiveRole(token, serviceId);
@@ -1209,7 +1147,7 @@ function hasAnyRole(token, roles, serviceId) {
1209
1147
  * @param publicPaths 공개 경로 배열
1210
1148
  * @returns 공개 경로 여부
1211
1149
  */
1212
- function isPublicPath(pathname, publicPaths) {
1150
+ export function isPublicPath(pathname, publicPaths) {
1213
1151
  return publicPaths.some(path => pathname === path || pathname.startsWith(path));
1214
1152
  }
1215
1153
  /**
@@ -1217,7 +1155,7 @@ function isPublicPath(pathname, publicPaths) {
1217
1155
  * @param pathname 경로명
1218
1156
  * @returns API 경로 여부
1219
1157
  */
1220
- function isApiPath(pathname) {
1158
+ export function isApiPath(pathname) {
1221
1159
  return pathname.startsWith('/api/');
1222
1160
  }
1223
1161
  /**
@@ -1226,7 +1164,7 @@ function isApiPath(pathname) {
1226
1164
  * @param exemptPaths 제외할 경로 배열
1227
1165
  * @returns 보호된 API 경로 여부
1228
1166
  */
1229
- function isProtectedApiPath(pathname, exemptPaths = []) {
1167
+ export function isProtectedApiPath(pathname, exemptPaths = []) {
1230
1168
  if (!isApiPath(pathname))
1231
1169
  return false;
1232
1170
  return !exemptPaths.some(path => pathname.startsWith(path));
@@ -1241,7 +1179,7 @@ function isProtectedApiPath(pathname, exemptPaths = []) {
1241
1179
  * @param options 옵션
1242
1180
  * @returns 인증 결과
1243
1181
  */
1244
- async function checkAuthentication(req, secret, options) {
1182
+ export async function checkAuthentication(req, secret, options) {
1245
1183
  const { cookiePrefix, serviceId, getNextAuthToken, } = options;
1246
1184
  let nextAuthToken = null;
1247
1185
  if (getNextAuthToken) {
@@ -1281,7 +1219,7 @@ async function checkAuthentication(req, secret, options) {
1281
1219
  * @param options 옵션
1282
1220
  * @returns 인증 결과
1283
1221
  */
1284
- async function verifyAndRefreshTokenWithNextAuth(req, nextAuthToken, secret, options) {
1222
+ export async function verifyAndRefreshTokenWithNextAuth(req, nextAuthToken, secret, options) {
1285
1223
  const { cookiePrefix, isProduction } = options;
1286
1224
  // NextAuth 세션 토큰 쿠키 확인
1287
1225
  const nextAuthSessionTokenCookieName = isProduction
@@ -1304,7 +1242,7 @@ async function verifyAndRefreshTokenWithNextAuth(req, nextAuthToken, secret, opt
1304
1242
  if (accessToken) {
1305
1243
  try {
1306
1244
  const secretBytes = new TextEncoder().encode(secret);
1307
- const { payload } = await (0, jose_1.jwtVerify)(accessToken, secretBytes);
1245
+ const { payload } = await jwtVerify(accessToken, secretBytes);
1308
1246
  if (payload && typeof payload === 'object' && payload.email) {
1309
1247
  hasValidAccessToken = true;
1310
1248
  }
@@ -1330,7 +1268,7 @@ async function verifyAndRefreshTokenWithNextAuth(req, nextAuthToken, secret, opt
1330
1268
  if (accessToken) {
1331
1269
  try {
1332
1270
  const secretBytes = new TextEncoder().encode(secret);
1333
- const result = await (0, jose_1.jwtVerify)(accessToken, secretBytes);
1271
+ const result = await jwtVerify(accessToken, secretBytes);
1334
1272
  payload = result.payload;
1335
1273
  }
1336
1274
  catch {
@@ -1369,7 +1307,7 @@ async function verifyAndRefreshTokenWithNextAuth(req, nextAuthToken, secret, opt
1369
1307
  if (accessToken && hasValidAccessToken) {
1370
1308
  try {
1371
1309
  const secretBytes = new TextEncoder().encode(secret);
1372
- const result = await (0, jose_1.jwtVerify)(accessToken, secretBytes);
1310
+ const result = await jwtVerify(accessToken, secretBytes);
1373
1311
  payload = result.payload;
1374
1312
  }
1375
1313
  catch {
@@ -1394,7 +1332,7 @@ async function verifyAndRefreshTokenWithNextAuth(req, nextAuthToken, secret, opt
1394
1332
  * @param defaults 기본 설정값 (선택사항, 제공하지 않으면 최소 기본값 사용)
1395
1333
  * @returns 미들웨어 설정 객체
1396
1334
  */
1397
- function createMiddlewareConfig(config, defaults) {
1335
+ export function createMiddlewareConfig(config, defaults) {
1398
1336
  // 기본값 설정
1399
1337
  const defaultPublicPaths = defaults?.publicPaths || [
1400
1338
  '/robots.txt',
@@ -1460,7 +1398,7 @@ function createMiddlewareConfig(config, defaults) {
1460
1398
  * @param options 미들웨어 실행 옵션 (secret 필수)
1461
1399
  * @returns NextResponse 또는 null (다음 미들웨어로 진행)
1462
1400
  */
1463
- async function handleMiddleware(req, config, options) {
1401
+ export async function handleMiddleware(req, config, options) {
1464
1402
  // Edge Runtime 호환을 위해 next/server를 한 번만 import
1465
1403
  const { NextResponse: NextResponseClass } = await getNextServer();
1466
1404
  try {
@@ -1480,7 +1418,7 @@ async function handleMiddleware(req, config, options) {
1480
1418
  }
1481
1419
  else {
1482
1420
  try {
1483
- const { getToken } = await Promise.resolve().then(() => __importStar(require('next-auth/jwt')));
1421
+ const { getToken } = await import('next-auth/jwt');
1484
1422
  token = await getToken({ req, secret });
1485
1423
  debugLog('handleMiddleware', 'getToken result:', { hasToken: !!token });
1486
1424
  }
@@ -1653,7 +1591,7 @@ async function handleMiddleware(req, config, options) {
1653
1591
  }
1654
1592
  else {
1655
1593
  try {
1656
- const { getToken } = await Promise.resolve().then(() => __importStar(require('next-auth/jwt')));
1594
+ const { getToken } = await import('next-auth/jwt');
1657
1595
  finalToken = await getToken({ req, secret });
1658
1596
  }
1659
1597
  catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thinkingcat/auth-utils",
3
- "version": "1.0.46",
3
+ "version": "1.0.48",
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",