nextjs-cms 0.5.55 → 0.5.56
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/api/index.d.ts +8 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/root.d.ts +8 -0
- package/dist/api/root.d.ts.map +1 -1
- package/dist/api/routers/auth.d.ts +8 -0
- package/dist/api/routers/auth.d.ts.map +1 -1
- package/dist/auth/hooks/useRefreshToken.d.ts.map +1 -1
- package/dist/auth/hooks/useRefreshToken.js +11 -9
- package/dist/auth/lib/actions.d.ts +10 -0
- package/dist/auth/lib/actions.d.ts.map +1 -1
- package/dist/auth/lib/actions.js +50 -20
- package/dist/auth/react.d.ts +7 -1
- package/dist/auth/react.d.ts.map +1 -1
- package/dist/auth/react.js +50 -28
- package/dist/auth/trpc.d.ts.map +1 -1
- package/dist/auth/trpc.js +2 -1
- package/package.json +3 -3
package/dist/api/index.d.ts
CHANGED
|
@@ -63,6 +63,14 @@ declare const createCaller: import("@trpc/server").TRPCRouterCaller<{
|
|
|
63
63
|
password: string;
|
|
64
64
|
};
|
|
65
65
|
output: {
|
|
66
|
+
status: number;
|
|
67
|
+
session: {
|
|
68
|
+
user: {
|
|
69
|
+
id: string;
|
|
70
|
+
name: string;
|
|
71
|
+
locale: string | null;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
66
74
|
user: {
|
|
67
75
|
id: string;
|
|
68
76
|
username: string;
|
package/dist/api/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAEzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACrC,OAAO,EAAuB,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAElE;;;;;;GAMG;AACH,QAAA,MAAM,YAAY
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAEzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACrC,OAAO,EAAuB,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAElE;;;;;;GAMG;AACH,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAAiC,CAAA;AAEnD;;;;;IAKI;AACJ,KAAK,YAAY,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAA;AAEhD;;;;;IAKI;AACJ,KAAK,aAAa,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAA;AAElD,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,YAAY,EAAE,CAAA;AACrD,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA"}
|
package/dist/api/root.d.ts
CHANGED
|
@@ -52,6 +52,14 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
52
52
|
password: string;
|
|
53
53
|
};
|
|
54
54
|
output: {
|
|
55
|
+
status: number;
|
|
56
|
+
session: {
|
|
57
|
+
user: {
|
|
58
|
+
id: string;
|
|
59
|
+
name: string;
|
|
60
|
+
locale: string | null;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
55
63
|
user: {
|
|
56
64
|
id: string;
|
|
57
65
|
username: string;
|
package/dist/api/root.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root.d.ts","sourceRoot":"","sources":["../../src/api/root.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,SAAS
|
|
1
|
+
{"version":3,"file":"root.d.ts","sourceRoot":"","sources":["../../src/api/root.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgBpB,CAAA;AAIF,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAA"}
|
|
@@ -29,6 +29,14 @@ export declare const authRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
29
29
|
password: string;
|
|
30
30
|
};
|
|
31
31
|
output: {
|
|
32
|
+
status: number;
|
|
33
|
+
session: {
|
|
34
|
+
user: {
|
|
35
|
+
id: string;
|
|
36
|
+
name: string;
|
|
37
|
+
locale: string | null;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
32
40
|
user: {
|
|
33
41
|
id: string;
|
|
34
42
|
username: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/api/routers/auth.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAIxB,eAAO,MAAM,UAAU
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/api/routers/auth.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAIxB,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsBrB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useRefreshToken.d.ts","sourceRoot":"","sources":["../../../src/auth/hooks/useRefreshToken.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useRefreshToken.d.ts","sourceRoot":"","sources":["../../../src/auth/hooks/useRefreshToken.tsx"],"names":[],"mappings":"AAGA;;;GAGG;AACH,QAAA,MAAM,eAAe,8BA8EpB,CAAA;AAED,eAAe,eAAe,CAAA"}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
1
2
|
import { logout, refreshSession } from '../react.js';
|
|
2
3
|
/**
|
|
3
4
|
* This hook is used to refresh the access token when it expires.
|
|
4
5
|
* It is used in the useAxiosPrivate hook to refresh the access token when a request returns a 401 error.
|
|
5
6
|
*/
|
|
6
7
|
const useRefreshToken = () => {
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
const isRefreshingRef = React.useRef(false); // Is a refresh request being sent?
|
|
9
|
+
// An array of requests that failed because of 401
|
|
10
|
+
const failedQueueRef = React.useRef([]);
|
|
9
11
|
const processQueue = (error, token = null) => {
|
|
10
|
-
|
|
12
|
+
failedQueueRef.current.forEach((prom) => {
|
|
11
13
|
if (error) {
|
|
12
14
|
prom.reject(error);
|
|
13
15
|
}
|
|
@@ -15,7 +17,7 @@ const useRefreshToken = () => {
|
|
|
15
17
|
prom.resolve(token);
|
|
16
18
|
}
|
|
17
19
|
});
|
|
18
|
-
|
|
20
|
+
failedQueueRef.current = [];
|
|
19
21
|
};
|
|
20
22
|
const refresh = async () => {
|
|
21
23
|
try {
|
|
@@ -24,7 +26,7 @@ const useRefreshToken = () => {
|
|
|
24
26
|
/**
|
|
25
27
|
* The refresh request is done
|
|
26
28
|
*/
|
|
27
|
-
|
|
29
|
+
isRefreshingRef.current = false;
|
|
28
30
|
if (response.status !== 200) {
|
|
29
31
|
/**
|
|
30
32
|
* If the refresh token is invalid, we log out the user
|
|
@@ -41,7 +43,7 @@ const useRefreshToken = () => {
|
|
|
41
43
|
/**
|
|
42
44
|
* update the session
|
|
43
45
|
*/
|
|
44
|
-
await refreshSession();
|
|
46
|
+
await refreshSession(data.session);
|
|
45
47
|
}
|
|
46
48
|
/**
|
|
47
49
|
* Process the failed requests
|
|
@@ -65,14 +67,14 @@ const useRefreshToken = () => {
|
|
|
65
67
|
// TODO: Apply this inside useAxiosPrivate.tsx to prevent even the 401 errors from happening
|
|
66
68
|
// Let's use semaphores to prevent multiple refreshes at the same time
|
|
67
69
|
return async () => {
|
|
68
|
-
if (
|
|
70
|
+
if (isRefreshingRef.current) {
|
|
69
71
|
// If a refresh request is being sent, we return a promise
|
|
70
72
|
// that will be resolved when the refresh request is done
|
|
71
73
|
return new Promise((resolve, reject) => {
|
|
72
|
-
|
|
74
|
+
failedQueueRef.current.push({ resolve, reject });
|
|
73
75
|
});
|
|
74
76
|
}
|
|
75
|
-
|
|
77
|
+
isRefreshingRef.current = true; // A refresh request is being sent
|
|
76
78
|
return await refresh(); // Send the refresh request and return the new access token
|
|
77
79
|
};
|
|
78
80
|
};
|
|
@@ -5,10 +5,12 @@ export declare const authRefresh: (refreshToken?: {
|
|
|
5
5
|
status: number;
|
|
6
6
|
code: string;
|
|
7
7
|
state: string;
|
|
8
|
+
session?: undefined;
|
|
8
9
|
user?: undefined;
|
|
9
10
|
} | {
|
|
10
11
|
status: number;
|
|
11
12
|
state: string;
|
|
13
|
+
session: Session;
|
|
12
14
|
user: {
|
|
13
15
|
id: string;
|
|
14
16
|
username: string;
|
|
@@ -21,6 +23,14 @@ export declare const login: ({ username, password }: {
|
|
|
21
23
|
username: string;
|
|
22
24
|
password: string;
|
|
23
25
|
}) => Promise<{
|
|
26
|
+
status: number;
|
|
27
|
+
session: {
|
|
28
|
+
user: {
|
|
29
|
+
id: string;
|
|
30
|
+
name: string;
|
|
31
|
+
locale: string | null;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
24
34
|
user: {
|
|
25
35
|
id: string;
|
|
26
36
|
username: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../../src/auth/lib/actions.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAE1C,eAAO,MAAM,WAAW,GAAU,eAAe;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE
|
|
1
|
+
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../../src/auth/lib/actions.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAE1C,eAAO,MAAM,WAAW,GAAU,eAAe;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;;EA4HjE,CAAA;AAED,eAAO,MAAM,KAAK,GAAU,wBAAwB;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;EA+GzF,CAAA;AAED,eAAO,MAAM,aAAa,GAAU,UAAU,OAAO,GAAG,IAAI,qBA0B3D,CAAA"}
|
package/dist/auth/lib/actions.js
CHANGED
|
@@ -29,7 +29,7 @@ export const authRefresh = async (refreshToken) => {
|
|
|
29
29
|
.where(and(eq(AccessTokensTable.adminId, decoded.id), eq(AccessTokensTable.refreshToken, refreshToken.value)))
|
|
30
30
|
.limit(1);
|
|
31
31
|
const adminTokenResult = result[0];
|
|
32
|
-
if (!adminTokenResult) {
|
|
32
|
+
if (!adminTokenResult || !adminTokenResult.adminId || !adminTokenResult.username) {
|
|
33
33
|
return {
|
|
34
34
|
status: 401,
|
|
35
35
|
code: 'invalid_refresh_token',
|
|
@@ -39,17 +39,24 @@ export const authRefresh = async (refreshToken) => {
|
|
|
39
39
|
// Let's create a JWT access token and refresh token
|
|
40
40
|
const accessToken = encodeJWT({
|
|
41
41
|
id: decoded.id,
|
|
42
|
-
iss: '
|
|
42
|
+
iss: 'nextjs-cms',
|
|
43
43
|
aud: 'admin',
|
|
44
44
|
sub: adminTokenResult.username,
|
|
45
45
|
locale: adminTokenResult.locale || 'en',
|
|
46
46
|
});
|
|
47
47
|
const newRefreshToken = encodeRefreshToken({
|
|
48
48
|
id: adminTokenResult.adminId,
|
|
49
|
-
iss: '
|
|
49
|
+
iss: 'nextjs-cms',
|
|
50
50
|
aud: 'admin',
|
|
51
51
|
sub: adminTokenResult.username,
|
|
52
52
|
});
|
|
53
|
+
if (!accessToken || !newRefreshToken) {
|
|
54
|
+
return {
|
|
55
|
+
status: 401,
|
|
56
|
+
code: 'failed_to_create_tokens',
|
|
57
|
+
state: 'logged_out',
|
|
58
|
+
};
|
|
59
|
+
}
|
|
53
60
|
// Let's update the refresh token in the database
|
|
54
61
|
await db
|
|
55
62
|
.update(AccessTokensTable)
|
|
@@ -78,10 +85,17 @@ export const authRefresh = async (refreshToken) => {
|
|
|
78
85
|
sameSite: true,
|
|
79
86
|
maxAge: 60 * 60 * 2, // 2 hours
|
|
80
87
|
});
|
|
88
|
+
const session = {
|
|
89
|
+
user: {
|
|
90
|
+
id: adminTokenResult.adminId,
|
|
91
|
+
name: adminTokenResult.username,
|
|
92
|
+
locale: adminTokenResult.locale ?? null,
|
|
93
|
+
},
|
|
94
|
+
};
|
|
81
95
|
return {
|
|
82
96
|
status: 200,
|
|
83
|
-
// accessToken: accessToken,
|
|
84
97
|
state: 'logged_in',
|
|
98
|
+
session,
|
|
85
99
|
user: {
|
|
86
100
|
id: adminTokenResult.adminId,
|
|
87
101
|
username: adminTokenResult.username,
|
|
@@ -114,7 +128,7 @@ export const login = async ({ username, password }) => {
|
|
|
114
128
|
.where(eq(AdminsTable.user, username))
|
|
115
129
|
.limit(1);
|
|
116
130
|
const admin = result[0];
|
|
117
|
-
if (!admin) {
|
|
131
|
+
if (!admin || !admin.password || !admin.id || !admin.username) {
|
|
118
132
|
throw new Error('Invalid credentials');
|
|
119
133
|
}
|
|
120
134
|
// Verify password with bcrypt
|
|
@@ -125,33 +139,41 @@ export const login = async ({ username, password }) => {
|
|
|
125
139
|
// Let's create a JWT access token and refresh token
|
|
126
140
|
const accessToken = encodeJWT({
|
|
127
141
|
id: admin.id,
|
|
128
|
-
iss: '
|
|
142
|
+
iss: 'nextjs-cms',
|
|
129
143
|
aud: 'admin',
|
|
130
144
|
sub: admin.username,
|
|
131
145
|
locale: admin.locale || 'en',
|
|
132
146
|
});
|
|
133
147
|
const refreshToken = encodeRefreshToken({
|
|
134
148
|
id: admin.id,
|
|
135
|
-
iss: '
|
|
149
|
+
iss: 'nextjs-cms',
|
|
136
150
|
aud: 'admin',
|
|
137
151
|
sub: admin.username,
|
|
138
152
|
});
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
})
|
|
148
|
-
.onDuplicateKeyUpdate({
|
|
149
|
-
set: {
|
|
153
|
+
if (!accessToken || !refreshToken) {
|
|
154
|
+
throw new Error('Failed to create access and refresh tokens');
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
// Let's save the refresh token in the database
|
|
158
|
+
await db
|
|
159
|
+
.insert(AccessTokensTable)
|
|
160
|
+
.values({
|
|
150
161
|
accessToken,
|
|
151
162
|
refreshToken,
|
|
163
|
+
adminId: admin.id,
|
|
152
164
|
expiration: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
|
|
153
|
-
}
|
|
154
|
-
|
|
165
|
+
})
|
|
166
|
+
.onDuplicateKeyUpdate({
|
|
167
|
+
set: {
|
|
168
|
+
accessToken,
|
|
169
|
+
refreshToken,
|
|
170
|
+
expiration: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
throw new Error('Failed to save access and refresh tokens to database');
|
|
176
|
+
}
|
|
155
177
|
const cookieStore = await cookies();
|
|
156
178
|
// Let's send the refresh token as a cookie
|
|
157
179
|
cookieStore.set({
|
|
@@ -173,6 +195,14 @@ export const login = async ({ username, password }) => {
|
|
|
173
195
|
});
|
|
174
196
|
// Now, return the access token in body
|
|
175
197
|
return {
|
|
198
|
+
status: 200,
|
|
199
|
+
session: {
|
|
200
|
+
user: {
|
|
201
|
+
id: admin.id,
|
|
202
|
+
name: admin.username,
|
|
203
|
+
locale: admin.locale,
|
|
204
|
+
},
|
|
205
|
+
},
|
|
176
206
|
user: {
|
|
177
207
|
id: admin.id,
|
|
178
208
|
username: admin.username,
|
package/dist/auth/react.d.ts
CHANGED
|
@@ -45,6 +45,12 @@ export interface AuthClientConfig {
|
|
|
45
45
|
* trigger session updates from places like `signIn` or `signOut`
|
|
46
46
|
*/
|
|
47
47
|
_getSession: (...args: any[]) => any;
|
|
48
|
+
/**
|
|
49
|
+
* Directly set the session in the current tab/window,
|
|
50
|
+
* after being fetched from the server during a refresh request.
|
|
51
|
+
* This is to avoid the need to fetch the session from the server again.
|
|
52
|
+
*/
|
|
53
|
+
_setSession: (session: Session | null) => void;
|
|
48
54
|
}
|
|
49
55
|
export declare const __AUTH: AuthClientConfig;
|
|
50
56
|
export declare function logout(options?: {
|
|
@@ -54,7 +60,7 @@ export declare function login({ username, password }: {
|
|
|
54
60
|
username: string;
|
|
55
61
|
password: string;
|
|
56
62
|
}): Promise<void>;
|
|
57
|
-
export declare function refreshSession(): Promise<void>;
|
|
63
|
+
export declare function refreshSession(session: Session): Promise<void>;
|
|
58
64
|
/**
|
|
59
65
|
* Returns the current CSRF token.
|
|
60
66
|
* required to make requests that changes state.
|
package/dist/auth/react.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../../src/auth/react.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAEzC,OAAO,eAAe,MAAM,4BAA4B,CAAA;AACxD,OAAO,eAAe,MAAM,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../../src/auth/react.tsx"],"names":[],"mappings":"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"}
|
package/dist/auth/react.js
CHANGED
|
@@ -11,6 +11,7 @@ export const __AUTH = {
|
|
|
11
11
|
_lastSync: 0,
|
|
12
12
|
_session: undefined,
|
|
13
13
|
_getSession: () => { },
|
|
14
|
+
_setSession: () => { },
|
|
14
15
|
};
|
|
15
16
|
export async function logout(options) {
|
|
16
17
|
const { deleteCookies = true } = options ?? {};
|
|
@@ -51,26 +52,27 @@ export async function login({ username, password }) {
|
|
|
51
52
|
const error = await response.json();
|
|
52
53
|
throw new Error(error.error);
|
|
53
54
|
}
|
|
55
|
+
const data = await response.json();
|
|
54
56
|
/**
|
|
55
57
|
* Broadcast a message to all other tabs/windows to tell them the user has logged in.
|
|
56
58
|
* This is to ensure all tabs/windows trigger a session update.
|
|
57
59
|
*/
|
|
58
|
-
broadcast().postMessage({ event: 'session', data: { trigger: '
|
|
60
|
+
broadcast().postMessage({ event: 'session', data: { trigger: 'refetch' } });
|
|
59
61
|
/**
|
|
60
62
|
* Update the session in the current tab/window.
|
|
61
63
|
*/
|
|
62
|
-
|
|
64
|
+
__AUTH._setSession(data.session);
|
|
63
65
|
}
|
|
64
|
-
export async function refreshSession() {
|
|
66
|
+
export async function refreshSession(session) {
|
|
65
67
|
/**
|
|
66
68
|
* Broadcast a message to all other tabs/windows to tell them the session has been updated.
|
|
67
69
|
* This is to ensure all tabs/windows trigger a session update.
|
|
68
70
|
*/
|
|
69
|
-
broadcast().postMessage({ event: 'session', data: { trigger: '
|
|
71
|
+
broadcast().postMessage({ event: 'session', data: { trigger: 'refetch' } });
|
|
70
72
|
/**
|
|
71
73
|
* Update the session in the current tab/window.
|
|
72
74
|
*/
|
|
73
|
-
|
|
75
|
+
__AUTH._setSession(session);
|
|
74
76
|
}
|
|
75
77
|
/**
|
|
76
78
|
* Returns the current CSRF token.
|
|
@@ -90,8 +92,8 @@ function broadcast() {
|
|
|
90
92
|
if (typeof BroadcastChannel === 'undefined') {
|
|
91
93
|
return {
|
|
92
94
|
postMessage: () => { },
|
|
93
|
-
addEventListener: () => { },
|
|
94
|
-
removeEventListener: () => { },
|
|
95
|
+
addEventListener: (_type, _listener) => { },
|
|
96
|
+
removeEventListener: (_type, _listener) => { },
|
|
95
97
|
};
|
|
96
98
|
}
|
|
97
99
|
if (broadcastChannel === null) {
|
|
@@ -176,6 +178,11 @@ export function SessionProvider(props) {
|
|
|
176
178
|
/** If session was passed, initialize as not loading */
|
|
177
179
|
const [loading, setLoading] = React.useState(!hasInitialSession);
|
|
178
180
|
React.useEffect(() => {
|
|
181
|
+
__AUTH._setSession = (session) => {
|
|
182
|
+
__AUTH._lastSync = now();
|
|
183
|
+
__AUTH._session = session;
|
|
184
|
+
setSession(session);
|
|
185
|
+
};
|
|
179
186
|
async function generateInitialSession() {
|
|
180
187
|
/**
|
|
181
188
|
* Use the `axiosPrivate` instance to make a request to `/api/auth/session` to get the
|
|
@@ -209,18 +216,15 @@ export function SessionProvider(props) {
|
|
|
209
216
|
*/
|
|
210
217
|
init()
|
|
211
218
|
.then(async (session) => {
|
|
212
|
-
|
|
219
|
+
/**
|
|
220
|
+
* Only update if we fetched a new session (i.e., no initial session was provided).
|
|
221
|
+
* When hasInitialSession is true, __AUTH._session and state are already set,
|
|
222
|
+
* so we skip the redundant update.
|
|
223
|
+
*/
|
|
224
|
+
if (!hasInitialSession && session) {
|
|
213
225
|
__AUTH._session = session;
|
|
214
226
|
setSession(session);
|
|
215
227
|
}
|
|
216
|
-
else {
|
|
217
|
-
/**
|
|
218
|
-
* It's not necessary to log out the user and broadcast the logout event,
|
|
219
|
-
* because there are no cookies set to delete.
|
|
220
|
-
* But it's a good practice to do so.
|
|
221
|
-
*/
|
|
222
|
-
// await logout()
|
|
223
|
-
}
|
|
224
228
|
})
|
|
225
229
|
.then(() => {
|
|
226
230
|
__AUTH._getSession = async ({ event } = {}) => {
|
|
@@ -235,11 +239,11 @@ export function SessionProvider(props) {
|
|
|
235
239
|
return;
|
|
236
240
|
}
|
|
237
241
|
/**
|
|
238
|
-
* If the event is a
|
|
239
|
-
*
|
|
242
|
+
* If the event is a refetch event, we should update the session and not broadcast the event.
|
|
243
|
+
* Refetch events can be triggered when a broadcast message is sent from another
|
|
240
244
|
* tab/window. (see below)
|
|
241
245
|
*/
|
|
242
|
-
if (event === '
|
|
246
|
+
if (event === 'refetch') {
|
|
243
247
|
__AUTH._lastSync = now();
|
|
244
248
|
__AUTH._session = await getSession({
|
|
245
249
|
broadcast: false,
|
|
@@ -254,7 +258,7 @@ export function SessionProvider(props) {
|
|
|
254
258
|
!event ||
|
|
255
259
|
// If the client doesn't have a session then we don't need to call
|
|
256
260
|
// the server to check if it does (if they have signed in via another
|
|
257
|
-
// tab or window that will come through as a "
|
|
261
|
+
// tab or window that will come through as a "refetch" event anyway)
|
|
258
262
|
__AUTH._session === null ||
|
|
259
263
|
// Bail out early if the client session is not stale yet
|
|
260
264
|
now() < __AUTH._lastSync) {
|
|
@@ -281,21 +285,39 @@ export function SessionProvider(props) {
|
|
|
281
285
|
__AUTH._lastSync = 0;
|
|
282
286
|
__AUTH._session = undefined;
|
|
283
287
|
__AUTH._getSession = () => { };
|
|
288
|
+
__AUTH._setSession = () => { };
|
|
284
289
|
};
|
|
285
290
|
}, []);
|
|
286
291
|
React.useEffect(() => {
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
+
const handleBroadcastMessage = (event) => {
|
|
293
|
+
try {
|
|
294
|
+
const message = event.data;
|
|
295
|
+
// Only handle session-related broadcast messages
|
|
296
|
+
if (message.event !== 'session')
|
|
297
|
+
return;
|
|
298
|
+
// Handle logout events by updating the session directly
|
|
299
|
+
if (message.data?.trigger === 'logout') {
|
|
300
|
+
__AUTH._getSession({ event: 'logout' });
|
|
301
|
+
}
|
|
302
|
+
// Handle refetch events by updating the session directly
|
|
303
|
+
if (message.data?.trigger === 'refetch') {
|
|
304
|
+
__AUTH._getSession({ event: 'refetch' });
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
catch (error) {
|
|
308
|
+
// Ignore invalid JSON or malformed messages
|
|
309
|
+
console.warn('Failed to parse broadcast message:', error);
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
// Listen for broadcast events fired from another tab/window
|
|
313
|
+
// refetch/logout the current session data depending on the event
|
|
292
314
|
// Note: We could pass session data through and do something like
|
|
293
315
|
// `setData(message.data)` but that can cause problems depending
|
|
294
316
|
// on how the session object is being used in the client; it is
|
|
295
317
|
// more robust to have each window/tab fetch it's own copy of the
|
|
296
318
|
// session object rather than share it across instances.
|
|
297
|
-
broadcast().addEventListener('message',
|
|
298
|
-
return () => broadcast().removeEventListener('message',
|
|
319
|
+
broadcast().addEventListener('message', handleBroadcastMessage);
|
|
320
|
+
return () => broadcast().removeEventListener('message', handleBroadcastMessage);
|
|
299
321
|
}, []);
|
|
300
322
|
React.useEffect(() => {
|
|
301
323
|
const { refetchOnWindowFocus = true } = props;
|
|
@@ -335,7 +357,7 @@ export function SessionProvider(props) {
|
|
|
335
357
|
setSession(newSession);
|
|
336
358
|
broadcast().postMessage({
|
|
337
359
|
event: 'session',
|
|
338
|
-
data: { trigger: '
|
|
360
|
+
data: { trigger: 'refetch' },
|
|
339
361
|
});
|
|
340
362
|
}
|
|
341
363
|
return newSession;
|
package/dist/auth/trpc.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trpc.d.ts","sourceRoot":"","sources":["../../src/auth/trpc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAM5C;;GAEG;AACH,eAAO,MAAM,gBAAgB,QAAO,QAAQ,CAAC,aAAa,CAAC,GAAG,
|
|
1
|
+
{"version":3,"file":"trpc.d.ts","sourceRoot":"","sources":["../../src/auth/trpc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAM5C;;GAEG;AACH,eAAO,MAAM,gBAAgB,QAAO,QAAQ,CAAC,aAAa,CAAC,GAAG,CA6E7D,CAAA"}
|
package/dist/auth/trpc.js
CHANGED
|
@@ -25,6 +25,7 @@ export const refreshTokenLink = () => {
|
|
|
25
25
|
refreshPromise = (async () => {
|
|
26
26
|
try {
|
|
27
27
|
const response = await fetch('/api/auth/refresh');
|
|
28
|
+
const data = await response.json();
|
|
28
29
|
if (response.status !== 200) {
|
|
29
30
|
await logout({
|
|
30
31
|
/**
|
|
@@ -34,7 +35,7 @@ export const refreshTokenLink = () => {
|
|
|
34
35
|
});
|
|
35
36
|
return;
|
|
36
37
|
}
|
|
37
|
-
await refreshSession();
|
|
38
|
+
await refreshSession(data.session);
|
|
38
39
|
}
|
|
39
40
|
catch (e) {
|
|
40
41
|
await logout({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nextjs-cms",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.56",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -142,8 +142,8 @@
|
|
|
142
142
|
"tsx": "^4.20.6",
|
|
143
143
|
"typescript": "^5.9.2",
|
|
144
144
|
"@lzcms/eslint-config": "0.3.0",
|
|
145
|
-
"@lzcms/
|
|
146
|
-
"@lzcms/
|
|
145
|
+
"@lzcms/tsconfig": "0.1.0",
|
|
146
|
+
"@lzcms/prettier-config": "0.1.0"
|
|
147
147
|
},
|
|
148
148
|
"license": "MIT",
|
|
149
149
|
"keywords": [
|