@thinkingcat/auth-utils 1.0.33 → 1.0.35

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 +60 -51
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -211,50 +211,36 @@ function createNextAuthJWT(payload, serviceId) {
211
211
  * @returns 인코딩된 세션 토큰
212
212
  */
213
213
  async function encodeNextAuthToken(jwt, secret, maxAge = 30 * 24 * 60 * 60) {
214
+ // Edge Runtime과 Node.js Runtime 간 호환성을 위해
215
+ // next-auth/jwt의 encode 대신 jose를 직접 사용
216
+ // (Edge Runtime에서 encode한 토큰을 Node.js Runtime에서 decode 못하는 문제 방지)
217
+ debugLog('encodeNextAuthToken', 'Using jose EncryptJWT for cross-runtime compatibility');
218
+ // NextAuth는 secret을 SHA-256 해시하여 32바이트 키로 사용
219
+ const secretHash = await createHashSHA256(secret);
220
+ // SHA-256 해시는 64자 hex 문자열이므로, 32바이트로 변환
221
+ const keyBytes = new Uint8Array(32);
222
+ for (let i = 0; i < 32; i++) {
223
+ keyBytes[i] = parseInt(secretHash.slice(i * 2, i * 2 + 2), 16);
224
+ }
225
+ const now = Math.floor(Date.now() / 1000);
226
+ // EncryptJWT를 사용하여 JWE 토큰 생성
227
+ // NextAuth는 'dir' 키 관리와 'A256GCM' 암호화를 사용
214
228
  try {
215
- // next-auth/jwt의 encode 함수를 동적 import로 사용 (Edge Runtime 호환)
216
- const { encode } = await Promise.resolve().then(() => __importStar(require('next-auth/jwt')));
217
- debugLog('encodeNextAuthToken', 'Using next-auth/jwt encode');
218
- const encoded = await encode({
219
- token: jwt,
220
- secret: secret,
221
- maxAge: maxAge,
222
- });
223
- debugLog('encodeNextAuthToken', 'Encode successful, length:', encoded.length);
224
- return encoded;
229
+ const token = await new jose_1.EncryptJWT(jwt)
230
+ .setProtectedHeader({
231
+ alg: 'dir', // Direct key agreement
232
+ enc: 'A256GCM' // AES-256-GCM encryption
233
+ })
234
+ .setIssuedAt(now)
235
+ .setExpirationTime(now + maxAge)
236
+ .setJti(crypto.randomUUID())
237
+ .encrypt(keyBytes);
238
+ debugLog('encodeNextAuthToken', 'EncryptJWT successful, length:', token.length);
239
+ return token;
225
240
  }
226
241
  catch (error) {
227
- // Edge Runtime에서 encode가 작동하지 않을 수 있으므로
228
- // jose의 EncryptJWT를 사용하여 JWE 토큰 생성 (NextAuth가 기대하는 형식)
229
- debugLog('encodeNextAuthToken', 'encode failed, using EncryptJWT fallback', error);
230
- // NextAuth는 secret을 SHA-256 해시하여 32바이트 키로 사용
231
- // jose의 EncryptJWT는 'dir' 알고리즘에서 Uint8Array 키를 직접 사용
232
- const secretHash = await createHashSHA256(secret);
233
- // SHA-256 해시는 64자 hex 문자열이므로, 32바이트로 변환
234
- const keyBytes = new Uint8Array(32);
235
- for (let i = 0; i < 32; i++) {
236
- keyBytes[i] = parseInt(secretHash.slice(i * 2, i * 2 + 2), 16);
237
- }
238
- const now = Math.floor(Date.now() / 1000);
239
- // EncryptJWT를 사용하여 JWE 토큰 생성
240
- // NextAuth는 'dir' 키 관리와 'A256GCM' 암호화를 사용
241
- // 'dir' 알고리즘은 Uint8Array 키를 직접 사용
242
- try {
243
- const token = await new jose_1.EncryptJWT(jwt)
244
- .setProtectedHeader({
245
- alg: 'dir', // Direct key agreement
246
- enc: 'A256GCM' // AES-256-GCM encryption
247
- })
248
- .setIssuedAt(now)
249
- .setExpirationTime(now + maxAge)
250
- .setJti(crypto.randomUUID())
251
- .encrypt(keyBytes);
252
- return token;
253
- }
254
- catch (encryptError) {
255
- debugError('encodeNextAuthToken', 'EncryptJWT also failed:', encryptError);
256
- throw new Error(`Failed to encode NextAuth token: ${error instanceof Error ? error.message : String(error)}`);
257
- }
242
+ debugError('encodeNextAuthToken', 'EncryptJWT failed:', error);
243
+ throw new Error(`Failed to encode NextAuth token: ${error instanceof Error ? error.message : String(error)}`);
258
244
  }
259
245
  }
260
246
  function setCustomTokens(response, accessToken, optionsOrRefreshToken, options) {
@@ -455,13 +441,11 @@ async function createAuthResponse(accessToken, secret, options) {
455
441
  hasRole: !!jwt.role,
456
442
  hasRefreshToken: !!jwt.refreshToken,
457
443
  });
458
- // 3. NextAuth session cookie 생성
444
+ // 3. NextAuth session cookie 생성 (jose 사용으로 Edge/Node.js Runtime 호환)
459
445
  const nextAuthToken = await encodeNextAuthToken(jwt, secret);
460
446
  debugLog('createAuthResponse', 'NextAuth session token encoded:', {
461
447
  tokenLength: nextAuthToken.length,
462
448
  tokenPrefix: nextAuthToken.substring(0, 30) + '...',
463
- jwtId: jwt.id,
464
- jwtEmail: jwt.email?.substring(0, 20) + '...',
465
449
  });
466
450
  // 4. Response 생성 (HTTP 302 리다이렉트 사용)
467
451
  const { NextResponse: NextResponseClass } = await getNextServer();
@@ -469,7 +453,7 @@ async function createAuthResponse(accessToken, secret, options) {
469
453
  const response = redirectPath
470
454
  ? NextResponseClass.redirect(new URL(redirectPath, req.url), { status: 302 })
471
455
  : NextResponseClass.json({ success: true, message: text || 'Authentication successful' }, { status: 200 });
472
- // 4. NextAuth session cookie 설정
456
+ // 5. NextAuth session cookie 설정
473
457
  const nextAuthCookieName = isProduction
474
458
  ? '__Secure-next-auth.session-token'
475
459
  : 'next-auth.session-token';
@@ -489,7 +473,7 @@ async function createAuthResponse(accessToken, secret, options) {
489
473
  valueLength: nextAuthToken.length,
490
474
  ...cookieOptions,
491
475
  });
492
- // 5. 커스텀 토큰 쿠키 설정
476
+ // 6. 커스텀 토큰 쿠키 설정
493
477
  if (refreshToken) {
494
478
  setCustomTokens(response, accessToken, refreshToken, {
495
479
  cookiePrefix,
@@ -654,12 +638,37 @@ async function verifyAndRefreshToken(req, secret, options) {
654
638
  catch {
655
639
  // 토큰 검증 실패
656
640
  }
657
- debugLog('verifyAndRefreshToken', 'Updating custom cookies only (NextAuth will handle session)...');
658
- // NextResponse.next()를 생성하고 커스텀 토큰만 설정
659
- // NextAuth 쿠키는 생성하지 않음 - NextAuth가 자체적으로 처리하도록 함
641
+ debugLog('verifyAndRefreshToken', 'Updating cookies with NextAuth session...');
642
+ // NextResponse.next()를 생성하고 쿠키 설정
660
643
  const { NextResponse: NextResponseClass } = await getNextServer();
661
644
  const response = NextResponseClass.next();
662
- // 커스텀 토큰 쿠키만 설정
645
+ // NextAuth JWT 생성
646
+ const jwt = createNextAuthJWT(payload, serviceId);
647
+ if (newRefreshToken) {
648
+ jwt.refreshToken = newRefreshToken;
649
+ }
650
+ jwt.accessTokenExpires = Date.now() + (15 * 60 * 1000);
651
+ // NextAuth 세션 쿠키 설정 (jose 사용으로 Edge/Node.js Runtime 호환)
652
+ const nextAuthToken = await encodeNextAuthToken(jwt, secret);
653
+ const nextAuthCookieName = isProduction
654
+ ? '__Secure-next-auth.session-token'
655
+ : 'next-auth.session-token';
656
+ const cookieOptions = {
657
+ httpOnly: true,
658
+ secure: isProduction,
659
+ sameSite: isProduction ? 'none' : 'lax',
660
+ path: '/',
661
+ maxAge: 30 * 24 * 60 * 60,
662
+ };
663
+ if (cookieDomain) {
664
+ cookieOptions.domain = cookieDomain;
665
+ }
666
+ response.cookies.set(nextAuthCookieName, nextAuthToken, cookieOptions);
667
+ debugLog('verifyAndRefreshToken', 'NextAuth session cookie set:', {
668
+ name: nextAuthCookieName,
669
+ valueLength: nextAuthToken.length,
670
+ });
671
+ // 커스텀 토큰 쿠키 설정
663
672
  if (newRefreshToken) {
664
673
  setCustomTokens(response, refreshResult.accessToken, newRefreshToken, {
665
674
  cookiePrefix,
@@ -674,7 +683,7 @@ async function verifyAndRefreshToken(req, secret, options) {
674
683
  cookieDomain,
675
684
  });
676
685
  }
677
- debugLog('verifyAndRefreshToken', 'Custom cookies updated, NextAuth will pick them up via handleJWTCallback');
686
+ debugLog('verifyAndRefreshToken', 'All cookies updated, continuing with current request');
678
687
  return { isValid: true, response, payload };
679
688
  }
680
689
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thinkingcat/auth-utils",
3
- "version": "1.0.33",
3
+ "version": "1.0.35",
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",