Skip to content

Commit bdfa5b3

Browse files
committed
fix(frontend): prevent infinite loop of setting cookies
1 parent 22abe84 commit bdfa5b3

File tree

6 files changed

+72
-118
lines changed

6 files changed

+72
-118
lines changed

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/backend/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "ryot"
3-
version = "4.3.10"
3+
version = "4.3.11"
44
edition = "2021"
55
repository = "https://github.com/IgnisDa/ryot"
66
license = "GPL-3.0"

apps/frontend/app/lib/utilities.server.ts

+55-33
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@ import {
1313
} from "@remix-run/node";
1414
import {
1515
type CoreDetails,
16+
CoreDetailsDocument,
1617
CoreEnabledFeaturesDocument,
1718
GetPresignedS3UrlDocument,
1819
PresignedPutS3UrlDocument,
20+
UserCollectionsListDocument,
1921
type UserCollectionsListQuery,
2022
type UserLot,
2123
type UserPreferences,
24+
UserPreferencesDocument,
2225
} from "@ryot/generated/graphql/backend/graphql";
2326
import { UserDetailsDocument } from "@ryot/generated/graphql/backend/graphql";
2427
import { GraphQLClient } from "graphql-request";
@@ -40,8 +43,14 @@ const getAuthorizationCookie = async (request: Request) => {
4043
return cookie;
4144
};
4245

43-
export const getAuthorizationHeader = async (request: Request) => {
44-
const cookie = await getAuthorizationCookie(request);
46+
export const getAuthorizationHeader = async (
47+
request?: Request,
48+
token?: string,
49+
) => {
50+
let cookie: string;
51+
if (request) cookie = await getAuthorizationCookie(request);
52+
else if (token) cookie = token;
53+
else cookie = "";
4554
return { Authorization: `Bearer ${cookie}` };
4655
};
4756

@@ -147,6 +156,15 @@ export const processSubmission = <Schema extends ZodTypeAny>(
147156
return submission.value;
148157
};
149158

159+
export const getUserCollectionsList = async (request: Request) => {
160+
const { userCollectionsList } = await gqlClient.request(
161+
UserCollectionsListDocument,
162+
{},
163+
await getAuthorizationHeader(request),
164+
);
165+
return userCollectionsList;
166+
};
167+
150168
export const getLogoutCookies = async () => {
151169
return combineHeaders(
152170
{
@@ -169,11 +187,6 @@ export const getLogoutCookies = async () => {
169187
expires: new Date(0),
170188
}),
171189
},
172-
{
173-
"Set-Cookie": await userCollectionsListCookie.serialize("", {
174-
expires: new Date(0),
175-
}),
176-
},
177190
);
178191
};
179192

@@ -256,7 +269,6 @@ export const getCoreDetails = async (request: Request) => {
256269
const details = await coreDetailsCookie.parse(
257270
request.headers.get("cookie") || "",
258271
);
259-
redirectIfDetailNotPresent(request, details);
260272
return details as CoreDetails;
261273
};
262274

@@ -265,7 +277,6 @@ export const getUserPreferences = async (request: Request) => {
265277
const prefs = await userPreferencesCookie.parse(
266278
request.headers.get("cookie") || "",
267279
);
268-
redirectIfDetailNotPresent(request, prefs);
269280
return prefs as UserPreferences;
270281
};
271282

@@ -274,28 +285,9 @@ export const getUserDetails = async (request: Request) => {
274285
const details = await userDetailsCookie.parse(
275286
request.headers.get("cookie") || "",
276287
);
277-
redirectIfDetailNotPresent(request, details);
278288
return details as ApplicationUser;
279289
};
280290

281-
export const getUserCollectionsList = async (request: Request) => {
282-
await redirectIfNotAuthenticated(request);
283-
const list = await userCollectionsListCookie.parse(
284-
request.headers.get("cookie") || "",
285-
);
286-
redirectIfDetailNotPresent(request, list);
287-
return list as UserCollectionsListQuery["userCollectionsList"];
288-
};
289-
290-
const redirectIfDetailNotPresent = (request: Request, detail: unknown) => {
291-
if (!detail)
292-
throw redirect(
293-
withQuery($path("/actions"), {
294-
[redirectToQueryParam]: withoutHost(request.url),
295-
}),
296-
);
297-
};
298-
299291
const envVariables = expectedEnvironmentVariables.parse(process.env);
300292

301293
const commonCookieOptions = {
@@ -329,11 +321,6 @@ export const userDetailsCookie = createCookie(
329321
commonCookieOptions,
330322
);
331323

332-
export const userCollectionsListCookie = createCookie(
333-
ApplicationKey.UserCollectionsList,
334-
commonCookieOptions,
335-
);
336-
337324
export const toastSessionStorage = createCookieSessionStorage({
338325
cookie: { ...commonCookieOptions, name: ApplicationKey.Toast },
339326
});
@@ -392,3 +379,38 @@ export async function getToast(request: Request) {
392379
: null,
393380
};
394381
}
382+
383+
export const getCookiesForApplication = async (token: string) => {
384+
const [{ coreDetails }, { userPreferences }, { userDetails }] =
385+
await Promise.all([
386+
gqlClient.request(CoreDetailsDocument),
387+
gqlClient.request(
388+
UserPreferencesDocument,
389+
undefined,
390+
await getAuthorizationHeader(undefined, token),
391+
),
392+
gqlClient.request(
393+
UserDetailsDocument,
394+
undefined,
395+
await getAuthorizationHeader(undefined, token),
396+
),
397+
]);
398+
const cookieMaxAge = coreDetails.tokenValidForDays * 24 * 60 * 60;
399+
return combineHeaders(
400+
{
401+
"Set-Cookie": await coreDetailsCookie.serialize(coreDetails, {
402+
maxAge: cookieMaxAge,
403+
}),
404+
},
405+
{
406+
"Set-Cookie": await userPreferencesCookie.serialize(userPreferences, {
407+
maxAge: cookieMaxAge,
408+
}),
409+
},
410+
{
411+
"Set-Cookie": await userDetailsCookie.serialize(userDetails, {
412+
maxAge: cookieMaxAge,
413+
}),
414+
},
415+
);
416+
};

apps/frontend/app/routes/_dashboard.settings.preferences.tsx

+5-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { DragDropContext, Draggable, Droppable } from "@hello-pangea/dnd";
2-
import { $path } from "@ignisda/remix-routes";
32
import {
43
ActionIcon,
54
Affix,
@@ -49,12 +48,12 @@ import {
4948
import clsx from "clsx";
5049
import { Fragment, useState } from "react";
5150
import { match } from "ts-pattern";
52-
import { withQuery, withoutHost } from "ufo";
5351
import { z } from "zod";
5452
import { zx } from "zodix";
55-
import { redirectToQueryParam } from "~/lib/generals";
5653
import {
54+
authCookie,
5755
getAuthorizationHeader,
56+
getCookiesForApplication,
5857
getUserDetails,
5958
getUserPreferences,
6059
gqlClient,
@@ -109,11 +108,9 @@ export const action = async ({ request }: ActionFunctionArgs) => {
109108
await getAuthorizationHeader(request),
110109
);
111110
}
112-
return redirect(
113-
withQuery($path("/actions"), {
114-
[redirectToQueryParam]: withoutHost(request.url),
115-
}),
116-
);
111+
const token = await authCookie.parse(request.headers.get("Cookie"));
112+
const headers = await getCookiesForApplication(token);
113+
return json({}, { headers });
117114
};
118115

119116
export default function Page() {

apps/frontend/app/routes/actions.tsx

+1-69
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { $path } from "@ignisda/remix-routes";
22
import {
33
type ActionFunctionArgs,
4-
type LoaderFunctionArgs,
54
json,
65
redirect,
76
unstable_parseMultipartFormData,
@@ -10,7 +9,6 @@ import {
109
AddEntityToCollectionDocument,
1110
CommitMetadataDocument,
1211
CommitPersonDocument,
13-
CoreDetailsDocument,
1412
CreateMediaReminderDocument,
1513
CreateReviewCommentDocument,
1614
DeleteMediaReminderDocument,
@@ -22,12 +20,8 @@ import {
2220
PostReviewDocument,
2321
RemoveEntityFromCollectionDocument,
2422
ToggleMediaMonitorDocument,
25-
UserCollectionsListDocument,
26-
UserDetailsDocument,
27-
UserPreferencesDocument,
2823
Visibility,
2924
} from "@ryot/generated/graphql/backend/graphql";
30-
import { safeRedirect } from "remix-utils/safe-redirect";
3125
import invariant from "tiny-invariant";
3226
import { match } from "ts-pattern";
3327
import { z } from "zod";
@@ -36,77 +30,15 @@ import { redirectToQueryParam } from "~/lib/generals";
3630
import {
3731
MetadataSpecificsSchema,
3832
colorSchemeCookie,
39-
combineHeaders,
40-
coreDetailsCookie,
4133
createToastHeaders,
4234
getAuthorizationHeader,
4335
getLogoutCookies,
4436
gqlClient,
4537
processSubmission,
46-
redirectIfNotAuthenticated,
4738
s3FileUploader,
48-
userCollectionsListCookie,
49-
userDetailsCookie,
50-
userPreferencesCookie,
5139
} from "~/lib/utilities.server";
5240

53-
export const loader = async ({ request }: LoaderFunctionArgs) => {
54-
await redirectIfNotAuthenticated(request);
55-
const url = new URL(request.url);
56-
const [
57-
{ coreDetails },
58-
{ userPreferences },
59-
{ userDetails },
60-
{ userCollectionsList },
61-
] = await Promise.all([
62-
gqlClient.request(CoreDetailsDocument),
63-
gqlClient.request(
64-
UserPreferencesDocument,
65-
undefined,
66-
await getAuthorizationHeader(request),
67-
),
68-
gqlClient.request(
69-
UserDetailsDocument,
70-
undefined,
71-
await getAuthorizationHeader(request),
72-
),
73-
gqlClient.request(
74-
UserCollectionsListDocument,
75-
{},
76-
await getAuthorizationHeader(request),
77-
),
78-
]);
79-
const cookieMaxAge = coreDetails.tokenValidForDays * 24 * 60 * 60;
80-
let redirectUrl = safeRedirect(
81-
url.searchParams.get(redirectToQueryParam) || "/",
82-
);
83-
if (redirectUrl.includes("actions")) redirectUrl = "/";
84-
return redirect(redirectUrl, {
85-
headers: combineHeaders(
86-
{
87-
"Set-Cookie": await coreDetailsCookie.serialize(coreDetails, {
88-
maxAge: cookieMaxAge,
89-
}),
90-
},
91-
{
92-
"Set-Cookie": await userPreferencesCookie.serialize(userPreferences, {
93-
maxAge: cookieMaxAge,
94-
}),
95-
},
96-
{
97-
"Set-Cookie": await userDetailsCookie.serialize(userDetails, {
98-
maxAge: cookieMaxAge,
99-
}),
100-
},
101-
{
102-
"Set-Cookie": await userCollectionsListCookie.serialize(
103-
userCollectionsList,
104-
{ maxAge: cookieMaxAge },
105-
),
106-
},
107-
),
108-
});
109-
};
41+
export const loader = async () => redirect($path("/"));
11042

11143
export const action = async ({ request }: ActionFunctionArgs) => {
11244
const formData = await request.clone().formData();

apps/frontend/app/routes/auth.login.tsx

+9-6
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ import { z } from "zod";
2020
import { redirectToQueryParam } from "~/lib/generals";
2121
import {
2222
authCookie,
23+
combineHeaders,
2324
createToastHeaders,
25+
getCookiesForApplication,
2426
getCoreEnabledFeatures,
2527
getIsAuthenticated,
2628
gqlClient,
@@ -57,16 +59,17 @@ export const action = async ({ request }: ActionFunctionArgs) => {
5759
let redirectUrl = $path("/");
5860
if (submission[redirectToQueryParam])
5961
redirectUrl = safeRedirect(submission[redirectToQueryParam]);
60-
return redirect(
61-
$path("/actions", { [redirectToQueryParam]: redirectUrl }),
62-
{
63-
headers: {
62+
const cookies = await getCookiesForApplication(loginUser.apiKey);
63+
return redirect(redirectUrl, {
64+
headers: combineHeaders(
65+
{
6466
"Set-Cookie": await authCookie.serialize(loginUser.apiKey, {
6567
maxAge: coreDetails.tokenValidForDays * 24 * 60 * 60,
6668
}),
6769
},
68-
},
69-
);
70+
cookies,
71+
),
72+
});
7073
}
7174
const message = match(loginUser.error)
7275
.with(

0 commit comments

Comments
 (0)