mirror of
https://github.com/fosrl/pangolin.git
synced 2026-08-24 13:10:15 +02:00
use proxy for logout on org auth page session expire
This commit is contained in:
@@ -16,18 +16,26 @@ export async function logout(
|
|||||||
next: NextFunction
|
next: NextFunction
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
const { user, session } = await verifySession(req);
|
const { user, session } = await verifySession(req);
|
||||||
|
const isSecure = req.protocol === "https";
|
||||||
|
|
||||||
|
// Always clear the session cookie so logout is idempotent, even when
|
||||||
|
// the session is already missing or invalid
|
||||||
|
res.setHeader("Set-Cookie", createBlankSessionTokenCookie(isSecure));
|
||||||
|
|
||||||
if (!user || !session) {
|
if (!user || !session) {
|
||||||
if (config.getRawConfig().app.log_failed_attempts) {
|
if (config.getRawConfig().app.log_failed_attempts) {
|
||||||
logger.info(
|
logger.info(
|
||||||
`Log out failed because missing or invalid session. IP: ${req.ip}.`
|
`Log out with missing or invalid session. IP: ${req.ip}.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return next(
|
|
||||||
createHttpError(
|
return response<null>(res, {
|
||||||
HttpCode.BAD_REQUEST,
|
data: null,
|
||||||
"You must be logged in to sign out"
|
success: true,
|
||||||
)
|
error: false,
|
||||||
);
|
message: "Logged out successfully",
|
||||||
|
status: HttpCode.OK
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -37,9 +45,6 @@ export async function logout(
|
|||||||
logger.error("Failed to invalidate session", error);
|
logger.error("Failed to invalidate session", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
const isSecure = req.protocol === "https";
|
|
||||||
res.setHeader("Set-Cookie", createBlankSessionTokenCookie(isSecure));
|
|
||||||
|
|
||||||
return response<null>(res, {
|
return response<null>(res, {
|
||||||
data: null,
|
data: null,
|
||||||
success: true,
|
success: true,
|
||||||
|
|||||||
@@ -248,6 +248,39 @@ export async function loginProxy(
|
|||||||
return await makeApiRequest<LoginResponse>(url, "POST", request);
|
return await makeApiRequest<LoginResponse>(url, "POST", request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function logoutProxy(): Promise<ResponseT<null>> {
|
||||||
|
const env = pullEnv();
|
||||||
|
const serverPort = process.env.SERVER_EXTERNAL_PORT;
|
||||||
|
const url = `http://localhost:${serverPort}/api/v1/auth/logout`;
|
||||||
|
|
||||||
|
const result = await makeApiRequest<null>(url, "POST");
|
||||||
|
|
||||||
|
try {
|
||||||
|
const headersList = await reqHeaders();
|
||||||
|
const host = headersList.get("host")?.split(":")[0];
|
||||||
|
const allCookies = await cookies();
|
||||||
|
const clearOptions = {
|
||||||
|
httpOnly: true,
|
||||||
|
secure: true,
|
||||||
|
sameSite: "lax" as const,
|
||||||
|
path: "/",
|
||||||
|
maxAge: 0
|
||||||
|
};
|
||||||
|
// Clear both host-only and domain-scoped variants.
|
||||||
|
allCookies.set(env.server.sessionCookieName, "", clearOptions);
|
||||||
|
if (host) {
|
||||||
|
allCookies.set(env.server.sessionCookieName, "", {
|
||||||
|
...clearOptions,
|
||||||
|
domain: host
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (cookieError) {
|
||||||
|
console.error("Failed to clear session cookie:", cookieError);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
export async function securityKeyStartProxy(
|
export async function securityKeyStartProxy(
|
||||||
request: SecurityKeyStartRequest,
|
request: SecurityKeyStartRequest,
|
||||||
forceLogin?: boolean
|
forceLogin?: boolean
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import { Shield, ArrowRight } from "lucide-react";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { createApiClient } from "@app/lib/api";
|
import { useState } from "react";
|
||||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
import { logoutProxy } from "@app/actions/server";
|
||||||
|
|
||||||
type OrgPolicyRequiredProps = {
|
type OrgPolicyRequiredProps = {
|
||||||
orgId: string;
|
orgId: string;
|
||||||
@@ -40,21 +40,23 @@ export default function OrgPolicyRequired({
|
|||||||
}: OrgPolicyRequiredProps) {
|
}: OrgPolicyRequiredProps) {
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
const api = createApiClient(useEnvContext());
|
|
||||||
|
|
||||||
const sessionExpired =
|
const sessionExpired =
|
||||||
policies?.maxSessionLength &&
|
policies?.maxSessionLength &&
|
||||||
policies.maxSessionLength.compliant === false;
|
policies.maxSessionLength.compliant === false;
|
||||||
|
|
||||||
function reauthenticate() {
|
async function reauthenticate() {
|
||||||
api.post("/auth/logout")
|
setLoading(true);
|
||||||
.catch(() => {})
|
try {
|
||||||
.then(() => {
|
await logoutProxy();
|
||||||
const destination = redirectAfterAuth ?? `/${orgId}`;
|
} catch (error) {
|
||||||
router.push(destination);
|
console.error("Error during logout:", error);
|
||||||
router.refresh();
|
} finally {
|
||||||
});
|
const destination = redirectAfterAuth ?? `/${orgId}`;
|
||||||
|
router.push(destination);
|
||||||
|
router.refresh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sessionExpired) {
|
if (sessionExpired) {
|
||||||
@@ -76,6 +78,7 @@ export default function OrgPolicyRequired({
|
|||||||
<Button
|
<Button
|
||||||
className="w-full"
|
className="w-full"
|
||||||
onClick={reauthenticate}
|
onClick={reauthenticate}
|
||||||
|
loading={loading}
|
||||||
>
|
>
|
||||||
{t("reauthenticate")}
|
{t("reauthenticate")}
|
||||||
<ArrowRight className="ml-2 h-4 w-4" />
|
<ArrowRight className="ml-2 h-4 w-4" />
|
||||||
|
|||||||
Reference in New Issue
Block a user