@thinkingcat/auth-utils 1.0.49 → 2.0.0

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.
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.verifyAndRefreshToken = verifyAndRefreshToken;
4
+ exports.verifyAndRefreshTokenWithNextAuth = verifyAndRefreshTokenWithNextAuth;
5
+ const jose_1 = require("jose");
6
+ const logger_1 = require("../utils/logger");
7
+ const api_1 = require("../sso/api");
8
+ const server_1 = require("../utils/server");
9
+ const jwt_1 = require("./jwt");
10
+ const cookies_1 = require("./cookies");
11
+ /**
12
+ * 토큰 검증 및 갱신 시도
13
+ */
14
+ async function verifyAndRefreshToken(req, secret, options) {
15
+ const { cookiePrefix, serviceId, isProduction, cookieDomain, ssoBaseURL, authServiceKey, forceRefresh = false, } = options;
16
+ const accessTokenName = `${cookiePrefix}_access_token`;
17
+ const accessToken = req.cookies.get(accessTokenName)?.value;
18
+ if (accessToken && !forceRefresh) {
19
+ try {
20
+ const secretBytes = new TextEncoder().encode(secret);
21
+ const { payload } = await (0, jose_1.jwtVerify)(accessToken, secretBytes);
22
+ if (payload && typeof payload === 'object' && payload.email) {
23
+ return { isValid: true, payload: payload };
24
+ }
25
+ }
26
+ catch {
27
+ // Token verification failed
28
+ }
29
+ }
30
+ const refreshTokenName = `${cookiePrefix}_refresh_token`;
31
+ const refreshToken = req.cookies.get(refreshTokenName)?.value;
32
+ if (refreshToken) {
33
+ try {
34
+ if (!ssoBaseURL || !authServiceKey) {
35
+ return { isValid: false, error: 'SSO_CONFIG_MISSING' };
36
+ }
37
+ const refreshResult = await (0, api_1.refreshSSOToken)(refreshToken, {
38
+ ssoBaseURL,
39
+ authServiceKey,
40
+ });
41
+ if (refreshResult.success && refreshResult.accessToken) {
42
+ const newRefreshToken = refreshResult.refreshToken || refreshToken;
43
+ try {
44
+ let payload;
45
+ const secretBytes = new TextEncoder().encode(secret);
46
+ const { payload: tokenPayload } = await (0, jose_1.jwtVerify)(refreshResult.accessToken, secretBytes);
47
+ if (tokenPayload && typeof tokenPayload === 'object' && tokenPayload.email) {
48
+ payload = tokenPayload;
49
+ }
50
+ const { NextResponse: NextResponseClass } = await (0, server_1.getNextServer)();
51
+ const response = NextResponseClass.next();
52
+ const jwt = (0, jwt_1.createNextAuthJWT)(payload, serviceId);
53
+ if (newRefreshToken)
54
+ jwt.refreshToken = newRefreshToken;
55
+ jwt.accessTokenExpires = Date.now() + (15 * 60 * 1000);
56
+ try {
57
+ const encodedSessionToken = await (0, jwt_1.encodeNextAuthToken)(jwt, secret, 30 * 24 * 60 * 60);
58
+ (0, cookies_1.setNextAuthToken)(response, encodedSessionToken, {
59
+ isProduction,
60
+ cookieDomain,
61
+ });
62
+ }
63
+ catch (error) {
64
+ (0, logger_1.debugError)('verifyAndRefreshToken', 'Failed to set NextAuth session cookie', error);
65
+ }
66
+ if (newRefreshToken) {
67
+ (0, cookies_1.setCustomTokens)(response, refreshResult.accessToken, newRefreshToken, {
68
+ cookiePrefix,
69
+ isProduction,
70
+ cookieDomain,
71
+ });
72
+ }
73
+ else {
74
+ (0, cookies_1.setCustomTokens)(response, refreshResult.accessToken, {
75
+ cookiePrefix,
76
+ isProduction,
77
+ cookieDomain,
78
+ });
79
+ }
80
+ return { isValid: true, response, payload };
81
+ }
82
+ catch (error) {
83
+ return { isValid: false, error: 'SESSION_CREATION_FAILED' };
84
+ }
85
+ }
86
+ else {
87
+ const { NextResponse: NextResponseClass } = await (0, server_1.getNextServer)();
88
+ const response = NextResponseClass.next();
89
+ (0, cookies_1.clearAllAuthCookies)(response, options.cookiePrefix, options.isProduction);
90
+ return { isValid: false, response, error: 'REFRESH_FAILED' };
91
+ }
92
+ }
93
+ catch (error) {
94
+ const { NextResponse: NextResponseClass } = await (0, server_1.getNextServer)();
95
+ const response = NextResponseClass.next();
96
+ (0, cookies_1.clearAllAuthCookies)(response, options.cookiePrefix, options.isProduction);
97
+ return { isValid: false, response, error: 'REFRESH_ERROR' };
98
+ }
99
+ }
100
+ return { isValid: false, error: 'NO_TOKEN' };
101
+ }
102
+ /**
103
+ * NextAuth 토큰과 자체 토큰을 모두 확인하는 미들웨어용 함수
104
+ */
105
+ async function verifyAndRefreshTokenWithNextAuth(req, nextAuthToken, secret, options) {
106
+ const { cookiePrefix, isProduction } = options;
107
+ const nextAuthSessionTokenCookieName = isProduction
108
+ ? '__Secure-next-auth.session-token'
109
+ : 'next-auth.session-token';
110
+ const nextAuthCookieValue = req.cookies.get(nextAuthSessionTokenCookieName)?.value;
111
+ const hasNextAuthSessionTokenCookie = !!nextAuthCookieValue;
112
+ const hasValidNextAuthToken = nextAuthToken && (0, jwt_1.isValidToken)(nextAuthToken);
113
+ const accessTokenName = `${cookiePrefix}_access_token`;
114
+ const accessToken = req.cookies.get(accessTokenName)?.value;
115
+ let hasValidAccessToken = false;
116
+ if (accessToken) {
117
+ try {
118
+ const secretBytes = new TextEncoder().encode(secret);
119
+ const { payload } = await (0, jose_1.jwtVerify)(accessToken, secretBytes);
120
+ if (payload && typeof payload === 'object' && payload.email) {
121
+ hasValidAccessToken = true;
122
+ }
123
+ }
124
+ catch {
125
+ // Ignored
126
+ }
127
+ }
128
+ const refreshTokenName = `${cookiePrefix}_refresh_token`;
129
+ const refreshToken = req.cookies.get(refreshTokenName)?.value;
130
+ if (hasValidNextAuthToken && hasValidAccessToken) {
131
+ let payload;
132
+ if (accessToken) {
133
+ try {
134
+ const secretBytes = new TextEncoder().encode(secret);
135
+ const result = await (0, jose_1.jwtVerify)(accessToken, secretBytes);
136
+ payload = result.payload;
137
+ }
138
+ catch {
139
+ // Ignored
140
+ }
141
+ }
142
+ return {
143
+ isValid: true,
144
+ token: nextAuthToken,
145
+ payload
146
+ };
147
+ }
148
+ if (refreshToken && (!hasValidNextAuthToken || !hasValidAccessToken)) {
149
+ const authCheck = await verifyAndRefreshToken(req, secret, {
150
+ ...options,
151
+ forceRefresh: true,
152
+ });
153
+ let refreshedToken = null;
154
+ if (authCheck.isValid && authCheck.payload) {
155
+ refreshedToken = (0, jwt_1.createNextAuthJWT)(authCheck.payload, options.serviceId);
156
+ }
157
+ return {
158
+ ...authCheck,
159
+ token: refreshedToken || undefined
160
+ };
161
+ }
162
+ if (hasValidNextAuthToken || hasValidAccessToken) {
163
+ let payload;
164
+ if (accessToken && hasValidAccessToken) {
165
+ try {
166
+ const secretBytes = new TextEncoder().encode(secret);
167
+ const result = await (0, jose_1.jwtVerify)(accessToken, secretBytes);
168
+ payload = result.payload;
169
+ }
170
+ catch {
171
+ // Ignored
172
+ }
173
+ }
174
+ return {
175
+ isValid: true,
176
+ token: nextAuthToken || (payload ? (0, jwt_1.createNextAuthJWT)(payload, options.serviceId) : undefined),
177
+ payload
178
+ };
179
+ }
180
+ return { isValid: false, error: 'NO_TOKEN' };
181
+ }