@thinkingcat/auth-utils 1.0.14 → 1.0.16

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
@@ -60,7 +60,7 @@ npm install @thinkingcat/auth-utils
60
60
  ```json
61
61
  {
62
62
  "dependencies": {
63
- "@thinkingcat/auth-utils": "^1.0.12"
63
+ "@thinkingcat/auth-utils": "^1.0.16"
64
64
  }
65
65
  }
66
66
  ```
@@ -140,6 +140,15 @@ import {
140
140
  handleMiddleware,
141
141
  verifyAndRefreshTokenWithNextAuth,
142
142
 
143
+ // NextAuth 설정 및 콜백
144
+ createNextAuthBaseConfig,
145
+ createNextAuthCookies,
146
+ handleJWTCallback,
147
+ createInitialJWTToken,
148
+ createEmptySession,
149
+ mapTokenToSession,
150
+ getJWTFromCustomTokenCookie,
151
+
143
152
  // 라이센스
144
153
  checkLicenseKey,
145
154
 
@@ -249,7 +258,7 @@ const role = extractRoleFromPayload(payload, "myservice", "ADMIN");
249
258
  // 'ADMIN', 'TEACHER', 'STUDENT' 등
250
259
  ```
251
260
 
252
- #### `createNextAuthJWT(payload: JWTPayload, serviceId: string, includeAcademies?: boolean)`
261
+ #### `createNextAuthJWT(payload: JWTPayload, serviceId: string)`
253
262
 
254
263
  NextAuth와 호환되는 JWT 객체를 생성합니다.
255
264
 
@@ -257,7 +266,6 @@ NextAuth와 호환되는 JWT 객체를 생성합니다.
257
266
 
258
267
  - `payload`: 원본 JWT payload (JWTPayload 타입)
259
268
  - `serviceId`: 서비스 ID (필수)
260
- - `includeAcademies`: academies 정보 포함 여부 (기본값: false)
261
269
 
262
270
  **반환값:**
263
271
 
@@ -266,8 +274,7 @@ NextAuth와 호환되는 JWT 객체를 생성합니다.
266
274
  **사용 예시:**
267
275
 
268
276
  ```typescript
269
- const jwt = createNextAuthJWT(payload, "myservice", true);
270
- // academies 정보가 포함된 JWT 객체
277
+ const jwt = createNextAuthJWT(payload, "myservice");
271
278
  ```
272
279
 
273
280
  #### `encodeNextAuthToken(jwt: JWT, secret: string, maxAge?: number)`
@@ -522,6 +529,159 @@ SSO 서버에서 토큰을 검증합니다.
522
529
 
523
530
  - `{ isValid: boolean; redirectUrl?: string }`
524
531
 
532
+ ### NextAuth 설정 및 콜백
533
+
534
+ #### `createNextAuthBaseConfig(options)`
535
+
536
+ NextAuth 기본 설정을 생성합니다.
537
+
538
+ **파라미터:**
539
+
540
+ - `options.secret`: NextAuth secret (필수)
541
+ - `options.isProduction`: 프로덕션 환경 여부 (기본값: false)
542
+ - `options.cookieDomain`: 쿠키 도메인 (선택)
543
+ - `options.signInPath`: 로그인 페이지 경로 (기본값: '/login')
544
+ - `options.errorPath`: 에러 페이지 경로 (기본값: '/login')
545
+ - `options.nextAuthUrl`: NextAuth URL (선택)
546
+ - `options.sessionMaxAge`: 세션 최대 유지 시간 (초, 기본값: 30일)
547
+ - `options.jwtMaxAge`: JWT 최대 유지 시간 (초, 기본값: 30일)
548
+
549
+ **반환값:**
550
+
551
+ - NextAuth 기본 설정 객체
552
+
553
+ **사용 예시:**
554
+
555
+ ```typescript
556
+ import { createNextAuthBaseConfig } from "@thinkingcat/auth-utils";
557
+
558
+ const baseConfig = createNextAuthBaseConfig({
559
+ secret: process.env.NEXTAUTH_SECRET!,
560
+ isProduction: process.env.NODE_ENV === 'production',
561
+ cookieDomain: process.env.COOKIE_DOMAIN,
562
+ signInPath: "/login",
563
+ errorPath: "/login",
564
+ nextAuthUrl: process.env.NEXTAUTH_URL,
565
+ });
566
+
567
+ export const authOptions: NextAuthOptions = {
568
+ ...baseConfig,
569
+ // ... 기타 설정
570
+ };
571
+ ```
572
+
573
+ #### `createNextAuthCookies(options)`
574
+
575
+ NextAuth 쿠키 설정을 생성합니다.
576
+
577
+ **파라미터:**
578
+
579
+ - `options.isProduction`: 프로덕션 환경 여부 (기본값: false)
580
+ - `options.cookieDomain`: 쿠키 도메인 (선택)
581
+
582
+ **반환값:**
583
+
584
+ - NextAuth 쿠키 설정 객체
585
+
586
+ #### `handleJWTCallback(token, user?, account?, options?)`
587
+
588
+ JWT 콜백을 위한 통합 헬퍼 함수입니다. 초기 로그인, 토큰 갱신, 커스텀 토큰 읽기를 모두 처리합니다.
589
+
590
+ **파라미터:**
591
+
592
+ - `token`: 기존 JWT 토큰
593
+ - `user`: 사용자 정보 (초기 로그인 시)
594
+ - `account`: 계정 정보 (초기 로그인 시)
595
+ - `options.secret`: NextAuth secret (커스텀 토큰 읽기용)
596
+ - `options.licenseKey`: 라이센스 키 (커스텀 토큰 읽기용)
597
+ - `options.serviceId`: 서비스 ID (커스텀 토큰 읽기용)
598
+ - `options.cookieName`: 커스텀 토큰 쿠키 이름 (기본값: '{serviceId}_access_token')
599
+ - `options.debug`: 디버깅 로그 출력 여부 (기본값: false)
600
+
601
+ **반환값:**
602
+
603
+ - 업데이트된 JWT 토큰
604
+
605
+ **사용 예시:**
606
+
607
+ ```typescript
608
+ import { handleJWTCallback } from "@thinkingcat/auth-utils";
609
+
610
+ async jwt({ token, user, account }) {
611
+ return handleJWTCallback(
612
+ token,
613
+ user ? {
614
+ id: user.id,
615
+ email: user.email,
616
+ role: user.role,
617
+ // ... 기타 사용자 정보
618
+ } : null,
619
+ account,
620
+ {
621
+ secret: process.env.NEXTAUTH_SECRET!,
622
+ licenseKey: process.env.LICENSE_KEY!,
623
+ serviceId: 'myservice',
624
+ cookieName: 'myservice_access_token',
625
+ debug: true,
626
+ }
627
+ );
628
+ }
629
+ ```
630
+
631
+ #### `createInitialJWTToken(token, user, account?)`
632
+
633
+ JWT 콜백에서 초기 로그인 시 토큰 생성 헬퍼입니다.
634
+
635
+ **파라미터:**
636
+
637
+ - `token`: 기존 토큰
638
+ - `user`: 사용자 정보
639
+ - `account`: 계정 정보 (선택)
640
+
641
+ **반환값:**
642
+
643
+ - 업데이트된 JWT 토큰
644
+
645
+ #### `createEmptySession(session)`
646
+
647
+ Session 콜백에서 빈 세션 반환 헬퍼입니다.
648
+
649
+ **파라미터:**
650
+
651
+ - `session`: 기존 세션
652
+
653
+ **반환값:**
654
+
655
+ - 빈 세션 객체
656
+
657
+ #### `mapTokenToSession(session, token)`
658
+
659
+ Session 콜백에서 토큰 정보를 세션에 매핑하는 헬퍼입니다.
660
+
661
+ **파라미터:**
662
+
663
+ - `session`: 기존 세션
664
+ - `token`: JWT 토큰
665
+
666
+ **반환값:**
667
+
668
+ - 업데이트된 세션
669
+
670
+ #### `getJWTFromCustomTokenCookie(cookieName, secret, serviceId, licenseKey)`
671
+
672
+ 쿠키에서 커스텀 토큰을 읽어서 NextAuth JWT로 변환하는 헬퍼 함수입니다.
673
+
674
+ **파라미터:**
675
+
676
+ - `cookieName`: 쿠키 이름 (예: 'checkon_access_token')
677
+ - `secret`: JWT 서명에 사용할 secret key
678
+ - `serviceId`: 서비스 ID (필수)
679
+ - `licenseKey`: 라이센스 키 (필수)
680
+
681
+ **반환값:**
682
+
683
+ - NextAuth JWT 객체 또는 null
684
+
525
685
  ### 미들웨어
526
686
 
527
687
  #### `createMiddlewareConfig(config: Partial<MiddlewareConfig> & { serviceId: string }, defaults?)`
@@ -1087,13 +1247,6 @@ interface JWTPayload {
1087
1247
  // 서비스 정보
1088
1248
  services?: ServiceInfo[];
1089
1249
 
1090
- // 학원 정보
1091
- academies?: Array<{
1092
- id: string;
1093
- name: string;
1094
- role: string;
1095
- }>;
1096
-
1097
1250
  // 인증 상태
1098
1251
  phoneVerified?: boolean;
1099
1252
  emailVerified?: boolean;
@@ -1101,8 +1254,6 @@ interface JWTPayload {
1101
1254
 
1102
1255
  // 선택적 필드
1103
1256
  phone?: string;
1104
- academyId?: string;
1105
- academyName?: string;
1106
1257
  isPasswordReset?: boolean;
1107
1258
  decryptedEmail?: string;
1108
1259
  decryptedPhone?: string;
@@ -1308,7 +1459,7 @@ const response = await handleMiddleware(req, middlewareConfig, {
1308
1459
  ## 📦 패키지 정보
1309
1460
 
1310
1461
  - **패키지명**: `@thinkingcat/auth-utils`
1311
- - **버전**: `1.0.12`
1462
+ - **버전**: `1.0.16`
1312
1463
  - **라이선스**: MIT
1313
1464
  - **저장소**: npm registry
1314
1465
 
package/dist/index.d.ts CHANGED
@@ -69,17 +69,10 @@ export interface JWTPayload {
69
69
  iat?: number;
70
70
  exp?: number;
71
71
  services?: ServiceInfo[];
72
- academies?: Array<{
73
- id: string;
74
- name: string;
75
- role: string;
76
- }>;
77
72
  phoneVerified?: boolean;
78
73
  emailVerified?: boolean;
79
74
  smsVerified?: boolean;
80
75
  phone?: string;
81
- academyId?: string;
82
- academyName?: string;
83
76
  isPasswordReset?: boolean;
84
77
  decryptedEmail?: string;
85
78
  decryptedPhone?: string;
@@ -114,10 +107,9 @@ export declare function extractRoleFromPayload(payload: JWTPayload, serviceId: s
114
107
  * payload에서 NextAuth JWT 객체 생성
115
108
  * @param payload JWT payload
116
109
  * @param serviceId 서비스 ID (필수)
117
- * @param includeAcademies academies 정보 포함 여부
118
110
  * @returns NextAuth JWT 객체
119
111
  */
120
- export declare function createNextAuthJWT(payload: JWTPayload, serviceId: string, includeAcademies?: boolean): JWT;
112
+ export declare function createNextAuthJWT(payload: JWTPayload, serviceId: string): JWT;
121
113
  /**
122
114
  * NextAuth JWT를 인코딩된 세션 토큰으로 변환
123
115
  * @param jwt NextAuth JWT 객체
@@ -438,17 +430,44 @@ export declare function createEmptySession(session: Session): Session;
438
430
  * Session 콜백에서 토큰 정보를 세션에 매핑하는 헬퍼
439
431
  * @param session 기존 세션
440
432
  * @param token JWT 토큰
441
- * @param options 옵션
442
- * @param options.primaryAcademy 기본 학원 정보 (선택)
443
433
  * @returns 업데이트된 세션
444
434
  */
445
- export declare function mapTokenToSession(session: Session, token: JWT, options?: {
446
- primaryAcademy?: {
447
- id: string;
448
- name: string;
449
- role: string;
450
- } | null;
451
- }): Session;
435
+ export declare function mapTokenToSession(session: Session, token: JWT): Session;
436
+ /**
437
+ * JWT 콜백을 위한 통합 헬퍼 함수
438
+ * 초기 로그인, 토큰 갱신, 커스텀 토큰 읽기를 모두 처리
439
+ * @param token 기존 JWT 토큰
440
+ * @param user 사용자 정보 (초기 로그인 시)
441
+ * @param account 계정 정보 (초기 로그인 시)
442
+ * @param options 옵션
443
+ * @param options.secret NextAuth secret (커스텀 토큰 읽기용)
444
+ * @param options.licenseKey 라이센스 키 (커스텀 토큰 읽기용)
445
+ * @param options.serviceId 서비스 ID (커스텀 토큰 읽기용)
446
+ * @param options.cookieName 커스텀 토큰 쿠키 이름 (기본값: '{serviceId}_access_token')
447
+ * @param options.debug 디버깅 로그 출력 여부 (기본값: false)
448
+ * @returns 업데이트된 JWT 토큰
449
+ */
450
+ export declare function handleJWTCallback(token: JWT, user?: {
451
+ id: string;
452
+ email?: string | null;
453
+ emailHash?: string | null;
454
+ maskedEmail?: string | null;
455
+ phoneHash?: string | null;
456
+ maskedPhone?: string | null;
457
+ role?: string;
458
+ phone?: string | null;
459
+ decryptedEmail?: string | null;
460
+ decryptedPhone?: string | null;
461
+ refreshToken?: string | null;
462
+ } | null, account?: {
463
+ serviceId?: string;
464
+ } | null, options?: {
465
+ secret?: string;
466
+ licenseKey?: string;
467
+ serviceId?: string;
468
+ cookieName?: string;
469
+ debug?: boolean;
470
+ }): Promise<JWT>;
452
471
  /**
453
472
  * 쿠키에서 커스텀 토큰을 읽어서 NextAuth JWT로 변환하는 헬퍼 함수
454
473
  * NextAuth JWT 콜백에서 사용
@@ -456,10 +475,9 @@ export declare function mapTokenToSession(session: Session, token: JWT, options?
456
475
  * @param secret JWT 서명에 사용할 secret key
457
476
  * @param serviceId 서비스 ID (필수)
458
477
  * @param licenseKey 라이센스 키 (필수)
459
- * @param includeAcademies academies 정보 포함 여부 (기본값: false)
460
478
  * @returns NextAuth JWT 객체 또는 null
461
479
  */
462
- export declare function getJWTFromCustomTokenCookie(cookieName: string, secret: string, serviceId: string, licenseKey: string, includeAcademies?: boolean): Promise<JWT | null>;
480
+ export declare function getJWTFromCustomTokenCookie(cookieName: string, secret: string, serviceId: string, licenseKey: string): Promise<JWT | null>;
463
481
  export declare function checkLicenseKey(licenseKey: string): Promise<void>;
464
482
  export declare function checkRoleAccess(pathname: string, role: string, roleConfig: RoleAccessConfig): {
465
483
  allowed: boolean;
package/dist/index.js CHANGED
@@ -54,6 +54,7 @@ exports.createNextAuthBaseConfig = createNextAuthBaseConfig;
54
54
  exports.createInitialJWTToken = createInitialJWTToken;
55
55
  exports.createEmptySession = createEmptySession;
56
56
  exports.mapTokenToSession = mapTokenToSession;
57
+ exports.handleJWTCallback = handleJWTCallback;
57
58
  exports.getJWTFromCustomTokenCookie = getJWTFromCustomTokenCookie;
58
59
  exports.checkLicenseKey = checkLicenseKey;
59
60
  exports.checkRoleAccess = checkRoleAccess;
@@ -116,10 +117,9 @@ function extractRoleFromPayload(payload, serviceId, defaultRole = 'ADMIN') {
116
117
  * payload에서 NextAuth JWT 객체 생성
117
118
  * @param payload JWT payload
118
119
  * @param serviceId 서비스 ID (필수)
119
- * @param includeAcademies academies 정보 포함 여부
120
120
  * @returns NextAuth JWT 객체
121
121
  */
122
- function createNextAuthJWT(payload, serviceId, includeAcademies = false) {
122
+ function createNextAuthJWT(payload, serviceId) {
123
123
  const services = payload.services || [];
124
124
  const service = services.find((s) => s.serviceId === serviceId);
125
125
  const effectiveRole = service?.role || payload.role || 'ADMIN';
@@ -129,9 +129,6 @@ function createNextAuthJWT(payload, serviceId, includeAcademies = false) {
129
129
  name: payload.name,
130
130
  role: effectiveRole, // Role enum 타입 (string으로 캐스팅)
131
131
  services: payload.services,
132
- academies: includeAcademies
133
- ? payload.academies || []
134
- : [],
135
132
  phoneVerified: payload.phoneVerified ?? false,
136
133
  emailVerified: payload.emailVerified ?? false,
137
134
  smsVerified: payload.smsVerified ?? false,
@@ -141,10 +138,6 @@ function createNextAuthJWT(payload, serviceId, includeAcademies = false) {
141
138
  // 선택적 필드들
142
139
  if (payload.phone)
143
140
  jwt.phone = payload.phone;
144
- if (payload.academyId)
145
- jwt.academyId = payload.academyId;
146
- if (payload.academyName)
147
- jwt.academyName = payload.academyName;
148
141
  if (payload.isPasswordReset)
149
142
  jwt.isPasswordReset = payload.isPasswordReset;
150
143
  if (payload.decryptedEmail)
@@ -736,28 +729,17 @@ function createEmptySession(session) {
736
729
  * Session 콜백에서 토큰 정보를 세션에 매핑하는 헬퍼
737
730
  * @param session 기존 세션
738
731
  * @param token JWT 토큰
739
- * @param options 옵션
740
- * @param options.primaryAcademy 기본 학원 정보 (선택)
741
732
  * @returns 업데이트된 세션
742
733
  */
743
- function mapTokenToSession(session, token, options) {
734
+ function mapTokenToSession(session, token) {
744
735
  if (!session.user) {
745
736
  return session;
746
737
  }
747
- const { primaryAcademy } = options || {};
748
738
  const user = session.user;
749
739
  user.id = token.id;
750
740
  user.email = token.email;
751
741
  user.name = token.name;
752
- user.role = primaryAcademy?.role || token.role;
753
- user.academyId = primaryAcademy?.id;
754
- user.academyName = primaryAcademy?.name;
755
- if (token.academies && Array.isArray(token.academies)) {
756
- user.academies = token.academies.map((ua) => ({
757
- id: ua.id,
758
- name: ua.name,
759
- }));
760
- }
742
+ user.role = token.role;
761
743
  user.smsVerified = token.smsVerified;
762
744
  user.emailVerified = token.emailVerified;
763
745
  user.phone = token.phone;
@@ -767,6 +749,49 @@ function mapTokenToSession(session, token, options) {
767
749
  user.decryptedPhone = token.decryptedPhone;
768
750
  return session;
769
751
  }
752
+ /**
753
+ * JWT 콜백을 위한 통합 헬퍼 함수
754
+ * 초기 로그인, 토큰 갱신, 커스텀 토큰 읽기를 모두 처리
755
+ * @param token 기존 JWT 토큰
756
+ * @param user 사용자 정보 (초기 로그인 시)
757
+ * @param account 계정 정보 (초기 로그인 시)
758
+ * @param options 옵션
759
+ * @param options.secret NextAuth secret (커스텀 토큰 읽기용)
760
+ * @param options.licenseKey 라이센스 키 (커스텀 토큰 읽기용)
761
+ * @param options.serviceId 서비스 ID (커스텀 토큰 읽기용)
762
+ * @param options.cookieName 커스텀 토큰 쿠키 이름 (기본값: '{serviceId}_access_token')
763
+ * @param options.debug 디버깅 로그 출력 여부 (기본값: false)
764
+ * @returns 업데이트된 JWT 토큰
765
+ */
766
+ async function handleJWTCallback(token, user, account, options) {
767
+ const { secret, licenseKey, serviceId, cookieName, debug = false, } = options || {};
768
+ // 디버깅 로그
769
+ if (debug) {
770
+ console.log('[JWT Callback] Token received:', {
771
+ hasId: !!token.id,
772
+ hasEmail: !!token.email,
773
+ hasRole: !!token.role,
774
+ tokenKeys: Object.keys(token),
775
+ });
776
+ }
777
+ // 1. 초기 로그인 시 (providers를 통한 로그인)
778
+ if (account && user) {
779
+ return createInitialJWTToken(token, user, account);
780
+ }
781
+ // 2. 이미 토큰에 정보가 있으면 그대로 사용
782
+ if (token.id) {
783
+ return token;
784
+ }
785
+ // 3. 토큰에 id가 없는 경우 - 커스텀 토큰 쿠키에서 정보 읽기
786
+ if (secret && licenseKey && serviceId) {
787
+ const cookieNameToUse = cookieName || `${serviceId}_access_token`;
788
+ const jwt = await getJWTFromCustomTokenCookie(cookieNameToUse, secret, serviceId, licenseKey);
789
+ if (jwt) {
790
+ return jwt;
791
+ }
792
+ }
793
+ return token;
794
+ }
770
795
  /**
771
796
  * 쿠키에서 커스텀 토큰을 읽어서 NextAuth JWT로 변환하는 헬퍼 함수
772
797
  * NextAuth JWT 콜백에서 사용
@@ -774,10 +799,9 @@ function mapTokenToSession(session, token, options) {
774
799
  * @param secret JWT 서명에 사용할 secret key
775
800
  * @param serviceId 서비스 ID (필수)
776
801
  * @param licenseKey 라이센스 키 (필수)
777
- * @param includeAcademies academies 정보 포함 여부 (기본값: false)
778
802
  * @returns NextAuth JWT 객체 또는 null
779
803
  */
780
- async function getJWTFromCustomTokenCookie(cookieName, secret, serviceId, licenseKey, includeAcademies = false) {
804
+ async function getJWTFromCustomTokenCookie(cookieName, secret, serviceId, licenseKey) {
781
805
  try {
782
806
  const { cookies } = await Promise.resolve().then(() => __importStar(require('next/headers')));
783
807
  const cookieStore = await cookies();
@@ -791,7 +815,7 @@ async function getJWTFromCustomTokenCookie(cookieName, secret, serviceId, licens
791
815
  return null;
792
816
  }
793
817
  const { payload } = tokenResult;
794
- const jwt = createNextAuthJWT(payload, serviceId, includeAcademies);
818
+ const jwt = createNextAuthJWT(payload, serviceId);
795
819
  return jwt;
796
820
  }
797
821
  catch (error) {
@@ -1203,7 +1227,7 @@ async function handleMiddleware(req, config, options) {
1203
1227
  const tokenResult = await verifyToken(accessToken, secret);
1204
1228
  if (tokenResult) {
1205
1229
  const { payload } = tokenResult;
1206
- finalToken = createNextAuthJWT(payload, serviceId, true); // academies 포함
1230
+ finalToken = createNextAuthJWT(payload, serviceId);
1207
1231
  }
1208
1232
  }
1209
1233
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thinkingcat/auth-utils",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
4
4
  "description": "Authentication utilities for ThinkingCat SSO services",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",