Skip to content

Commit 5d433ac

Browse files
JiriLojdaSimply007
authored andcommitted
Improve auth redirects handling
1 parent cb775bb commit 5d433ac

File tree

8 files changed

+156
-89
lines changed

8 files changed

+156
-89
lines changed

lib/constants/cookies.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
export const envIdCookieName = "currentEnvId";
22

33
export const previewApiKeyCookieName = "currentPreviewApiKey";
4+
5+
export const ignoreMissingApiKeyCookieName = "ignoreMissingApiKey";

middleware.ts

+19-11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { NextRequest, NextResponse } from 'next/server'
22

3-
import { envIdCookieName, previewApiKeyCookieName } from './lib/constants/cookies';
3+
import { envIdCookieName, ignoreMissingApiKeyCookieName, previewApiKeyCookieName } from './lib/constants/cookies';
44
import { createQueryString } from './lib/routing';
55
import { defaultEnvId } from './lib/utils/env';
66

@@ -36,27 +36,34 @@ const handleExplicitProjectRoute = (currentEnvId: string) => (prevResponse: Next
3636
return prevResponse;
3737
}
3838

39+
if (request.nextUrl.pathname.includes("/api/exit-preview") && request.cookies.get(ignoreMissingApiKeyCookieName)) {
40+
return prevResponse;
41+
}
42+
3943
if (routeEnvId === defaultEnvId) {
4044
const res = NextResponse.redirect(new URL(createUrlWithQueryString(remainingUrl, request.nextUrl.searchParams), request.nextUrl.origin));
41-
res.cookies.set(envIdCookieName, routeEnvId, { path: '/', sameSite: 'none', secure: true });
42-
res.cookies.set(previewApiKeyCookieName, '', { path: '/', sameSite: 'none', secure: true });
45+
res.cookies.set(envIdCookieName, routeEnvId, cookieOptions);
46+
res.cookies.set(previewApiKeyCookieName, '', cookieOptions);
4347

4448
return res
4549
}
4650

4751
if (routeEnvId !== currentEnvId || !request.cookies.get(previewApiKeyCookieName)) {
48-
const res = NextResponse.redirect(new URL(`/getPreviewApiKey?path=${encodeURIComponent(createUrlWithQueryString(remainingUrl, request.nextUrl.searchParams.entries()))}`, request.url))
52+
const originalPath = encodeURIComponent(createUrlWithQueryString(remainingUrl, request.nextUrl.searchParams.entries()));
53+
const redirectPath = `/api/exit-preview?callback=${encodeURIComponent(`/getPreviewApiKey?path=${originalPath}`)}`;
54+
const res = NextResponse.redirect(new URL(redirectPath, request.url));
4955

50-
res.cookies.set(envIdCookieName, routeEnvId, { path: '/', sameSite: 'none', secure: true });
51-
res.cookies.set(previewApiKeyCookieName, '', { path: '/', sameSite: 'none', secure: true });
56+
res.cookies.set(envIdCookieName, routeEnvId, cookieOptions);
57+
res.cookies.set(previewApiKeyCookieName, '', cookieOptions);
58+
res.cookies.set(ignoreMissingApiKeyCookieName, "true", cookieOptions);
5259

5360
return res;
5461
}
5562

5663
return NextResponse.redirect(new URL(`${remainingUrl ?? ''}?${createQueryString(Object.fromEntries(request.nextUrl.searchParams.entries()))}`, request.nextUrl.origin));
5764
}
5865

59-
const handleArticlesRoute = (currentEnvId: string) => (prevResponse: NextResponse, request: NextRequest,) => request.nextUrl.pathname === '/articles'
66+
const handleArticlesRoute = (currentEnvId: string) => (prevResponse: NextResponse, request: NextRequest) => request.nextUrl.pathname === '/articles'
6067
? NextResponse.rewrite(new URL(`/${currentEnvId}/articles/category/all/page/1`, request.url))
6168
: prevResponse;
6269

@@ -72,11 +79,10 @@ const handleArticlesCategoryWithNoPaginationRoute = (currentEnvId: string) => (p
7279

7380
const handleEmptyCookies = (prevResponse: NextResponse, request: NextRequest) => {
7481
if (!request.cookies.get(envIdCookieName)?.value) {
75-
prevResponse.cookies.set(envIdCookieName, defaultEnvId, { path: '/', sameSite: 'none', secure: true })
82+
prevResponse.cookies.set(envIdCookieName, defaultEnvId, cookieOptions)
7683
}
7784
if (!request.cookies.get(envIdCookieName)?.value || request.cookies.get(envIdCookieName)?.value === defaultEnvId) {
78-
79-
prevResponse.cookies.set(previewApiKeyCookieName, KONTENT_PREVIEW_API_KEY, { path: '/', sameSite: 'none', secure: true })
85+
prevResponse.cookies.set(previewApiKeyCookieName, KONTENT_PREVIEW_API_KEY, cookieOptions)
8086
}
8187

8288

@@ -101,4 +107,6 @@ export const config = {
101107
'/((?!api|_next/static|_next/image|favicon.png|getPreviewApiKey|logo.png|callback).*)',
102108
'/'
103109
],
104-
}
110+
};
111+
112+
const cookieOptions = { path: '/', sameSite: 'none', secure: true } as const;

package-lock.json

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

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"@vercel/analytics": "^1.0.0",
2121
"auth0-js": "^9.22.1",
2222
"cookies-next": "^4.0.0",
23-
"next": "13.4.19",
23+
"next": "^13.5.3",
2424
"react": "18.2.0",
2525
"react-dom": "18.2.0",
2626
"swiper": "^10.2.0"

pages/api/exit-preview.tsx

+13-10
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
import { NextApiHandler } from "next";
22

3+
import { ignoreMissingApiKeyCookieName } from "../../lib/constants/cookies";
4+
35
const handler: NextApiHandler = (req, res) => {
4-
console.log("Exiting preview");
5-
// Exit the current user from "Preview Mode". This function accepts no args.
6-
res.clearPreviewData();
7-
8-
// Redirect the user back to the index page.
9-
// Might be implemented return URL by the query string.
10-
res.redirect("/");
11-
}
12-
13-
export default handler;
6+
// Exit the current user from "Preview Mode". This function accepts no args.
7+
res.clearPreviewData();
8+
9+
res.setHeader("Set-Cookie", `${ignoreMissingApiKeyCookieName}=; Path=/; SameSite=None; Secure; Max-Age=-1`);
10+
11+
// Redirect the user back to the index page.
12+
// Might be implemented return URL by the query string.
13+
res.redirect(typeof req.query.callback === "string" ? req.query.callback : "/");
14+
}
15+
16+
export default handler;

pages/api/preview.ts

+30-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { NextApiHandler } from "next";
1+
import { NextApiHandler, NextApiResponse } from "next";
22

33
import { envIdCookieName, previewApiKeyCookieName } from "../../lib/constants/cookies";
44
import { ResolutionContext, resolveUrlPath } from "../../lib/routing";
@@ -7,18 +7,23 @@ import { defaultEnvId } from "../../lib/utils/env";
77
const handler: NextApiHandler = async (req, res) => {
88
// TODO move secret to env variables
99
if (req.query.secret !== 'mySuperSecret' || !req.query.slug || !req.query.type) {
10-
return res.status(401).json({ message: 'Invalid preview token, or no slug and type provided.' })
10+
res.status(401).json({ message: 'Invalid preview token, or no slug and type provided.' });
11+
return;
1112
}
1213

1314
const currentEnvId = req.cookies[envIdCookieName];
1415
const currentPreviewApiKey = req.cookies[previewApiKeyCookieName];
1516

1617
if (!currentPreviewApiKey && currentEnvId !== defaultEnvId) {
17-
return res.redirect(`/getPreviewApiKey?path=${encodeURIComponent(req.url ?? '')}`);
18+
res.redirect(`/api/exit-preview?callback=${`/getPreviewApiKey?path=${encodeURIComponent(req.url ?? '')}`}`);
19+
return;
1820
}
19-
2021
// Enable Preview Mode by setting the cookies
2122
res.setPreviewData({ currentPreviewApiKey });
23+
const newCookieHeader = makeCookiesCrossOrigin(res);
24+
if (newCookieHeader) {
25+
res.setHeader("Set-Cookie", newCookieHeader);
26+
}
2227

2328
const path = resolveUrlPath({
2429
type: req.query.type.toString(),
@@ -30,3 +35,24 @@ const handler: NextApiHandler = async (req, res) => {
3035
}
3136

3237
export default handler;
38+
39+
const makeCookieCrossOrigin = (header: string) => {
40+
const cookie = header.split(";")[0];
41+
42+
return cookie
43+
? `${cookie}; Path=/; SameSite=None; Secure`
44+
: "";
45+
};
46+
47+
const makeCookiesCrossOrigin = (response: NextApiResponse) => {
48+
const header = response.getHeader("Set-Cookie");
49+
50+
if (typeof header === "string") {
51+
return makeCookieCrossOrigin(header);
52+
}
53+
if (Array.isArray(header)) {
54+
return header.map(makeCookieCrossOrigin);
55+
}
56+
57+
return header;
58+
}

0 commit comments

Comments
 (0)