enzostvs HF Staff commited on
Commit
c2bb873
·
1 Parent(s): 17234c8
app/api/auth/logout/route.ts ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { NextResponse } from "next/server";
2
+ import MY_TOKEN_KEY from "@/lib/get-cookie-name";
3
+
4
+ export async function POST() {
5
+ const cookieName = MY_TOKEN_KEY();
6
+ const isProduction = process.env.NODE_ENV === "production";
7
+
8
+ const response = NextResponse.json(
9
+ { message: "Logged out successfully" },
10
+ { status: 200 }
11
+ );
12
+
13
+ // Clear the HTTP-only cookie
14
+ const cookieOptions = [
15
+ `${cookieName}=`,
16
+ "Max-Age=0",
17
+ "Path=/",
18
+ "HttpOnly",
19
+ ...(isProduction ? ["Secure", "SameSite=None"] : ["SameSite=Lax"])
20
+ ].join("; ");
21
+
22
+ response.headers.set("Set-Cookie", cookieOptions);
23
+
24
+ return response;
25
+ }
app/api/auth/route.ts CHANGED
@@ -1,4 +1,5 @@
1
  import { NextRequest, NextResponse } from "next/server";
 
2
 
3
  export async function POST(req: NextRequest) {
4
  const body = await req.json();
@@ -70,11 +71,17 @@ export async function POST(req: NextRequest) {
70
  }
71
  const user = await userResponse.json();
72
 
73
- return NextResponse.json(
 
 
 
 
74
  {
75
  access_token: response.access_token,
76
  expires_in: response.expires_in,
77
  user,
 
 
78
  },
79
  {
80
  status: 200,
@@ -83,4 +90,17 @@ export async function POST(req: NextRequest) {
83
  },
84
  }
85
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  }
 
1
  import { NextRequest, NextResponse } from "next/server";
2
+ import MY_TOKEN_KEY from "@/lib/get-cookie-name";
3
 
4
  export async function POST(req: NextRequest) {
5
  const body = await req.json();
 
71
  }
72
  const user = await userResponse.json();
73
 
74
+ const cookieName = MY_TOKEN_KEY();
75
+ const isProduction = process.env.NODE_ENV === "production";
76
+
77
+ // Create response with user data
78
+ const nextResponse = NextResponse.json(
79
  {
80
  access_token: response.access_token,
81
  expires_in: response.expires_in,
82
  user,
83
+ // Include fallback flag for iframe contexts
84
+ useLocalStorageFallback: true,
85
  },
86
  {
87
  status: 200,
 
90
  },
91
  }
92
  );
93
+
94
+ // Set HTTP-only cookie with proper attributes for iframe support
95
+ const cookieOptions = [
96
+ `${cookieName}=${response.access_token}`,
97
+ `Max-Age=${response.expires_in || 3600}`, // Default 1 hour if not provided
98
+ "Path=/",
99
+ "HttpOnly",
100
+ ...(isProduction ? ["Secure", "SameSite=None"] : ["SameSite=Lax"])
101
+ ].join("; ");
102
+
103
+ nextResponse.headers.set("Set-Cookie", cookieOptions);
104
+
105
+ return nextResponse;
106
  }
hooks/useUser.ts CHANGED
@@ -5,26 +5,36 @@ import { useCookie } from "react-use";
5
  import { useRouter } from "next/navigation";
6
 
7
  import { User } from "@/types";
8
- import MY_TOKEN_KEY from "@/lib/get-cookie-name";
9
  import { api } from "@/lib/api";
10
  import { toast } from "sonner";
 
 
 
 
 
 
11
 
12
 
13
  export const useUser = (initialData?: {
14
  user: User | null;
15
  errCode: number | null;
16
  }) => {
17
- const cookie_name = MY_TOKEN_KEY();
18
  const client = useQueryClient();
19
  const router = useRouter();
20
- const [, setCookie, removeCookie] = useCookie(cookie_name);
21
  const [currentRoute, setCurrentRoute, removeCurrentRoute] = useCookie("deepsite-currentRoute");
22
 
23
  const { data: { user, errCode } = { user: null, errCode: null }, isLoading } =
24
  useQuery({
25
  queryKey: ["user.me"],
26
  queryFn: async () => {
27
- return { user: initialData?.user, errCode: initialData?.errCode };
 
 
 
 
 
 
 
28
  },
29
  refetchOnWindowFocus: false,
30
  refetchOnReconnect: false,
@@ -59,7 +69,12 @@ export const useUser = (initialData?: {
59
  .post("/auth", { code })
60
  .then(async (res: any) => {
61
  if (res.data) {
62
- setCookie(res.data.access_token);
 
 
 
 
 
63
  client.setQueryData(["user.me"], {
64
  user: res.data.user,
65
  errCode: null,
@@ -82,12 +97,27 @@ export const useUser = (initialData?: {
82
  };
83
 
84
  const logout = async () => {
85
- removeCookie();
86
- removeCurrentRoute();
87
- router.push("/");
88
- toast.success("Logout successful");
89
- client.invalidateQueries({ queryKey: ["user.me"] });
90
- window.location.reload();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  };
92
 
93
  return {
 
5
  import { useRouter } from "next/navigation";
6
 
7
  import { User } from "@/types";
 
8
  import { api } from "@/lib/api";
9
  import { toast } from "sonner";
10
+ import {
11
+ storeAuthDataFallback,
12
+ getAuthDataFallback,
13
+ clearAuthDataFallback,
14
+ isInIframe
15
+ } from "@/lib/iframe-storage";
16
 
17
 
18
  export const useUser = (initialData?: {
19
  user: User | null;
20
  errCode: number | null;
21
  }) => {
 
22
  const client = useQueryClient();
23
  const router = useRouter();
 
24
  const [currentRoute, setCurrentRoute, removeCurrentRoute] = useCookie("deepsite-currentRoute");
25
 
26
  const { data: { user, errCode } = { user: null, errCode: null }, isLoading } =
27
  useQuery({
28
  queryKey: ["user.me"],
29
  queryFn: async () => {
30
+ // Check for fallback data if no initial data provided and we're in iframe
31
+ if (!initialData && isInIframe()) {
32
+ const fallbackData = getAuthDataFallback();
33
+ if (fallbackData.user && fallbackData.token) {
34
+ return { user: fallbackData.user, errCode: null };
35
+ }
36
+ }
37
+ return { user: initialData?.user || null, errCode: initialData?.errCode || null };
38
  },
39
  refetchOnWindowFocus: false,
40
  refetchOnReconnect: false,
 
69
  .post("/auth", { code })
70
  .then(async (res: any) => {
71
  if (res.data) {
72
+ // Cookie is now set server-side with proper iframe attributes
73
+ // Also store fallback data for iframe contexts
74
+ if (res.data.useLocalStorageFallback) {
75
+ storeAuthDataFallback(res.data.access_token, res.data.user);
76
+ }
77
+
78
  client.setQueryData(["user.me"], {
79
  user: res.data.user,
80
  errCode: null,
 
97
  };
98
 
99
  const logout = async () => {
100
+ try {
101
+ // Call server endpoint to clear the HTTP-only cookie
102
+ await api.post("/auth/logout");
103
+ // Clear fallback storage
104
+ clearAuthDataFallback();
105
+ removeCurrentRoute();
106
+ client.setQueryData(["user.me"], { user: null, errCode: null });
107
+ router.push("/");
108
+ toast.success("Logout successful");
109
+ client.invalidateQueries({ queryKey: ["user.me"] });
110
+ window.location.reload();
111
+ } catch (error) {
112
+ console.error("Logout error:", error);
113
+ // Even if server call fails, clear client state
114
+ clearAuthDataFallback();
115
+ removeCurrentRoute();
116
+ client.setQueryData(["user.me"], { user: null, errCode: null });
117
+ router.push("/");
118
+ toast.success("Logout successful");
119
+ window.location.reload();
120
+ }
121
  };
122
 
123
  return {
lib/api.ts CHANGED
@@ -14,7 +14,6 @@ export const apiServer = axios.create({
14
  cache: "no-store",
15
  },
16
  });
17
-
18
  api.interceptors.request.use(
19
  async (config) => {
20
  // get the token from cookies
@@ -33,3 +32,4 @@ api.interceptors.request.use(
33
  return Promise.reject(error);
34
  }
35
  );
 
 
14
  cache: "no-store",
15
  },
16
  });
 
17
  api.interceptors.request.use(
18
  async (config) => {
19
  // get the token from cookies
 
32
  return Promise.reject(error);
33
  }
34
  );
35
+
lib/iframe-storage.ts ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Utility for handling storage in iframe contexts
3
+ * Falls back to localStorage when cookies are blocked
4
+ */
5
+
6
+ export const isInIframe = (): boolean => {
7
+ try {
8
+ return window.self !== window.top;
9
+ } catch {
10
+ return true; // If we can't access window.top, we're likely in an iframe
11
+ }
12
+ };
13
+
14
+ export const STORAGE_KEYS = {
15
+ ACCESS_TOKEN: "deepsite-auth-token-fallback",
16
+ USER_DATA: "deepsite-user-data-fallback",
17
+ } as const;
18
+
19
+ export const iframeStorage = {
20
+ setItem: (key: string, value: string): void => {
21
+ try {
22
+ localStorage.setItem(key, value);
23
+ } catch (error) {
24
+ console.warn("Failed to set localStorage item:", error);
25
+ }
26
+ },
27
+
28
+ getItem: (key: string): string | null => {
29
+ try {
30
+ return localStorage.getItem(key);
31
+ } catch (error) {
32
+ console.warn("Failed to get localStorage item:", error);
33
+ return null;
34
+ }
35
+ },
36
+
37
+ removeItem: (key: string): void => {
38
+ try {
39
+ localStorage.removeItem(key);
40
+ } catch (error) {
41
+ console.warn("Failed to remove localStorage item:", error);
42
+ }
43
+ },
44
+
45
+ clear: (): void => {
46
+ try {
47
+ localStorage.removeItem(STORAGE_KEYS.ACCESS_TOKEN);
48
+ localStorage.removeItem(STORAGE_KEYS.USER_DATA);
49
+ } catch (error) {
50
+ console.warn("Failed to clear localStorage items:", error);
51
+ }
52
+ },
53
+ };
54
+
55
+ export const storeAuthDataFallback = (accessToken: string, userData: any): void => {
56
+ if (isInIframe()) {
57
+ iframeStorage.setItem(STORAGE_KEYS.ACCESS_TOKEN, accessToken);
58
+ iframeStorage.setItem(STORAGE_KEYS.USER_DATA, JSON.stringify(userData));
59
+ }
60
+ };
61
+
62
+ export const getAuthDataFallback = (): { token: string | null; user: any | null } => {
63
+ if (isInIframe()) {
64
+ const token = iframeStorage.getItem(STORAGE_KEYS.ACCESS_TOKEN);
65
+ const userDataStr = iframeStorage.getItem(STORAGE_KEYS.USER_DATA);
66
+ const user = userDataStr ? JSON.parse(userDataStr) : null;
67
+ return { token, user };
68
+ }
69
+ return { token: null, user: null };
70
+ };
71
+
72
+ export const clearAuthDataFallback = (): void => {
73
+ if (isInIframe()) {
74
+ iframeStorage.clear();
75
+ }
76
+ };