@thinkingcat/auth-utils 2.0.0 → 2.0.2

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 CHANGED
@@ -91,7 +91,7 @@ npm install @thinkingcat/auth-utils
91
91
  ```json
92
92
  {
93
93
  "dependencies": {
94
- "@thinkingcat/auth-utils": "^1.0.17"
94
+ "@thinkingcat/auth-utils": "^2.0.2"
95
95
  }
96
96
  }
97
97
  ```
@@ -237,7 +237,7 @@ const response = await handleMiddleware(req, middlewareConfig, {
237
237
 
238
238
  ### 토큰 검증 및 생성
239
239
 
240
- #### `verifyToken(accessToken: string, secret: string, licenseKey: string)`
240
+ #### `verifyToken(accessToken: string, secret: string)`
241
241
 
242
242
  JWT access token을 검증하고 디코딩합니다.
243
243
 
@@ -245,7 +245,6 @@ JWT access token을 검증하고 디코딩합니다.
245
245
 
246
246
  - `accessToken`: 검증할 JWT 토큰
247
247
  - `secret`: JWT 서명에 사용할 secret key
248
- - `licenseKey`: 라이센스 키 (필수)
249
248
 
250
249
  **반환값:**
251
250
 
@@ -256,8 +255,7 @@ JWT access token을 검증하고 디코딩합니다.
256
255
 
257
256
  ```typescript
258
257
  const secret = process.env.NEXTAUTH_SECRET!;
259
- const licenseKey = process.env.LICENSE_KEY!;
260
- const result = await verifyToken(accessToken, secret, licenseKey);
258
+ const result = await verifyToken(accessToken, secret);
261
259
 
262
260
  if (result) {
263
261
  const { payload } = result;
@@ -1054,8 +1052,8 @@ export async function GET(req: NextRequest) {
1054
1052
  }
1055
1053
 
1056
1054
  const secret = process.env.NEXTAUTH_SECRET!;
1057
- const licenseKey = process.env.LICENSE_KEY!;
1058
- const tokenResult = await verifyToken(tokenParam, secret, licenseKey);
1055
+
1056
+ const tokenResult = await verifyToken(tokenParam, secret);
1059
1057
 
1060
1058
  if (!tokenResult) {
1061
1059
  return NextResponse.redirect("/login");
@@ -1096,8 +1094,8 @@ export async function GET(req: NextRequest) {
1096
1094
  }
1097
1095
 
1098
1096
  const secret = process.env.NEXTAUTH_SECRET!;
1099
- const licenseKey = process.env.LICENSE_KEY!;
1100
- const tokenResult = await verifyToken(tokenParam, secret, licenseKey);
1097
+
1098
+ const tokenResult = await verifyToken(tokenParam, secret);
1101
1099
 
1102
1100
  if (!tokenResult) {
1103
1101
  return NextResponse.redirect("/login");
@@ -1140,10 +1138,10 @@ export async function GET(req: NextRequest) {
1140
1138
  }
1141
1139
 
1142
1140
  const secret = process.env.NEXTAUTH_SECRET!;
1143
- const licenseKey = process.env.LICENSE_KEY!;
1141
+
1144
1142
  const isProduction = process.env.NODE_ENV === "production";
1145
1143
 
1146
- const tokenResult = await verifyToken(tokenParam, secret, licenseKey);
1144
+ const tokenResult = await verifyToken(tokenParam, secret);
1147
1145
  if (!tokenResult) {
1148
1146
  return NextResponse.redirect("/login");
1149
1147
  }
@@ -1490,31 +1488,27 @@ const response = await handleMiddleware(req, middlewareConfig, {
1490
1488
  ## 📦 패키지 정보
1491
1489
 
1492
1490
  - **패키지명**: `@thinkingcat/auth-utils`
1493
- - **버전**: `1.0.17`
1491
+ - **버전**: `2.0.2`
1494
1492
  - **라이선스**: MIT
1495
1493
  - **저장소**: npm registry
1496
1494
 
1497
1495
  ## 📝 변경 이력 (Changelog)
1498
1496
 
1499
- ### v1.0.17 (2024-11-15)
1497
+ ### v2.0.2 (2025-12-24)
1500
1498
 
1501
1499
  **새로운 기능:**
1502
1500
 
1503
- - 조건부 로깅 시스템 추가 (`debugLog`, `debugError`)
1504
- - 환경 변수 `AUTH_UTILS_DEBUG` 지원
1505
- - 프로덕션 환경에서 로그 출력 최적화
1501
+ - SSO 로그인 URL 경로 최적화 (`/login` -> `/auth/login`)
1502
+ - 라이센스 키(LICENSE_KEY) 필수화 및 가용성 확대
1503
+ - 타입 정의 미들웨어 핸들러의 안정성 강화
1506
1504
 
1507
1505
  **개선 사항:**
1508
1506
 
1509
- - 코드 구조 개선 (15개 기능별 섹션으로 그룹화)
1510
- - 중복 코드 제거 (`deleteNextAuthSessionCookie`, `clearAllAuthCookies` 헬퍼 추가)
1511
- - 로그 메시지 간소화 가독성 향상
1512
- - 불필요한 주석 제거
1513
-
1514
- **성능 최적화:**
1507
+ - `verifyToken` 함수에서 `licenseKey` 파라미터 제거 (내부 로직 최적화)
1508
+ - `README.md`를 최신 소스 코드 API에 맞게 전체 업데이트
1509
+ - 미들웨어 설정(`MiddlewareConfig`)옵션(`MiddlewareOptions`) 상세화
1515
1510
 
1516
- - 프로덕션 환경에서 로그 출력 비활성화로 성능 향상
1517
- - 조건부 로깅으로 런타임 오버헤드 감소
1511
+ ### v1.0.17 (2024-11-15)
1518
1512
 
1519
1513
  ## 🤝 기여 (Contributing)
1520
1514
 
@@ -1,41 +1,41 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createAuthResponse = createAuthResponse;
4
- const license_1 = require("../utils/license");
5
- const jwt_1 = require("./jwt");
6
- const server_1 = require("../utils/server");
7
- const cookies_1 = require("./cookies");
8
- const logger_1 = require("../utils/logger");
4
+ const license_js_1 = require("../utils/license.js");
5
+ const jwt_js_1 = require("./jwt.js");
6
+ const server_js_1 = require("../utils/server.js");
7
+ const cookies_js_1 = require("./cookies.js");
8
+ const logger_js_1 = require("../utils/logger.js");
9
9
  /**
10
10
  * access token과 refresh token을 사용하여 완전한 인증 세션 생성
11
11
  */
12
12
  async function createAuthResponse(accessToken, secret, options) {
13
- await (0, license_1.checkLicenseKey)(options.licenseKey);
13
+ await (0, license_js_1.checkLicenseKey)(options.licenseKey);
14
14
  const { req, refreshToken, redirectPath, text, cookiePrefix, isProduction = false, cookieDomain, serviceId, } = options;
15
- const tokenResult = await (0, jwt_1.verifyToken)(accessToken, secret);
15
+ const tokenResult = await (0, jwt_js_1.verifyToken)(accessToken, secret);
16
16
  if (!tokenResult) {
17
17
  throw new Error('Invalid token');
18
18
  }
19
19
  const { payload } = tokenResult;
20
- const jwt = (0, jwt_1.createNextAuthJWT)(payload, serviceId);
20
+ const jwt = (0, jwt_js_1.createNextAuthJWT)(payload, serviceId);
21
21
  if (refreshToken) {
22
22
  jwt.refreshToken = refreshToken;
23
23
  }
24
24
  jwt.accessTokenExpires = Date.now() + (15 * 60 * 1000);
25
- (0, logger_1.debugLog)('createAuthResponse', 'JWT prepared', { jwtId: jwt?.id });
26
- const { NextResponse: NextResponseClass } = await (0, server_1.getNextServer)();
25
+ (0, logger_js_1.debugLog)('createAuthResponse', 'JWT prepared', { jwtId: jwt?.id });
26
+ const { NextResponse: NextResponseClass } = await (0, server_js_1.getNextServer)();
27
27
  const response = redirectPath
28
28
  ? NextResponseClass.redirect(new URL(redirectPath, req.url), { status: 302 })
29
29
  : NextResponseClass.json({ success: true, message: text || 'Authentication successful' }, { status: 200 });
30
30
  if (refreshToken) {
31
- (0, cookies_1.setCustomTokens)(response, accessToken, refreshToken, {
31
+ (0, cookies_js_1.setCustomTokens)(response, accessToken, refreshToken, {
32
32
  cookiePrefix,
33
33
  isProduction,
34
34
  cookieDomain,
35
35
  });
36
36
  }
37
37
  else {
38
- (0, cookies_1.setCustomTokens)(response, accessToken, {
38
+ (0, cookies_js_1.setCustomTokens)(response, accessToken, {
39
39
  cookiePrefix,
40
40
  isProduction,
41
41
  cookieDomain,
@@ -1,4 +1,4 @@
1
- import type { ResponseLike } from '../types';
1
+ import type { ResponseLike } from '../types/index.js';
2
2
  /**
3
3
  * NextAuth 세션 토큰 쿠키를 삭제하는 헬퍼 함수
4
4
  */
@@ -5,7 +5,7 @@ exports.clearAuthCookies = clearAuthCookies;
5
5
  exports.clearAllAuthCookies = clearAllAuthCookies;
6
6
  exports.setCustomTokens = setCustomTokens;
7
7
  exports.setNextAuthToken = setNextAuthToken;
8
- const logger_1 = require("../utils/logger");
8
+ const logger_js_1 = require("../utils/logger.js");
9
9
  /**
10
10
  * NextAuth 세션 토큰 쿠키를 삭제하는 헬퍼 함수
11
11
  */
@@ -70,7 +70,7 @@ function setCustomTokens(response, accessToken, optionsOrRefreshToken, options)
70
70
  const accessTokenName = `${cookiePrefix}_access_token`;
71
71
  response.cookies.delete(accessTokenName);
72
72
  response.cookies.set(accessTokenName, accessToken, cookieOptions);
73
- (0, logger_1.debugLog)('setCustomTokens', `Set ${accessTokenName} cookie`, { domain: cookieOptions.domain });
73
+ (0, logger_js_1.debugLog)('setCustomTokens', `Set ${accessTokenName} cookie`, { domain: cookieOptions.domain });
74
74
  if (refreshTokenValue) {
75
75
  const refreshTokenName = `${cookiePrefix}_refresh_token`;
76
76
  const refreshCookieOptions = {
@@ -1,5 +1,5 @@
1
1
  import type { JWT } from "next-auth/jwt";
2
- import type { JWTPayload } from '../types';
2
+ import type { JWTPayload } from '../types/index.js';
3
3
  /**
4
4
  * 토큰 검증 및 디코딩
5
5
  */
package/dist/core/jwt.js CHANGED
@@ -40,8 +40,8 @@ exports.encodeNextAuthToken = encodeNextAuthToken;
40
40
  exports.isTokenExpired = isTokenExpired;
41
41
  exports.isValidToken = isValidToken;
42
42
  const jose_1 = require("jose");
43
- const logger_1 = require("../utils/logger");
44
- const crypto_1 = require("../utils/crypto");
43
+ const logger_js_1 = require("../utils/logger.js");
44
+ const crypto_js_1 = require("../utils/crypto.js");
45
45
  /**
46
46
  * 토큰 검증 및 디코딩
47
47
  */
@@ -121,7 +121,7 @@ function createNextAuthJWT(payload, serviceId) {
121
121
  async function encodeNextAuthToken(jwt, secret, maxAge = 30 * 24 * 60 * 60) {
122
122
  try {
123
123
  const { encode } = await Promise.resolve().then(() => __importStar(require('next-auth/jwt')));
124
- (0, logger_1.debugLog)('encodeNextAuthToken', 'Using next-auth/jwt encode');
124
+ (0, logger_js_1.debugLog)('encodeNextAuthToken', 'Using next-auth/jwt encode');
125
125
  const encoded = await encode({
126
126
  token: jwt,
127
127
  secret: secret,
@@ -130,8 +130,8 @@ async function encodeNextAuthToken(jwt, secret, maxAge = 30 * 24 * 60 * 60) {
130
130
  return encoded;
131
131
  }
132
132
  catch (error) {
133
- (0, logger_1.debugLog)('encodeNextAuthToken', 'NextAuth encode failed, using jose EncryptJWT fallback', error);
134
- const secretHash = await (0, crypto_1.createHashSHA256)(secret);
133
+ (0, logger_js_1.debugLog)('encodeNextAuthToken', 'NextAuth encode failed, using jose EncryptJWT fallback', error);
134
+ const secretHash = await (0, crypto_js_1.createHashSHA256)(secret);
135
135
  const keyBytes = new Uint8Array(32);
136
136
  for (let i = 0; i < 32; i++) {
137
137
  keyBytes[i] = parseInt(secretHash.slice(i * 2, i * 2 + 2), 16);
@@ -150,7 +150,7 @@ async function encodeNextAuthToken(jwt, secret, maxAge = 30 * 24 * 60 * 60) {
150
150
  return token;
151
151
  }
152
152
  catch (encryptError) {
153
- (0, logger_1.debugError)('encodeNextAuthToken', 'EncryptJWT also failed:', encryptError);
153
+ (0, logger_js_1.debugError)('encodeNextAuthToken', 'EncryptJWT also failed:', encryptError);
154
154
  throw new Error(`Failed to encode NextAuth token: ${error instanceof Error ? error.message : String(error)}`);
155
155
  }
156
156
  }
@@ -1,5 +1,5 @@
1
1
  import type { JWT } from "next-auth/jwt";
2
- import type { RoleAccessConfig } from '../types';
2
+ import type { RoleAccessConfig } from '../types/index.js';
3
3
  /**
4
4
  * JWT에서 서비스별 역할을 추출하는 헬퍼 함수
5
5
  */
@@ -30,8 +30,9 @@ function requiresSubscription(pathname, role, subscriptionRequiredPaths, systemA
30
30
  * 역할 기반 접근 제어 확인
31
31
  */
32
32
  function checkRoleAccess(pathname, role, roleConfig) {
33
- for (const [configRole, config] of Object.entries(roleConfig)) {
34
- const isPathMatch = config.paths.some(path => pathname.startsWith(path));
33
+ for (const [configRole, configEntry] of Object.entries(roleConfig)) {
34
+ const config = configEntry;
35
+ const isPathMatch = config.paths.some((path) => pathname.startsWith(path));
35
36
  if (isPathMatch) {
36
37
  if (role === configRole || config.allowedRoles?.includes(role)) {
37
38
  return { allowed: true };
@@ -1,5 +1,5 @@
1
1
  import type { NextRequest, NextResponse } from 'next/server';
2
- import type { JWTPayload } from '../types';
2
+ import type { JWTPayload } from '../types/index';
3
3
  /**
4
4
  * 토큰 검증 및 갱신 시도
5
5
  */
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export * from './types';
1
+ export * from './types/index';
2
2
  export * from './utils/logger';
3
3
  export * from './utils/crypto';
4
4
  export * from './utils/server';
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./types"), exports);
17
+ __exportStar(require("./types/index"), exports);
18
18
  __exportStar(require("./utils/logger"), exports);
19
19
  __exportStar(require("./utils/crypto"), exports);
20
20
  __exportStar(require("./utils/server"), exports);
@@ -1,4 +1,4 @@
1
- import type { MiddlewareConfig } from '../types';
1
+ import type { MiddlewareConfig } from '../types/index.js';
2
2
  /**
3
3
  * 기본 미들웨어 설정을 생성하는 함수
4
4
  */
@@ -1,5 +1,5 @@
1
1
  import type { NextRequest, NextResponse } from 'next/server';
2
- import type { MiddlewareConfig, MiddlewareOptions } from '../types';
2
+ import type { MiddlewareConfig, MiddlewareOptions } from '../types/index.js';
3
3
  /**
4
4
  * 통합 미들웨어 핸들러 함수
5
5
  */
@@ -34,25 +34,25 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.handleMiddleware = handleMiddleware;
37
- const server_1 = require("../utils/server");
38
- const license_1 = require("../utils/license");
39
- const logger_1 = require("../utils/logger");
40
- const roles_1 = require("../core/roles");
41
- const verify_1 = require("../core/verify");
42
- const jwt_1 = require("../core/jwt");
43
- const api_1 = require("../sso/api");
44
- const auth_response_1 = require("../core/auth-response");
45
- const redirect_1 = require("../utils/redirect");
46
- const cookies_1 = require("../core/cookies");
37
+ const server_js_1 = require("../utils/server.js");
38
+ const license_js_1 = require("../utils/license.js");
39
+ const logger_js_1 = require("../utils/logger.js");
40
+ const roles_js_1 = require("../core/roles.js");
41
+ const verify_js_1 = require("../core/verify.js");
42
+ const jwt_js_1 = require("../core/jwt.js");
43
+ const api_js_1 = require("../sso/api.js");
44
+ const auth_response_js_1 = require("../core/auth-response.js");
45
+ const redirect_js_1 = require("../utils/redirect.js");
46
+ const cookies_js_1 = require("../core/cookies.js");
47
47
  /**
48
48
  * 통합 미들웨어 핸들러 함수
49
49
  */
50
50
  async function handleMiddleware(req, config, options) {
51
- const { NextResponse: NextResponseClass } = await (0, server_1.getNextServer)();
51
+ const { NextResponse: NextResponseClass } = await (0, server_js_1.getNextServer)();
52
52
  try {
53
53
  const pathname = req.nextUrl.pathname;
54
54
  const { secret, isProduction, cookieDomain, getNextAuthToken, licenseKey } = options;
55
- await (0, license_1.checkLicenseKey)(licenseKey);
55
+ await (0, license_js_1.checkLicenseKey)(licenseKey);
56
56
  if (!config.serviceId) {
57
57
  throw new Error('serviceId is required in middleware config');
58
58
  }
@@ -68,10 +68,10 @@ async function handleMiddleware(req, config, options) {
68
68
  token = await getToken({ req, secret });
69
69
  }
70
70
  catch (error) {
71
- (0, logger_1.debugLog)('handleMiddleware', 'getToken failed', error);
71
+ (0, logger_js_1.debugLog)('handleMiddleware', 'getToken failed', error);
72
72
  }
73
73
  }
74
- const effectiveRole = (0, roles_1.getEffectiveRole)(token, serviceId);
74
+ const effectiveRole = (0, roles_js_1.getEffectiveRole)(token, serviceId);
75
75
  // 1. API 요청 처리
76
76
  if (pathname.startsWith('/api/')) {
77
77
  if (config.authApiPaths.includes(pathname)) {
@@ -80,7 +80,7 @@ async function handleMiddleware(req, config, options) {
80
80
  if (config.subscriptionExemptApiPaths.some((path) => pathname.startsWith(path))) {
81
81
  return NextResponseClass.next();
82
82
  }
83
- const authCheck = await (0, verify_1.verifyAndRefreshTokenWithNextAuth)(req, token, secret, {
83
+ const authCheck = await (0, verify_js_1.verifyAndRefreshTokenWithNextAuth)(req, token, secret, {
84
84
  cookiePrefix,
85
85
  serviceId,
86
86
  isProduction,
@@ -93,8 +93,8 @@ async function handleMiddleware(req, config, options) {
93
93
  if (authCheck.response)
94
94
  return authCheck.response;
95
95
  if (!authCheck.isValid) {
96
- const response = await (0, redirect_1.redirectToError)(req, 'UNAUTHORIZED', '인증이 필요합니다.', config.errorPath);
97
- (0, cookies_1.clearAuthCookies)(response, cookiePrefix);
96
+ const response = await (0, redirect_js_1.redirectToError)(req, 'UNAUTHORIZED', '인증이 필요합니다.', config.errorPath);
97
+ (0, cookies_js_1.clearAuthCookies)(response, cookiePrefix);
98
98
  return response;
99
99
  }
100
100
  return NextResponseClass.next();
@@ -104,27 +104,27 @@ async function handleMiddleware(req, config, options) {
104
104
  const tokenParam = req.nextUrl.searchParams.get('token');
105
105
  if (tokenParam) {
106
106
  try {
107
- const tokenResult = await (0, jwt_1.verifyToken)(tokenParam, secret);
107
+ const tokenResult = await (0, jwt_js_1.verifyToken)(tokenParam, secret);
108
108
  if (!tokenResult)
109
109
  throw new Error('Invalid token');
110
110
  const { payload } = tokenResult;
111
111
  const defaultRole = Object.keys(config.rolePaths)[0] || 'ADMIN';
112
- const tokenRole = (0, jwt_1.extractRoleFromPayload)(payload, serviceId, defaultRole);
112
+ const tokenRole = (0, jwt_js_1.extractRoleFromPayload)(payload, serviceId, defaultRole);
113
113
  const userId = payload.id || payload.sub || payload.userId || '';
114
114
  const ssoBaseURL = options.ssoBaseURL;
115
115
  const authServiceKey = options.authServiceKey;
116
116
  let refreshToken = '';
117
117
  if (authServiceKey && userId) {
118
118
  try {
119
- const refreshTokenResult = await (0, api_1.getRefreshTokenFromSSO)(userId, tokenParam, { ssoBaseURL, authServiceKey });
119
+ const refreshTokenResult = await (0, api_js_1.getRefreshTokenFromSSO)(userId, tokenParam, { ssoBaseURL, authServiceKey });
120
120
  refreshToken = refreshTokenResult || '';
121
121
  }
122
122
  catch (error) {
123
- (0, logger_1.debugError)('handleMiddleware', 'Failed to get refresh token', error);
123
+ (0, logger_js_1.debugError)('handleMiddleware', 'Failed to get refresh token', error);
124
124
  }
125
125
  }
126
126
  const redirectPath = config.rolePaths[tokenRole] || config.rolePaths[defaultRole] || '/admin';
127
- return await (0, auth_response_1.createAuthResponse)(tokenParam, secret, {
127
+ return await (0, auth_response_js_1.createAuthResponse)(tokenParam, secret, {
128
128
  req,
129
129
  refreshToken: refreshToken || undefined,
130
130
  redirectPath,
@@ -137,16 +137,16 @@ async function handleMiddleware(req, config, options) {
137
137
  });
138
138
  }
139
139
  catch (error) {
140
- (0, logger_1.debugError)('handleMiddleware', 'Error processing token', error);
140
+ (0, logger_js_1.debugError)('handleMiddleware', 'Error processing token', error);
141
141
  const ssoBaseURL = options.ssoBaseURL;
142
- return await (0, redirect_1.redirectToSSOLogin)(req, serviceId, ssoBaseURL);
142
+ return await (0, redirect_js_1.redirectToSSOLogin)(req, serviceId, ssoBaseURL);
143
143
  }
144
144
  }
145
145
  if (token && effectiveRole) {
146
- return await (0, redirect_1.redirectToRoleDashboard)(req, effectiveRole, config.rolePaths);
146
+ return await (0, redirect_js_1.redirectToRoleDashboard)(req, effectiveRole, config.rolePaths);
147
147
  }
148
148
  const ssoBaseURL = options.ssoBaseURL;
149
- return await (0, redirect_1.redirectToSSOLogin)(req, serviceId, ssoBaseURL);
149
+ return await (0, redirect_js_1.redirectToSSOLogin)(req, serviceId, ssoBaseURL);
150
150
  }
151
151
  // 3. 공개 경로 처리
152
152
  if (config.publicPaths.some((path) => pathname === path || pathname.startsWith(path))) {
@@ -154,12 +154,12 @@ async function handleMiddleware(req, config, options) {
154
154
  return NextResponseClass.next();
155
155
  }
156
156
  if (token && effectiveRole) {
157
- return await (0, redirect_1.redirectToRoleDashboard)(req, effectiveRole, config.rolePaths);
157
+ return await (0, redirect_js_1.redirectToRoleDashboard)(req, effectiveRole, config.rolePaths);
158
158
  }
159
159
  return NextResponseClass.next();
160
160
  }
161
161
  // 4. 인증 체크
162
- const authCheck = await (0, verify_1.verifyAndRefreshTokenWithNextAuth)(req, token, secret, {
162
+ const authCheck = await (0, verify_js_1.verifyAndRefreshTokenWithNextAuth)(req, token, secret, {
163
163
  cookiePrefix,
164
164
  serviceId,
165
165
  isProduction,
@@ -173,7 +173,7 @@ async function handleMiddleware(req, config, options) {
173
173
  return authCheck.response;
174
174
  if (!authCheck.isValid) {
175
175
  const ssoBaseURL = options.ssoBaseURL;
176
- return await (0, redirect_1.redirectToSSOLogin)(req, serviceId, ssoBaseURL);
176
+ return await (0, redirect_js_1.redirectToSSOLogin)(req, serviceId, ssoBaseURL);
177
177
  }
178
178
  let finalToken = authCheck.token || token;
179
179
  if (!finalToken && authCheck.isValid) {
@@ -192,29 +192,29 @@ async function handleMiddleware(req, config, options) {
192
192
  }
193
193
  if (!finalToken) {
194
194
  const ssoBaseURL = options.ssoBaseURL;
195
- return await (0, redirect_1.redirectToSSOLogin)(req, serviceId, ssoBaseURL);
195
+ return await (0, redirect_js_1.redirectToSSOLogin)(req, serviceId, ssoBaseURL);
196
196
  }
197
197
  if (finalToken.error === "RefreshAccessTokenError") {
198
198
  const ssoBaseURL = options.ssoBaseURL;
199
- return await (0, redirect_1.redirectToSSOLogin)(req, serviceId, ssoBaseURL);
199
+ return await (0, redirect_js_1.redirectToSSOLogin)(req, serviceId, ssoBaseURL);
200
200
  }
201
201
  if (!finalToken.role || !finalToken.email) {
202
202
  const ssoBaseURL = options.ssoBaseURL;
203
- return await (0, redirect_1.redirectToSSOLogin)(req, serviceId, ssoBaseURL);
203
+ return await (0, redirect_js_1.redirectToSSOLogin)(req, serviceId, ssoBaseURL);
204
204
  }
205
- const finalEffectiveRole = finalToken.role || (0, roles_1.getEffectiveRole)(finalToken, serviceId) || effectiveRole || '';
205
+ const finalEffectiveRole = finalToken.role || (0, roles_js_1.getEffectiveRole)(finalToken, serviceId) || effectiveRole || '';
206
206
  if (config.roleAccessConfig && Object.keys(config.roleAccessConfig).length > 0 && finalEffectiveRole) {
207
- const roleCheck = (0, roles_1.checkRoleAccess)(pathname, finalEffectiveRole, config.roleAccessConfig);
207
+ const roleCheck = (0, roles_js_1.checkRoleAccess)(pathname, finalEffectiveRole, config.roleAccessConfig);
208
208
  if (!roleCheck.allowed) {
209
- return await (0, redirect_1.redirectToError)(req, 'ACCESS_DENIED', roleCheck.message || '접근 권한이 없습니다.', config.errorPath);
209
+ return await (0, redirect_js_1.redirectToError)(req, 'ACCESS_DENIED', roleCheck.message || '접근 권한이 없습니다.', config.errorPath);
210
210
  }
211
211
  }
212
- if (finalEffectiveRole && (0, roles_1.requiresSubscription)(pathname, finalEffectiveRole, config.subscriptionRequiredPaths, config.systemAdminRole)) {
212
+ if (finalEffectiveRole && (0, roles_js_1.requiresSubscription)(pathname, finalEffectiveRole, config.subscriptionRequiredPaths, config.systemAdminRole)) {
213
213
  const services = finalToken.services || [];
214
214
  const ssoBaseURL = options.ssoBaseURL;
215
215
  if (!ssoBaseURL)
216
216
  throw new Error('ssoBaseURL is required');
217
- const subscriptionCheck = (0, api_1.validateServiceSubscription)(services, serviceId, ssoBaseURL);
217
+ const subscriptionCheck = (0, api_js_1.validateServiceSubscription)(services, serviceId, ssoBaseURL);
218
218
  if (!subscriptionCheck.isValid) {
219
219
  return NextResponseClass.redirect(subscriptionCheck.redirectUrl);
220
220
  }
@@ -222,7 +222,7 @@ async function handleMiddleware(req, config, options) {
222
222
  return null;
223
223
  }
224
224
  catch (error) {
225
- (0, logger_1.debugError)('handleMiddleware', 'Middleware error', error);
226
- return await (0, redirect_1.redirectToError)(req, 'INTERNAL_ERROR', '서버 오류가 발생했습니다.', config.errorPath);
225
+ (0, logger_js_1.debugError)('handleMiddleware', 'Middleware error', error);
226
+ return await (0, redirect_js_1.redirectToError)(req, 'INTERNAL_ERROR', '서버 오류가 발생했습니다.', config.errorPath);
227
227
  }
228
228
  }
@@ -36,8 +36,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.createInitialJWTToken = createInitialJWTToken;
37
37
  exports.getJWTFromCustomTokenCookie = getJWTFromCustomTokenCookie;
38
38
  exports.handleJWTCallback = handleJWTCallback;
39
- const jwt_1 = require("../core/jwt");
40
- const license_1 = require("../utils/license");
39
+ const jwt_js_1 = require("../core/jwt.js");
40
+ const license_js_1 = require("../utils/license.js");
41
41
  /**
42
42
  * JWT 콜백에서 초기 로그인 시 토큰 생성 헬퍼
43
43
  */
@@ -69,12 +69,12 @@ async function getJWTFromCustomTokenCookie(cookieName, secret, serviceId, licens
69
69
  const accessToken = cookieStore.get(cookieName)?.value;
70
70
  if (!accessToken)
71
71
  return null;
72
- await (0, license_1.checkLicenseKey)(licenseKey);
73
- const tokenResult = await (0, jwt_1.verifyToken)(accessToken, secret);
72
+ await (0, license_js_1.checkLicenseKey)(licenseKey);
73
+ const tokenResult = await (0, jwt_js_1.verifyToken)(accessToken, secret);
74
74
  if (!tokenResult)
75
75
  return null;
76
76
  const { payload } = tokenResult;
77
- const jwt = (0, jwt_1.createNextAuthJWT)(payload, serviceId);
77
+ const jwt = (0, jwt_js_1.createNextAuthJWT)(payload, serviceId);
78
78
  jwt.accessTokenExpires = Date.now() + (15 * 60 * 1000);
79
79
  const refreshTokenCookieName = cookieName.replace('_access_token', '_refresh_token');
80
80
  const refreshToken = cookieStore.get(refreshTokenCookieName)?.value;
@@ -123,9 +123,9 @@ async function handleJWTCallback(token, user, account, options) {
123
123
  if (response.ok) {
124
124
  const result = await response.json();
125
125
  if (result.success && result.accessToken) {
126
- const tokenResult = await (0, jwt_1.verifyToken)(result.accessToken, secret);
126
+ const tokenResult = await (0, jwt_js_1.verifyToken)(result.accessToken, secret);
127
127
  if (tokenResult) {
128
- const newJWT = (0, jwt_1.createNextAuthJWT)(tokenResult.payload, serviceId || '');
128
+ const newJWT = (0, jwt_js_1.createNextAuthJWT)(tokenResult.payload, serviceId || '');
129
129
  return {
130
130
  ...newJWT,
131
131
  refreshToken,
package/dist/sso/api.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ServiceInfo, SSORefreshTokenResponse } from '../types';
1
+ import type { ServiceInfo, SSORefreshTokenResponse } from '../types/index.js';
2
2
  /**
3
3
  * 서비스 구독 유효성 확인 함수
4
4
  */
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.checkLicenseKey = checkLicenseKey;
4
- const crypto_1 = require("./crypto");
4
+ const crypto_js_1 = require("./crypto.js");
5
5
  // 유효한 라이센스 키 해시 목록
6
6
  const VALID_LICENSE_KEY_HASHES = new Set([
7
7
  '73bce4f3b64804c255cdab450d759a8b53038f9edb59ae42d9988b08dfd007e2',
@@ -13,7 +13,7 @@ async function checkLicenseKey(licenseKey) {
13
13
  if (!licenseKey || licenseKey.length < 10) {
14
14
  throw new Error('License key is required');
15
15
  }
16
- const keyHash = await (0, crypto_1.createHashSHA256)(licenseKey);
16
+ const keyHash = await (0, crypto_js_1.createHashSHA256)(licenseKey);
17
17
  if (!VALID_LICENSE_KEY_HASHES.has(keyHash)) {
18
18
  throw new Error('Invalid license key');
19
19
  }
@@ -1,20 +1,17 @@
1
1
  import type { NextRequest, NextResponse } from 'next/server';
2
2
  /**
3
- * 리다이렉트용 HTML 생성
4
- * @param redirectPath 리다이렉트할 경로
5
- * @param text 표시할 텍스트 (필수)
6
- * @returns HTML 문자열
3
+ * 리다이렉트를 위한 HTML 생성
7
4
  */
8
- export declare function createRedirectHTML(redirectPath: string, text: string): string;
5
+ export declare function createRedirectHTML(url: string): string;
9
6
  /**
10
- * 에러 페이지로 리다이렉트하는 헬퍼 함수
7
+ * 에러 페이지로 리다이렉트
11
8
  */
12
9
  export declare function redirectToError(req: NextRequest, code: string, message: string, errorPath?: string): Promise<NextResponse>;
13
10
  /**
14
- * SSO 로그인 페이지로 리다이렉트하는 헬퍼 함수
11
+ * SSO 로그인 페이지로 리다이렉트
15
12
  */
16
- export declare function redirectToSSOLogin(req: NextRequest, serviceId: string, ssoBaseURL: string): Promise<NextResponse>;
13
+ export declare function redirectToSSOLogin(req: NextRequest, serviceId: string, ssoBaseURL?: string): Promise<NextResponse>;
17
14
  /**
18
- * 역할별 대시보드 경로로 리다이렉트하는 헬퍼 함수
15
+ * 역할별 대시보드로 리다이렉트
19
16
  */
20
- export declare function redirectToRoleDashboard(req: NextRequest, role: string, rolePaths: Record<string, string>, defaultPath?: string): Promise<NextResponse>;
17
+ export declare function redirectToRoleDashboard(req: NextRequest, role: string, rolePaths: Record<string, string>): Promise<NextResponse>;
@@ -4,103 +4,47 @@ exports.createRedirectHTML = createRedirectHTML;
4
4
  exports.redirectToError = redirectToError;
5
5
  exports.redirectToSSOLogin = redirectToSSOLogin;
6
6
  exports.redirectToRoleDashboard = redirectToRoleDashboard;
7
- const server_1 = require("./server");
7
+ const server_js_1 = require("./server.js");
8
8
  /**
9
- * 리다이렉트용 HTML 생성
10
- * @param redirectPath 리다이렉트할 경로
11
- * @param text 표시할 텍스트 (필수)
12
- * @returns HTML 문자열
9
+ * 리다이렉트를 위한 HTML 생성
13
10
  */
14
- function createRedirectHTML(redirectPath, text) {
11
+ function createRedirectHTML(url) {
15
12
  return `
16
- <!DOCTYPE html>
17
13
  <html>
18
14
  <head>
19
- <meta charset="utf-8">
20
- <title>Redirecting...</title>
21
- <style>
22
- * { margin: 0; padding: 0; box-sizing: border-box; }
23
- html, body {
24
- width: 100%;
25
- height: 100%;
26
- overflow: hidden;
27
- }
28
- body {
29
- display: flex;
30
- align-items: flex-start;
31
- justify-content: flex-start;
32
- padding-left: 10%;
33
- padding-top: 20%;
34
- background: #fff;
35
- }
36
- .typing-text {
37
- font-family: 'Courier New', monospace;
38
- font-size: 2.5rem;
39
- font-weight: bold;
40
- color: #667eea;
41
- letter-spacing: 0.1em;
42
- }
43
- .cursor {
44
- display: inline-block;
45
- width: 3px;
46
- height: 2.5rem;
47
- background-color: #667eea;
48
- margin-left: 2px;
49
- animation: blink 0.7s infinite;
50
- }
51
- @keyframes blink {
52
- 0%, 50% { opacity: 1; }
53
- 51%, 100% { opacity: 0; }
54
- }
55
- </style>
15
+ <meta http-equiv="refresh" content="0;url=${url}">
56
16
  </head>
57
17
  <body>
58
- <div class="typing-text">
59
- <span id="text"></span><span class="cursor"></span>
60
- </div>
61
- <script>
62
- const text = '${text}';
63
- let index = 0;
64
- const speed = 100;
65
-
66
- function type() {
67
- if (index < text.length) {
68
- document.getElementById('text').textContent += text.charAt(index);
69
- index++;
70
- setTimeout(type, speed);
71
- } else {
72
- setTimeout(() => window.location.href = '${redirectPath}', 200);
73
- }
74
- }
75
-
76
- type();
77
- </script>
18
+ <p>Redirecting to <a href="${url}">${url}</a>...</p>
19
+ <script>window.location.href = "${url}";</script>
78
20
  </body>
79
21
  </html>
80
22
  `;
81
23
  }
82
24
  /**
83
- * 에러 페이지로 리다이렉트하는 헬퍼 함수
25
+ * 에러 페이지로 리다이렉트
84
26
  */
85
27
  async function redirectToError(req, code, message, errorPath = '/error') {
28
+ const { NextResponse: NextResponseClass } = await (0, server_js_1.getNextServer)();
86
29
  const url = new URL(errorPath, req.url);
87
30
  url.searchParams.set('code', code);
88
31
  url.searchParams.set('message', message);
89
- const { NextResponse: NextResponseClass } = await (0, server_1.getNextServer)();
90
32
  return NextResponseClass.redirect(url);
91
33
  }
92
34
  /**
93
- * SSO 로그인 페이지로 리다이렉트하는 헬퍼 함수
35
+ * SSO 로그인 페이지로 리다이렉트
94
36
  */
95
37
  async function redirectToSSOLogin(req, serviceId, ssoBaseURL) {
96
- const { NextResponse: NextResponseClass } = await (0, server_1.getNextServer)();
97
- return NextResponseClass.redirect(new URL(`${ssoBaseURL}/auth/login?serviceId=${serviceId}`, req.url));
38
+ const { NextResponse: NextResponseClass } = await (0, server_js_1.getNextServer)();
39
+ const baseUrl = ssoBaseURL || 'https://sso.thinkingcat.com';
40
+ const callbackUrl = encodeURIComponent(req.url);
41
+ return NextResponseClass.redirect(`${baseUrl}/auth/login?serviceId=${serviceId}&callbackUrl=${callbackUrl}`);
98
42
  }
99
43
  /**
100
- * 역할별 대시보드 경로로 리다이렉트하는 헬퍼 함수
44
+ * 역할별 대시보드로 리다이렉트
101
45
  */
102
- async function redirectToRoleDashboard(req, role, rolePaths, defaultPath = '/admin') {
103
- const redirectPath = rolePaths[role] || defaultPath;
104
- const { NextResponse: NextResponseClass } = await (0, server_1.getNextServer)();
105
- return NextResponseClass.redirect(new URL(redirectPath, req.url));
46
+ async function redirectToRoleDashboard(req, role, rolePaths) {
47
+ const { NextResponse: NextResponseClass } = await (0, server_js_1.getNextServer)();
48
+ const path = rolePaths[role] || rolePaths['ADMIN'] || '/admin';
49
+ return NextResponseClass.redirect(new URL(path, req.url));
106
50
  }
@@ -3,13 +3,6 @@
3
3
  */
4
4
  export declare function getNextServer(): Promise<{
5
5
  default: typeof import("next/server");
6
- NextFetchEvent: typeof import("next/server").NextFetchEvent;
7
6
  NextRequest: typeof import("next/server").NextRequest;
8
7
  NextResponse: typeof import("next/server").NextResponse;
9
- userAgentFromString: typeof import("next/server").userAgentFromString;
10
- userAgent: typeof import("next/server").userAgent;
11
- URLPattern: typeof import("next/server").URLPattern;
12
- ImageResponse: typeof import("next/server").ImageResponse;
13
- after: typeof import("next/server").after;
14
- connection: typeof import("next/server").connection;
15
8
  }>;
package/package.json CHANGED
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "@thinkingcat/auth-utils",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
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",
7
7
  "exports": {
8
8
  ".": {
9
9
  "types": "./dist/index.d.ts",
10
- "edge-light": "./dist/index.js",
11
10
  "default": "./dist/index.js"
12
11
  }
13
12
  },
@@ -30,7 +29,10 @@
30
29
  },
31
30
  "devDependencies": {
32
31
  "@types/node": "^20",
33
- "typescript": "^5"
32
+ "typescript": "^5",
33
+ "next": ">=13.0.0",
34
+ "@types/react": "^18",
35
+ "@types/react-dom": "^18"
34
36
  },
35
37
  "peerDependencies": {
36
38
  "next": ">=13.0.0"