I am setting the Cross-Origin-Opener-Policy (COOP) to 'same-origin' and I use window.open to open a window, while being within the same origin (user is located at https://example.com/here):
// within a user-invoked event
const childWindow = window.open('https://example.com/there', 'child-window')
This works as expected on Chrome and FF, where I have knowledge about the child window's location and closed state and the child can access window.opener.
However, on Safari Desktop (and only there), it behaves as if the sites don't have the same origin:
childWindow.closed // true
in the popup:
window.opener // null
Setting the COOP header to same-origin-allow-popups won't change anything.
Is this expected behaviour and I simply misunderstood the whole concept or is Safari doing things it's own way (hello IE...)?
Edit/note:
Setting COOP to unsafe-none makes it all work but that's not a real solution, right?
Edit 2:
System Info
MacOS 12.4
Safari 15.5 (17613.2.7.1.8)
Header info
Summary
URL: https://example.com/here
Status: 200
Source: Service Worker
Request
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.5 Safari/605.1.15
Response
Content-Type: text/html; charset=utf-8
Content-Security-Policy: upgrade-in-secure-requests;default-src 'self';script-src 'self' 'unsafe-eval' 'sha256-<hereIsAHash>' 'sha256-<hereIsAnotherHash>';child-src 'self';connect-src 'self' https://example.com wss://example.com;font-src 'self' data:;form-action 'self';frame-ancestors 'self';frame-src *;img-src 'self' data: blob:;manifest-src 'self';media-src 'self';object-src 'self';sandbox allow-same-origin allow-downloads allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-scripts allow-forms allow-modals allow-presentation;style-src 'self' 'unsafe-inline';worker-src 'self' blob:;base-uri 'self';script-src-attr 'none';upgrade-insecure-requests
X-XSS-Protection: 0
Cross-Origin-Resource-Policy: cross-origin
Content-Encoding: gzip
Referrer-Policy: no-referrer
Date: Fri, 18 Nov 2022 08:46:17 GMT
Cross-Origin-Opener-Policy: same-origin
X-DNS-Prefetch-Control: off
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Vary: Accept-Encoding
Server: nginx/1.21.6
x-download-options: noopen
x-permitted-cross-domain-policies: none
expect-ct: max-age=604800, enforce
Strict-Transport-Security: max-age=15552000; includeSubDomains, max-age=31536000
origin-agent-cluster: ?1
Helmet config
const self = '\'self\''
const data = 'data:'
const blob = 'blob:'
const unsafeEval = '\'unsafe-eval\''
const unsafeInline = '\'unsafe-inline\''
const connectSrc = ['https://example.com', 'wss://example.com']
const externalHostUrls = ['https://foobar.com']
export const helmetOptions = {
crossOriginEmbedderPolicy: false,
crossOriginOpenerPolicy: { policy: 'same-origin' },
crossOriginResourcePolicy: { policy: 'cross-origin' },
contentSecurityPolicy: {
blockAllMixedContent: true,
directives: {
upgradeInSecureRequests: [],
defaultSrc: [self],
scriptSrc: [
self,
// we use hashes to allow unsafeEval when using dynamic imports
unsafeEval,
`'sha256-${hashes[0]}'`,
`'sha256-${hashes[1]}'`
],
childSrc: [self],
connectSrc: connectSrc.concat(externalHostUrls),
fontSrc: [self, data],
formAction: [self],
frameAncestors: [self],
frameSrc: ['*'],
imgSrc: [self, data, blob].concat(externalHostUrls),
manifestSrc: [self],
mediaSrc: [self],
objectSrc: [self],
sandbox: [
'allow-same-origin',
'allow-downloads',
'allow-orientation-lock',
'allow-pointer-lock',
'allow-popups',
'allow-popups-to-escape-sandbox',
'allow-scripts',
'allow-forms',
'allow-modals',
'allow-presentation'
],
styleSrc: [self, unsafeInline],
workerSrc: [self, blob]
}
},
strictTransportSecurity: {
maxAge: 15552000,
includeSubDomains: true,
preload: false
},
referrerPolicy: {
policy: 'no-referrer'
},
expectCt: {
enforce: true,
maxAge: 604800
},
frameguard: {
action: 'sameorigin'
},
dnsPrefetchControl: {
allow: false
},
permittedCrossDomainPolicies: {
permittedPolicies: 'none'
},
hidePoweredBy: true
}
}
Version: [email protected]
It is great that you added the
helmet configinto your question's edit - that truly helps!My theory
Relying on MDN the
upgradeInSecureRequestsis deprecated and comes with several wranings. From the docs:The fact that the compatibility table (at the bottom of the page) states that there is no clear evidence for safari support (rest of browsers have more concrete data) only makes it a strong suspect
I suggest that you set this setting in your
helmet configto false and test it onsafari. This should not effect other browsers