fix(R61): 增加 session hint cookie 消除 bootstrap refresh 401
这个提交包含在:
@@ -58,6 +58,17 @@ function isWindowAvailable() {
|
||||
return typeof window !== "undefined";
|
||||
}
|
||||
|
||||
function hasSessionHint() {
|
||||
return typeof document !== "undefined" && document.cookie.includes("has_session=1");
|
||||
}
|
||||
|
||||
function clearSessionHint() {
|
||||
if (typeof document === "undefined") {
|
||||
return;
|
||||
}
|
||||
document.cookie = "has_session=; path=/; max-age=0";
|
||||
}
|
||||
|
||||
function readMustChangePasswordFromStorage() {
|
||||
return isWindowAvailable() && window.localStorage.getItem(MUST_CHANGE_PASSWORD_STORAGE_KEY) === "1";
|
||||
}
|
||||
@@ -168,7 +179,7 @@ export function AuthProvider({ children }: PropsWithChildren) {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const clearSession = useCallback(() => {
|
||||
const markUnauthenticatedReady = useCallback(() => {
|
||||
sessionGenerationRef.current += 1;
|
||||
bootstrapPromiseRef.current = null;
|
||||
refreshRequestRef.current = null;
|
||||
@@ -189,6 +200,12 @@ export function AuthProvider({ children }: PropsWithChildren) {
|
||||
}));
|
||||
}, [clearProactiveRefreshTimer]);
|
||||
|
||||
const clearSession = useCallback(() => {
|
||||
clearSessionHint();
|
||||
markUnauthenticatedReady();
|
||||
void clearPwaRuntimeCaches();
|
||||
}, [markUnauthenticatedReady]);
|
||||
|
||||
const commitAuthenticatedUser = useCallback(
|
||||
(accessToken: string, me: MeResponse, mustChangePassword: boolean) => {
|
||||
const permissions = resolvePermissionsForRoles(me.roleCodes ?? []);
|
||||
@@ -301,6 +318,11 @@ export function AuthProvider({ children }: PropsWithChildren) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasSessionHint() && (!isWindowAvailable() || window.navigator.onLine !== false)) {
|
||||
markUnauthenticatedReady();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bootstrapPromiseRef.current) {
|
||||
bootstrapPromiseRef.current = (async () => {
|
||||
try {
|
||||
@@ -323,7 +345,7 @@ export function AuthProvider({ children }: PropsWithChildren) {
|
||||
}
|
||||
|
||||
await bootstrapPromiseRef.current;
|
||||
}, [activateOfflineSnapshot, clearSession, runRefresh, scheduleProactiveRefresh]);
|
||||
}, [activateOfflineSnapshot, clearSession, markUnauthenticatedReady, runRefresh, scheduleProactiveRefresh]);
|
||||
|
||||
const login = useCallback(
|
||||
async (identifier: string, password: string) => {
|
||||
|
||||
@@ -118,6 +118,16 @@ function refreshCookieOptions(config) {
|
||||
};
|
||||
}
|
||||
|
||||
function sessionHintCookieOptions(config) {
|
||||
return {
|
||||
httpOnly: false,
|
||||
sameSite: "strict",
|
||||
secure: config.cookieSecure,
|
||||
path: "/",
|
||||
maxAge: config.refreshTtlSeconds,
|
||||
};
|
||||
}
|
||||
|
||||
function oidcCookieOptions(config) {
|
||||
return {
|
||||
httpOnly: true,
|
||||
@@ -420,6 +430,7 @@ async function authRoutes(fastify) {
|
||||
refreshToken,
|
||||
refreshCookieOptions(fastify.config),
|
||||
);
|
||||
reply.setCookie("has_session", "1", sessionHintCookieOptions(fastify.config));
|
||||
|
||||
const accessToken = fastify.issueAccessToken(employee);
|
||||
return sendData(reply, request, {
|
||||
@@ -540,6 +551,7 @@ async function authRoutes(fastify) {
|
||||
}
|
||||
|
||||
reply.clearCookie(fastify.config.refreshCookieName, refreshCookieOptions(fastify.config));
|
||||
reply.clearCookie("has_session", sessionHintCookieOptions(fastify.config));
|
||||
throw new AppError({
|
||||
code: "AUTH_005",
|
||||
message: "Refresh Token 无效/已撤销",
|
||||
@@ -607,6 +619,7 @@ async function authRoutes(fastify) {
|
||||
newRefreshToken,
|
||||
refreshCookieOptions(fastify.config),
|
||||
);
|
||||
reply.setCookie("has_session", "1", sessionHintCookieOptions(fastify.config));
|
||||
|
||||
const accessToken = fastify.issueAccessToken(employee);
|
||||
return sendData(reply, request, {
|
||||
@@ -857,6 +870,7 @@ async function authRoutes(fastify) {
|
||||
refreshToken,
|
||||
refreshCookieOptions(fastify.config),
|
||||
);
|
||||
reply.setCookie("has_session", "1", sessionHintCookieOptions(fastify.config));
|
||||
reply.clearCookie(oidcCookieName, oidcCookieOptions(fastify.config));
|
||||
|
||||
const origin = resolvePrimaryAppOrigin(fastify.config, request);
|
||||
@@ -894,6 +908,7 @@ async function authRoutes(fastify) {
|
||||
}
|
||||
|
||||
reply.clearCookie(fastify.config.refreshCookieName, refreshCookieOptions(fastify.config));
|
||||
reply.clearCookie("has_session", sessionHintCookieOptions(fastify.config));
|
||||
return sendData(reply, request, {});
|
||||
});
|
||||
|
||||
@@ -1019,6 +1034,7 @@ async function authRoutes(fastify) {
|
||||
refreshToken,
|
||||
refreshCookieOptions(fastify.config),
|
||||
);
|
||||
reply.setCookie("has_session", "1", sessionHintCookieOptions(fastify.config));
|
||||
|
||||
const accessToken = fastify.issueAccessToken(updatedEmployee);
|
||||
return sendData(reply, request, {
|
||||
@@ -1245,6 +1261,7 @@ async function authRoutes(fastify) {
|
||||
}
|
||||
|
||||
reply.clearCookie(fastify.config.refreshCookieName, refreshCookieOptions(fastify.config));
|
||||
reply.clearCookie("has_session", sessionHintCookieOptions(fastify.config));
|
||||
return sendData(reply, request, {});
|
||||
});
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户