@thinkingcat/auth-utils 1.0.13 → 1.0.14

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,4 +1,5 @@
1
1
  import { JWT } from "next-auth/jwt";
2
+ import type { Session } from "next-auth";
2
3
  import { NextResponse, NextRequest } from 'next/server';
3
4
  export interface ResponseLike {
4
5
  cookies: {
@@ -325,6 +326,140 @@ export interface MiddlewareOptions {
325
326
  /** 라이센스 키 (필수) */
326
327
  licenseKey: string;
327
328
  }
329
+ /**
330
+ * NextAuth 쿠키 설정 생성
331
+ * @param options 옵션
332
+ * @param options.isProduction 프로덕션 환경 여부
333
+ * @param options.cookieDomain 쿠키 도메인 (선택)
334
+ * @returns NextAuth 쿠키 설정 객체
335
+ */
336
+ export declare function createNextAuthCookies(options: {
337
+ isProduction?: boolean;
338
+ cookieDomain?: string;
339
+ }): {
340
+ sessionToken: {
341
+ name: string;
342
+ options: {
343
+ httpOnly: boolean;
344
+ sameSite: 'lax' | 'none';
345
+ path: string;
346
+ secure: boolean;
347
+ domain?: string;
348
+ };
349
+ };
350
+ callbackUrl: {
351
+ name: string;
352
+ options: {
353
+ sameSite: 'lax' | 'none';
354
+ path: string;
355
+ secure: boolean;
356
+ domain?: string;
357
+ };
358
+ };
359
+ csrfToken: {
360
+ name: string;
361
+ options: {
362
+ httpOnly: boolean;
363
+ sameSite: 'lax' | 'none';
364
+ path: string;
365
+ secure: boolean;
366
+ domain?: string;
367
+ };
368
+ };
369
+ };
370
+ /**
371
+ * NextAuth 기본 설정 생성
372
+ * @param options 옵션
373
+ * @param options.secret NextAuth secret
374
+ * @param options.isProduction 프로덕션 환경 여부
375
+ * @param options.cookieDomain 쿠키 도메인 (선택)
376
+ * @param options.signInPath 로그인 페이지 경로 (기본값: '/login')
377
+ * @param options.errorPath 에러 페이지 경로 (기본값: '/login')
378
+ * @param options.nextAuthUrl NextAuth URL (선택)
379
+ * @param options.sessionMaxAge 세션 최대 유지 시간 (초, 기본값: 30일)
380
+ * @param options.jwtMaxAge JWT 최대 유지 시간 (초, 기본값: 30일)
381
+ * @returns NextAuth 기본 설정 객체
382
+ */
383
+ export declare function createNextAuthBaseConfig(options: {
384
+ secret: string;
385
+ isProduction?: boolean;
386
+ cookieDomain?: string;
387
+ signInPath?: string;
388
+ errorPath?: string;
389
+ nextAuthUrl?: string;
390
+ sessionMaxAge?: number;
391
+ jwtMaxAge?: number;
392
+ }): {
393
+ session: {
394
+ strategy: 'jwt';
395
+ maxAge: number;
396
+ };
397
+ jwt: {
398
+ maxAge: number;
399
+ };
400
+ providers: never[];
401
+ url?: string;
402
+ pages: {
403
+ signIn: string;
404
+ error: string;
405
+ };
406
+ cookies: ReturnType<typeof createNextAuthCookies>;
407
+ secret: string;
408
+ };
409
+ /**
410
+ * JWT 콜백에서 초기 로그인 시 토큰 생성 헬퍼
411
+ * @param token 기존 토큰
412
+ * @param user 사용자 정보
413
+ * @param account 계정 정보
414
+ * @returns 업데이트된 JWT 토큰
415
+ */
416
+ export declare function createInitialJWTToken(token: JWT, user: {
417
+ id: string;
418
+ email?: string | null;
419
+ emailHash?: string | null;
420
+ maskedEmail?: string | null;
421
+ phoneHash?: string | null;
422
+ maskedPhone?: string | null;
423
+ role?: string;
424
+ phone?: string | null;
425
+ decryptedEmail?: string | null;
426
+ decryptedPhone?: string | null;
427
+ refreshToken?: string | null;
428
+ }, account?: {
429
+ serviceId?: string;
430
+ } | null): JWT;
431
+ /**
432
+ * Session 콜백에서 빈 세션 반환 헬퍼
433
+ * @param session 기존 세션
434
+ * @returns 빈 세션 객체
435
+ */
436
+ export declare function createEmptySession(session: Session): Session;
437
+ /**
438
+ * Session 콜백에서 토큰 정보를 세션에 매핑하는 헬퍼
439
+ * @param session 기존 세션
440
+ * @param token JWT 토큰
441
+ * @param options 옵션
442
+ * @param options.primaryAcademy 기본 학원 정보 (선택)
443
+ * @returns 업데이트된 세션
444
+ */
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;
452
+ /**
453
+ * 쿠키에서 커스텀 토큰을 읽어서 NextAuth JWT로 변환하는 헬퍼 함수
454
+ * NextAuth JWT 콜백에서 사용
455
+ * @param cookieName 쿠키 이름 (예: 'checkon_access_token')
456
+ * @param secret JWT 서명에 사용할 secret key
457
+ * @param serviceId 서비스 ID (필수)
458
+ * @param licenseKey 라이센스 키 (필수)
459
+ * @param includeAcademies academies 정보 포함 여부 (기본값: false)
460
+ * @returns NextAuth JWT 객체 또는 null
461
+ */
462
+ export declare function getJWTFromCustomTokenCookie(cookieName: string, secret: string, serviceId: string, licenseKey: string, includeAcademies?: boolean): Promise<JWT | null>;
328
463
  export declare function checkLicenseKey(licenseKey: string): Promise<void>;
329
464
  export declare function checkRoleAccess(pathname: string, role: string, roleConfig: RoleAccessConfig): {
330
465
  allowed: boolean;
package/dist/index.js CHANGED
@@ -49,6 +49,12 @@ exports.redirectToError = redirectToError;
49
49
  exports.clearAuthCookies = clearAuthCookies;
50
50
  exports.getEffectiveRole = getEffectiveRole;
51
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.getJWTFromCustomTokenCookie = getJWTFromCustomTokenCookie;
52
58
  exports.checkLicenseKey = checkLicenseKey;
53
59
  exports.checkRoleAccess = checkRoleAccess;
54
60
  exports.redirectToSSOLogin = redirectToSSOLogin;
@@ -607,6 +613,192 @@ function requiresSubscription(pathname, role, subscriptionRequiredPaths, systemA
607
613
  const VALID_LICENSE_KEY_HASHES = new Set([
608
614
  '73bce4f3b64804c255cdab450d759a8b53038f9edb59ae42d9988b08dfd007e2',
609
615
  ]);
616
+ /**
617
+ * NextAuth 쿠키 설정 생성
618
+ * @param options 옵션
619
+ * @param options.isProduction 프로덕션 환경 여부
620
+ * @param options.cookieDomain 쿠키 도메인 (선택)
621
+ * @returns NextAuth 쿠키 설정 객체
622
+ */
623
+ function createNextAuthCookies(options) {
624
+ const { isProduction = false, cookieDomain } = options;
625
+ const isSecure = isProduction;
626
+ return {
627
+ sessionToken: {
628
+ name: isSecure ? `__Secure-next-auth.session-token` : `next-auth.session-token`,
629
+ options: {
630
+ httpOnly: true,
631
+ sameSite: isSecure ? 'none' : 'lax',
632
+ path: '/',
633
+ secure: isSecure,
634
+ ...(cookieDomain && { domain: cookieDomain }),
635
+ },
636
+ },
637
+ callbackUrl: {
638
+ name: isSecure ? `__Secure-next-auth.callback-url` : `next-auth.callback-url`,
639
+ options: {
640
+ sameSite: isSecure ? 'none' : 'lax',
641
+ path: '/',
642
+ secure: isSecure,
643
+ ...(cookieDomain && { domain: cookieDomain }),
644
+ },
645
+ },
646
+ csrfToken: {
647
+ name: isSecure ? `__Secure-next-auth.csrf-token` : `next-auth.csrf-token`,
648
+ options: {
649
+ httpOnly: true,
650
+ sameSite: isSecure ? 'none' : 'lax',
651
+ path: '/',
652
+ secure: isSecure,
653
+ ...(cookieDomain && { domain: cookieDomain }),
654
+ },
655
+ },
656
+ };
657
+ }
658
+ /**
659
+ * NextAuth 기본 설정 생성
660
+ * @param options 옵션
661
+ * @param options.secret NextAuth secret
662
+ * @param options.isProduction 프로덕션 환경 여부
663
+ * @param options.cookieDomain 쿠키 도메인 (선택)
664
+ * @param options.signInPath 로그인 페이지 경로 (기본값: '/login')
665
+ * @param options.errorPath 에러 페이지 경로 (기본값: '/login')
666
+ * @param options.nextAuthUrl NextAuth URL (선택)
667
+ * @param options.sessionMaxAge 세션 최대 유지 시간 (초, 기본값: 30일)
668
+ * @param options.jwtMaxAge JWT 최대 유지 시간 (초, 기본값: 30일)
669
+ * @returns NextAuth 기본 설정 객체
670
+ */
671
+ function createNextAuthBaseConfig(options) {
672
+ const { secret, isProduction = false, cookieDomain, signInPath = '/login', errorPath = '/login', nextAuthUrl, sessionMaxAge = 30 * 24 * 60 * 60, // 30일
673
+ jwtMaxAge = 30 * 24 * 60 * 60, // 30일
674
+ } = options;
675
+ return {
676
+ session: {
677
+ strategy: 'jwt',
678
+ maxAge: sessionMaxAge,
679
+ },
680
+ jwt: {
681
+ maxAge: jwtMaxAge,
682
+ },
683
+ providers: [],
684
+ ...(nextAuthUrl && { url: nextAuthUrl }),
685
+ pages: {
686
+ signIn: signInPath,
687
+ error: errorPath,
688
+ },
689
+ cookies: createNextAuthCookies({ isProduction, cookieDomain }),
690
+ secret,
691
+ };
692
+ }
693
+ /**
694
+ * JWT 콜백에서 초기 로그인 시 토큰 생성 헬퍼
695
+ * @param token 기존 토큰
696
+ * @param user 사용자 정보
697
+ * @param account 계정 정보
698
+ * @returns 업데이트된 JWT 토큰
699
+ */
700
+ function createInitialJWTToken(token, user, account) {
701
+ return {
702
+ ...token,
703
+ id: user.id,
704
+ email: user.email ?? undefined,
705
+ emailHash: user.emailHash ?? undefined,
706
+ maskedEmail: user.maskedEmail ?? undefined,
707
+ phoneHash: user.phoneHash ?? undefined,
708
+ maskedPhone: user.maskedPhone ?? undefined,
709
+ role: user.role,
710
+ phone: user.phone ?? undefined,
711
+ decryptedEmail: user.decryptedEmail ?? undefined,
712
+ decryptedPhone: user.decryptedPhone ?? undefined,
713
+ refreshToken: user.refreshToken ?? undefined,
714
+ accessTokenExpires: Date.now() + (15 * 60 * 1000), // 15분
715
+ serviceId: account?.serviceId ?? undefined,
716
+ };
717
+ }
718
+ /**
719
+ * Session 콜백에서 빈 세션 반환 헬퍼
720
+ * @param session 기존 세션
721
+ * @returns 빈 세션 객체
722
+ */
723
+ function createEmptySession(session) {
724
+ return {
725
+ ...session,
726
+ user: {
727
+ ...session.user,
728
+ id: '',
729
+ email: null,
730
+ role: 'GUEST',
731
+ },
732
+ expires: new Date().toISOString(),
733
+ };
734
+ }
735
+ /**
736
+ * Session 콜백에서 토큰 정보를 세션에 매핑하는 헬퍼
737
+ * @param session 기존 세션
738
+ * @param token JWT 토큰
739
+ * @param options 옵션
740
+ * @param options.primaryAcademy 기본 학원 정보 (선택)
741
+ * @returns 업데이트된 세션
742
+ */
743
+ function mapTokenToSession(session, token, options) {
744
+ if (!session.user) {
745
+ return session;
746
+ }
747
+ const { primaryAcademy } = options || {};
748
+ const user = session.user;
749
+ user.id = token.id;
750
+ user.email = token.email;
751
+ 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
+ }
761
+ user.smsVerified = token.smsVerified;
762
+ user.emailVerified = token.emailVerified;
763
+ user.phone = token.phone;
764
+ user.phoneVerified = token.phoneVerified;
765
+ user.isPasswordReset = token.isPasswordReset || false;
766
+ user.decryptedEmail = token.decryptedEmail;
767
+ user.decryptedPhone = token.decryptedPhone;
768
+ return session;
769
+ }
770
+ /**
771
+ * 쿠키에서 커스텀 토큰을 읽어서 NextAuth JWT로 변환하는 헬퍼 함수
772
+ * NextAuth JWT 콜백에서 사용
773
+ * @param cookieName 쿠키 이름 (예: 'checkon_access_token')
774
+ * @param secret JWT 서명에 사용할 secret key
775
+ * @param serviceId 서비스 ID (필수)
776
+ * @param licenseKey 라이센스 키 (필수)
777
+ * @param includeAcademies academies 정보 포함 여부 (기본값: false)
778
+ * @returns NextAuth JWT 객체 또는 null
779
+ */
780
+ async function getJWTFromCustomTokenCookie(cookieName, secret, serviceId, licenseKey, includeAcademies = false) {
781
+ try {
782
+ const { cookies } = await Promise.resolve().then(() => __importStar(require('next/headers')));
783
+ const cookieStore = await cookies();
784
+ const accessToken = cookieStore.get(cookieName)?.value;
785
+ if (!accessToken) {
786
+ return null;
787
+ }
788
+ await checkLicenseKey(licenseKey);
789
+ const tokenResult = await verifyToken(accessToken, secret);
790
+ if (!tokenResult) {
791
+ return null;
792
+ }
793
+ const { payload } = tokenResult;
794
+ const jwt = createNextAuthJWT(payload, serviceId, includeAcademies);
795
+ return jwt;
796
+ }
797
+ catch (error) {
798
+ console.error(`[getJWTFromCustomTokenCookie] Failed to read ${cookieName}:`, error);
799
+ return null;
800
+ }
801
+ }
610
802
  async function checkLicenseKey(licenseKey) {
611
803
  if (!licenseKey || licenseKey.length < 10) {
612
804
  throw new Error('License key is required');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thinkingcat/auth-utils",
3
- "version": "1.0.13",
3
+ "version": "1.0.14",
4
4
  "description": "Authentication utilities for ThinkingCat SSO services",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",