@akinon/next 1.126.20-v1-rc.17 → 1.126.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -4
- package/__tests__/editor-embed.test.ts +76 -0
- package/api/auth.ts +47 -6
- package/api/client.ts +26 -4
- package/middlewares/oauth-login.ts +8 -2
- package/middlewares/url-redirection.ts +7 -1
- package/package.json +2 -2
- package/utils/csrf.ts +15 -1
- package/utils/editor-embed.ts +50 -0
- package/with-pz-config.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
# @akinon/next
|
|
2
2
|
|
|
3
|
-
## 1.126.20
|
|
3
|
+
## 1.126.20
|
|
4
4
|
|
|
5
5
|
### Patch Changes
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
- 55b89cb: ZERO-5206: Port the theme editor's app skeleton (chrome and page slots) to the v1 line
|
|
8
|
+
|
|
9
|
+
The `[pz]` layout now wraps the brand header and footer in `ChromeSlot` (the brand markup stays as the fallback) and mounts `ThemeModals`, so a designer can compose the header, the footer and modals from the theme editor. Basket, checkout (top/body/bottom outlets and the variants registry), auth, forgot and reset password, order success and the account area (menu, page body and the ten subpages) get the same slot layouts as 2.x, so the editor takes these pages over without any mounting in the brand. Account routes moved into route groups (`(dashboard)`, `orders/(list)`, `orders/[id]/(detail)`, `auth/(login)`); URLs are unchanged.
|
|
10
|
+
|
|
11
|
+
Inside the editor's cross-site preview iframe the storefront's cookies are now usable: when embedding is enabled (`NEXT_PUBLIC_THEME_EDITOR_ORIGINS` holds a valid origin, or outside production) the next-auth session, CSRF and callback cookies, the Django `csrftoken`, the commerce session cookies set at sign-in and the Set-Cookie headers relayed by the BFF and middlewares are sent as `SameSite=None; Secure`, and the BFF checks the request origin on every state-changing call. A storefront without the editor keeps the Lax defaults. The CSP `frame-ancestors` also lists the editor origins (`*.akinon.net`, `localhost:5174`).
|
|
12
|
+
|
|
13
|
+
The app depends on `@akinon/pz-theme` 2.0.143; `@akinon/pz-theme` and `@akinon/theme-contract` are transpiled and protected from plugin installs, and the Tailwind content globs include pz-theme's sources.
|
|
10
14
|
|
|
11
15
|
## 1.126.19
|
|
12
16
|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isEditorEmbedEnabled,
|
|
3
|
+
makeSetCookieEmbedAware
|
|
4
|
+
} from '../utils/editor-embed';
|
|
5
|
+
import { getCsrfCookieFlags } from '../utils/csrf';
|
|
6
|
+
|
|
7
|
+
const env = process.env as Record<string, string | undefined>;
|
|
8
|
+
const original = {
|
|
9
|
+
NODE_ENV: env.NODE_ENV,
|
|
10
|
+
NEXT_PUBLIC_THEME_EDITOR_ORIGINS: env.NEXT_PUBLIC_THEME_EDITOR_ORIGINS
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const setEnv = (nodeEnv: string, origins?: string) => {
|
|
14
|
+
env.NODE_ENV = nodeEnv;
|
|
15
|
+
if (origins === undefined) delete env.NEXT_PUBLIC_THEME_EDITOR_ORIGINS;
|
|
16
|
+
else env.NEXT_PUBLIC_THEME_EDITOR_ORIGINS = origins;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
setEnv(original.NODE_ENV, original.NEXT_PUBLIC_THEME_EDITOR_ORIGINS);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe('isEditorEmbedEnabled', () => {
|
|
24
|
+
it('is always on outside production', () => {
|
|
25
|
+
setEnv('development');
|
|
26
|
+
expect(isEditorEmbedEnabled()).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('is off in production without editor origins', () => {
|
|
30
|
+
setEnv('production');
|
|
31
|
+
expect(isEditorEmbedEnabled()).toBe(false);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('is on in production when an editor origin is declared', () => {
|
|
35
|
+
setEnv('production', 'https://editor.example.com, http://localhost:5174');
|
|
36
|
+
expect(isEditorEmbedEnabled()).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('ignores entries that are not origins', () => {
|
|
40
|
+
setEnv('production', ' , not-a-url ,');
|
|
41
|
+
expect(isEditorEmbedEnabled()).toBe(false);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe('makeSetCookieEmbedAware', () => {
|
|
46
|
+
it('passes the header through when embedding is off', () => {
|
|
47
|
+
setEnv('production');
|
|
48
|
+
const header = 'osessionid=abc; Path=/; HttpOnly; SameSite=Lax';
|
|
49
|
+
expect(makeSetCookieEmbedAware(header)).toBe(header);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('replaces SameSite and adds Secure once when embedding is on', () => {
|
|
53
|
+
setEnv('production', 'https://editor.example.com');
|
|
54
|
+
expect(
|
|
55
|
+
makeSetCookieEmbedAware('osessionid=abc; Path=/; Secure; SameSite=Lax')
|
|
56
|
+
).toBe('osessionid=abc; Path=/; Secure; SameSite=None');
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('getCsrfCookieFlags', () => {
|
|
61
|
+
it('keeps the Lax default on a storefront without the editor', () => {
|
|
62
|
+
setEnv('production');
|
|
63
|
+
expect(getCsrfCookieFlags()).toMatchObject({
|
|
64
|
+
secure: true,
|
|
65
|
+
sameSite: 'lax'
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('switches to SameSite=None; Secure in embed mode', () => {
|
|
70
|
+
setEnv('production', 'https://editor.example.com');
|
|
71
|
+
expect(getCsrfCookieFlags()).toMatchObject({
|
|
72
|
+
secure: true,
|
|
73
|
+
sameSite: 'none'
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
});
|
package/api/auth.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { AuthError } from '../types';
|
|
|
10
10
|
import getRootHostname from '../utils/get-root-hostname';
|
|
11
11
|
import { LocaleUrlStrategy } from '../localization';
|
|
12
12
|
import { getCsrfCookieFlags } from '../utils/csrf';
|
|
13
|
+
import { isEditorEmbedEnabled } from '../utils/editor-embed';
|
|
13
14
|
|
|
14
15
|
function getRequestRootHostname(headers: NextApiRequest['headers']) {
|
|
15
16
|
if (Settings.localization.localeUrlStrategy !== LocaleUrlStrategy.Subdomain) {
|
|
@@ -24,11 +25,7 @@ function getRequestRootHostname(headers: NextApiRequest['headers']) {
|
|
|
24
25
|
|
|
25
26
|
function buildCsrfCookieString(value: string, rootHostname: string | null) {
|
|
26
27
|
const flags = getCsrfCookieFlags();
|
|
27
|
-
const parts = [
|
|
28
|
-
`csrftoken=${value}`,
|
|
29
|
-
'Path=/',
|
|
30
|
-
`SameSite=${flags.sameSite}`
|
|
31
|
-
];
|
|
28
|
+
const parts = [`csrftoken=${value}`, 'Path=/', `SameSite=${flags.sameSite}`];
|
|
32
29
|
|
|
33
30
|
if (flags.httpOnly) {
|
|
34
31
|
parts.push('HttpOnly');
|
|
@@ -82,11 +79,48 @@ type CustomNextAuthOptions = (
|
|
|
82
79
|
res: NextApiResponse
|
|
83
80
|
) => Partial<NextAuthOptions>;
|
|
84
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Cookie attributes that let the shopper sign in while the storefront runs
|
|
84
|
+
* inside the theme editor's preview iframe. next-auth defaults to
|
|
85
|
+
* SameSite=Lax, and in a cross-site iframe (editor origin vs storefront
|
|
86
|
+
* origin) the browser then drops the CSRF cookie, so every credentials POST
|
|
87
|
+
* fails before it reaches the provider. SameSite=None requires Secure;
|
|
88
|
+
* browsers accept Secure cookies from plain-http localhost, so local
|
|
89
|
+
* development is covered too. Scoped to editor-enabled deployments (and
|
|
90
|
+
* dev) — a storefront without the editor keeps the stricter Lax default.
|
|
91
|
+
*
|
|
92
|
+
* next-auth 4 takes an override only with its cookie name, so the names
|
|
93
|
+
* stay next-auth's own defaults: the middleware's `getToken()` still finds
|
|
94
|
+
* the session under the name it expects.
|
|
95
|
+
*/
|
|
96
|
+
const getEmbedAwareCookies = (): NextAuthOptions['cookies'] => {
|
|
97
|
+
if (!isEditorEmbedEnabled()) return undefined;
|
|
98
|
+
|
|
99
|
+
const secureNames = process.env.NEXTAUTH_URL?.startsWith('https://') ?? false;
|
|
100
|
+
const prefix = secureNames ? '__Secure-' : '';
|
|
101
|
+
const options = {
|
|
102
|
+
sameSite: 'none' as const,
|
|
103
|
+
secure: true,
|
|
104
|
+
path: '/',
|
|
105
|
+
httpOnly: true
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
sessionToken: { name: `${prefix}next-auth.session-token`, options },
|
|
110
|
+
callbackUrl: { name: `${prefix}next-auth.callback-url`, options },
|
|
111
|
+
csrfToken: {
|
|
112
|
+
name: `${secureNames ? '__Host-' : ''}next-auth.csrf-token`,
|
|
113
|
+
options
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
|
|
85
118
|
const defaultNextAuthOptions = (
|
|
86
119
|
req: NextApiRequest,
|
|
87
120
|
res: NextApiResponse
|
|
88
121
|
): NextAuthOptions => {
|
|
89
122
|
return {
|
|
123
|
+
cookies: getEmbedAwareCookies(),
|
|
90
124
|
providers: [
|
|
91
125
|
CredentialProvider({
|
|
92
126
|
id: 'oauth',
|
|
@@ -212,7 +246,14 @@ const defaultNextAuthOptions = (
|
|
|
212
246
|
? getRootHostname(hostname)
|
|
213
247
|
: null;
|
|
214
248
|
const domainOption = rootHostname ? `Domain=${rootHostname};` : '';
|
|
215
|
-
|
|
249
|
+
// Without SameSite the browser defaults to Lax and drops the
|
|
250
|
+
// commerce session on every request from the theme editor's
|
|
251
|
+
// cross-site preview iframe — the shopper signs in and the next
|
|
252
|
+
// authenticated call 401s straight back to the login.
|
|
253
|
+
const sameSiteOption = isEditorEmbedEnabled()
|
|
254
|
+
? ' SameSite=None;'
|
|
255
|
+
: '';
|
|
256
|
+
const cookieOptions = `Path=/; HttpOnly; Secure; Max-Age=${maxAge}; ${domainOption}${sameSiteOption}`;
|
|
216
257
|
|
|
217
258
|
const setCookies = [
|
|
218
259
|
`osessionid=${sessionId}; ${cookieOptions}`,
|
package/api/client.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { cookies } from 'next/headers';
|
|
|
8
8
|
import getRootHostname from '../utils/get-root-hostname';
|
|
9
9
|
import { LocaleUrlStrategy } from '../localization';
|
|
10
10
|
import { getCsrfCookieFlags, isCsrfHttpOnly } from '../utils/csrf';
|
|
11
|
+
import { isEditorEmbedEnabled } from '../utils/editor-embed';
|
|
11
12
|
|
|
12
13
|
interface RouteParams {
|
|
13
14
|
params: {
|
|
@@ -160,7 +161,16 @@ async function proxyRequest(...args) {
|
|
|
160
161
|
// browser can no longer mirror it into the `x-csrftoken` header, so the
|
|
161
162
|
// proxy validates the request origin and injects the header server-side
|
|
162
163
|
// from the cookie that the browser sent with this request.
|
|
163
|
-
|
|
164
|
+
//
|
|
165
|
+
// The origin check also runs whenever editor embedding is enabled: embed
|
|
166
|
+
// mode relaxes session cookies to SameSite=None, so the browser attaches
|
|
167
|
+
// them to cross-site requests and the Lax barrier this check replaces is
|
|
168
|
+
// gone. `isOriginAllowed` keeps accepting origin-less requests, so
|
|
169
|
+
// non-browser clients are unaffected.
|
|
170
|
+
if (
|
|
171
|
+
(isCsrfHttpOnly() || isEditorEmbedEnabled()) &&
|
|
172
|
+
STATE_CHANGING_METHODS.includes(req.method)
|
|
173
|
+
) {
|
|
164
174
|
if (!isOriginAllowed(req)) {
|
|
165
175
|
logger.warn('Client Proxy Request - Blocked cross-origin request', {
|
|
166
176
|
url: req.url,
|
|
@@ -172,9 +182,11 @@ async function proxyRequest(...args) {
|
|
|
172
182
|
);
|
|
173
183
|
}
|
|
174
184
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
185
|
+
if (isCsrfHttpOnly()) {
|
|
186
|
+
const csrfToken = nextCookies.get('csrftoken')?.value;
|
|
187
|
+
if (csrfToken) {
|
|
188
|
+
fetchOptions.headers['x-csrftoken'] = csrfToken;
|
|
189
|
+
}
|
|
178
190
|
}
|
|
179
191
|
}
|
|
180
192
|
|
|
@@ -292,6 +304,16 @@ async function proxyRequest(...args) {
|
|
|
292
304
|
cookie.sameSite = flags.sameSite;
|
|
293
305
|
}
|
|
294
306
|
}
|
|
307
|
+
// Commerce sends its session cookies without SameSite; forwarded
|
|
308
|
+
// as-is the browser defaults them to Lax and drops them inside
|
|
309
|
+
// the theme editor's cross-site preview iframe, killing the
|
|
310
|
+
// session right after login. Editor-enabled deployments relax
|
|
311
|
+
// every forwarded cookie the same way the auth cookies already
|
|
312
|
+
// are.
|
|
313
|
+
if (isEditorEmbedEnabled()) {
|
|
314
|
+
cookie.sameSite = 'none';
|
|
315
|
+
cookie.secure = true;
|
|
316
|
+
}
|
|
295
317
|
return formatCookieString(cookie);
|
|
296
318
|
})
|
|
297
319
|
.join(', ');
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
import Settings from 'settings';
|
|
8
8
|
import { getUrlPathWithLocale } from '../utils/localization';
|
|
9
9
|
import logger from '../utils/log';
|
|
10
|
+
import { makeSetCookieEmbedAware } from '../utils/editor-embed';
|
|
10
11
|
|
|
11
12
|
const LOGIN_URL_REGEX = /^\/(\w+)\/login\/?$/;
|
|
12
13
|
const CALLBACK_URL_REGEX = /^\/(\w+)\/login\/callback\/?$/;
|
|
@@ -58,7 +59,9 @@ function fetchCommerce(
|
|
|
58
59
|
|
|
59
60
|
function forwardCookies(from: Response, to: NextResponse): void {
|
|
60
61
|
from.headers.getSetCookie().forEach((cookie) => {
|
|
61
|
-
to
|
|
62
|
+
// Relayed verbatim these would default to Lax and be rejected inside
|
|
63
|
+
// the editor's cross-site preview iframe.
|
|
64
|
+
to.headers.append('set-cookie', makeSetCookieEmbedAware(cookie));
|
|
62
65
|
});
|
|
63
66
|
}
|
|
64
67
|
|
|
@@ -172,7 +175,10 @@ const withOauthLogin =
|
|
|
172
175
|
async (req: NextRequest, event: NextFetchEvent) => {
|
|
173
176
|
const { pathname, search } = req.nextUrl;
|
|
174
177
|
|
|
175
|
-
if (
|
|
178
|
+
if (
|
|
179
|
+
!pathname.includes('/login') &&
|
|
180
|
+
!pathname.startsWith('/baskets/basket')
|
|
181
|
+
) {
|
|
176
182
|
return middleware(req, event);
|
|
177
183
|
}
|
|
178
184
|
|
|
@@ -5,6 +5,7 @@ import logger from '../utils/log';
|
|
|
5
5
|
import { urlLocaleMatcherRegex } from '../utils';
|
|
6
6
|
import { getUrlPathWithLocale } from '../utils/localization';
|
|
7
7
|
import { shouldIgnoreRedirect } from '../utils/redirect-ignore';
|
|
8
|
+
import { makeSetCookieEmbedAware } from '../utils/editor-embed';
|
|
8
9
|
import { ROUTES } from 'routes';
|
|
9
10
|
|
|
10
11
|
// This middleware is used to handle url redirections set in Omnitron
|
|
@@ -76,7 +77,12 @@ const withUrlRedirection =
|
|
|
76
77
|
|
|
77
78
|
if (setCookies && setCookies.length > 0) {
|
|
78
79
|
for (const cookie of setCookies) {
|
|
79
|
-
|
|
80
|
+
// Relayed verbatim these would default to Lax and be rejected
|
|
81
|
+
// inside the editor's cross-site preview iframe.
|
|
82
|
+
response.headers.append(
|
|
83
|
+
'Set-Cookie',
|
|
84
|
+
makeSetCookieEmbedAware(cookie)
|
|
85
|
+
);
|
|
80
86
|
}
|
|
81
87
|
}
|
|
82
88
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akinon/next",
|
|
3
3
|
"description": "Core package for Project Zero Next",
|
|
4
|
-
"version": "1.126.20
|
|
4
|
+
"version": "1.126.20",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"set-cookie-parser": "2.6.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@akinon/eslint-plugin-projectzero": "1.126.20
|
|
38
|
+
"@akinon/eslint-plugin-projectzero": "1.126.20",
|
|
39
39
|
"@babel/core": "7.26.10",
|
|
40
40
|
"@babel/preset-env": "7.26.9",
|
|
41
41
|
"@babel/preset-typescript": "7.27.0",
|
package/utils/csrf.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import settings from 'settings';
|
|
2
2
|
|
|
3
|
+
import { isEditorEmbedEnabled } from './editor-embed';
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
6
|
* Whether the Django `csrftoken` cookie should be hardened with the
|
|
5
7
|
* `HttpOnly` flag.
|
|
@@ -16,7 +18,7 @@ export function isCsrfHttpOnly(): boolean {
|
|
|
16
18
|
export interface CsrfCookieFlags {
|
|
17
19
|
httpOnly: boolean;
|
|
18
20
|
secure: boolean;
|
|
19
|
-
sameSite: 'lax';
|
|
21
|
+
sameSite: 'lax' | 'none';
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
/**
|
|
@@ -26,9 +28,21 @@ export interface CsrfCookieFlags {
|
|
|
26
28
|
* `SameSite=Lax` is the Next.js-layer CSRF defense: the browser will not
|
|
27
29
|
* attach the cookie on cross-site state-changing requests, so the proxy's
|
|
28
30
|
* server-side header injection cannot be abused from a third-party origin.
|
|
31
|
+
*
|
|
32
|
+
* Inside the theme editor's preview iframe the storefront itself is the
|
|
33
|
+
* cross-site party, and a Lax cookie is never sent — the shopper signs in
|
|
34
|
+
* and the next state-changing call fails. Editor-enabled deployments
|
|
35
|
+
* therefore switch to `None` + `Secure`. What replaces the Lax barrier
|
|
36
|
+
* there: the BFF's origin check runs for every state-changing request in
|
|
37
|
+
* embed mode (see api/client.ts), and the commerce backend's `x-csrftoken`
|
|
38
|
+
* double-submit stays on either way.
|
|
29
39
|
*/
|
|
30
40
|
export function getCsrfCookieFlags(): CsrfCookieFlags {
|
|
31
41
|
const httpOnly = isCsrfHttpOnly();
|
|
42
|
+
|
|
43
|
+
if (isEditorEmbedEnabled()) {
|
|
44
|
+
return { httpOnly, secure: true, sameSite: 'none' };
|
|
45
|
+
}
|
|
32
46
|
return {
|
|
33
47
|
httpOnly,
|
|
34
48
|
secure: process.env.NODE_ENV === 'production',
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether this storefront can run inside the theme editor's preview iframe.
|
|
3
|
+
* Editor-enabled deployments declare the editor origins; local development
|
|
4
|
+
* is always embeddable.
|
|
5
|
+
*
|
|
6
|
+
* Cookie attributes hang off this: the preview iframe is cross-site from
|
|
7
|
+
* the editor's (or Omnitron's) top-level page, and a browser only sends
|
|
8
|
+
* `SameSite=None; Secure` cookies there — every session-bearing cookie has
|
|
9
|
+
* to switch when embedding is on, or the shopper signs in and the very next
|
|
10
|
+
* commerce call comes back 401.
|
|
11
|
+
*/
|
|
12
|
+
const hasValidEditorOrigin = (raw: string | undefined): boolean => {
|
|
13
|
+
if (!raw) return false;
|
|
14
|
+
|
|
15
|
+
// Embed mode relaxes the whole storefront's cookie posture, so a stray
|
|
16
|
+
// character or leftover whitespace in the env var must not flip it — only
|
|
17
|
+
// entries that parse as real origins count.
|
|
18
|
+
return raw
|
|
19
|
+
.split(',')
|
|
20
|
+
.map((entry) => entry.trim())
|
|
21
|
+
.filter(Boolean)
|
|
22
|
+
.some((entry) => {
|
|
23
|
+
try {
|
|
24
|
+
return Boolean(new URL(entry).origin);
|
|
25
|
+
} catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const isEditorEmbedEnabled = (): boolean =>
|
|
32
|
+
hasValidEditorOrigin(process.env.NEXT_PUBLIC_THEME_EDITOR_ORIGINS) ||
|
|
33
|
+
process.env.NODE_ENV !== 'production';
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Rewrites a raw Set-Cookie header so it survives the editor's cross-site
|
|
37
|
+
* preview iframe: `SameSite=None` requires `Secure`, and any prior SameSite
|
|
38
|
+
* value must be replaced rather than doubled. Outside embed mode the header
|
|
39
|
+
* passes through untouched. Use wherever commerce Set-Cookies are relayed
|
|
40
|
+
* verbatim (middlewares) — the BFF proxy applies the same rewrite itself.
|
|
41
|
+
*/
|
|
42
|
+
export const makeSetCookieEmbedAware = (setCookie: string): string => {
|
|
43
|
+
if (!isEditorEmbedEnabled()) return setCookie;
|
|
44
|
+
|
|
45
|
+
const withoutAttrs = setCookie
|
|
46
|
+
.replace(/;\s*samesite=[^;]*/gi, '')
|
|
47
|
+
.replace(/;\s*secure(?=;|\s*$)/gi, '');
|
|
48
|
+
|
|
49
|
+
return `${withoutAttrs}; Secure; SameSite=None`;
|
|
50
|
+
};
|
package/with-pz-config.js
CHANGED
|
@@ -58,7 +58,7 @@ const defaultConfig = {
|
|
|
58
58
|
{
|
|
59
59
|
key: 'Content-Security-Policy',
|
|
60
60
|
value:
|
|
61
|
-
"frame-ancestors 'self' https://*.akifast.com akifast.com https://*.akinoncloud.com akinoncloud.com https://*.akinoncloudcdn.com akinoncloudcdn.com"
|
|
61
|
+
"frame-ancestors 'self' https://*.akifast.com akifast.com https://*.akinoncloud.com akinoncloud.com https://*.akinoncloudcdn.com akinoncloudcdn.com https://*.akinon.net *.akinon.net http://localhost:5174 localhost:5174"
|
|
62
62
|
}
|
|
63
63
|
]
|
|
64
64
|
}
|