feat: enhance security by implementing HttpOnly refresh tokens and in-memory access token management

- Added isomorphic-dompurify for improved XSS protection
- Refactored token storage to use in-memory management for access tokens
- Implemented server-side route handlers for OAuth token management
- Introduced security headers in next.config.js
- Removed client-side exposure of client_secret and sensitive tokens
- Updated API interceptors to handle token refresh logic
- Cleaned up cookie management for refresh tokens
This commit is contained in:
hamed
2026-06-20 13:10:17 +03:30
parent a19058d9a2
commit 194ffd889c
29 changed files with 1007 additions and 190 deletions
+5 -4
View File
@@ -8,6 +8,7 @@ import { redirect } from "next/navigation";
import { fetchReq } from "@/lib/req";
import { safeJsonParse } from "@/lib/sanitize";
import { buildPatientUser } from "@/lib/representationAdapters";
import { getServerAccessToken } from "@/lib/serverToken";
export default async function Dashboard({ searchParams }) {
const awaitedSearchParams = await searchParams;
@@ -16,9 +17,9 @@ export default async function Dashboard({ searchParams }) {
const ability = defineAbilitiesFor(user);
const cookieStore = await cookies();
const token = cookieStore.get("access_token");
const token = await getServerAccessToken();
if (!ability.can("access", "Dashboard")) {
if (!ability.can("access", "Dashboard") || !token) {
removeToken();
return redirect("/login");
}
@@ -27,7 +28,7 @@ export default async function Dashboard({ searchParams }) {
// may still carry the OTP uuid, which 404s against user-profile.
const userInfo = safeJsonParse(cookieStore.get("userInfo")?.value);
const userUuid = userInfo?.uuid || cookieStore.get("uuid")?.value;
const authHeader = { headers: { Authorization: `Bearer ${token.value}` } };
const authHeader = { headers: { Authorization: `Bearer ${token}` } };
const API = process.env.NEXT_PUBLIC_API_URL;
const [profile, appointmentsRes, paymentsRes] = await Promise.all([
@@ -54,7 +55,7 @@ export default async function Dashboard({ searchParams }) {
return (
<Content
logged={token.value}
logged={true}
params={awaitedSearchParams}
matchedCity={matchedCity}
user={buildPatientUser(profile, extras)}