demo · v131

OAuth popup flow under each COOP value

An OAuth implicit-flow popup needs to (a) open from a click and (b) ship a token back to the opener. Some COOP values block (b). This page lays out three flows side-by-side: default unsafe-none, the new noopener-allow-popups, and full same-origin. Each tile shows what works.

1. opener: unsafe-none (default)

Cross-Origin-Opener-Policy: unsafe-none
open: works
postMessage back: works
side-channel attack: possible

Bidirectional access — the popup can script the opener and vice versa. Vulnerable to cross-origin Spectre-style leaks.

2. opener: noopener-allow-popups (new in v131)

Cross-Origin-Opener-Policy: noopener-allow-popups
open: works
window.opener: null in popup
side-channel attack: blocked

The new sweet spot: popup still opens, but it's noopener by default. Use BroadcastChannel or a redirect-back to deliver the token instead of window.opener.postMessage.

3. opener: same-origin

Cross-Origin-Opener-Policy: same-origin
open: cross-origin popup is severed
postMessage back: no opener handle
side-channel attack: blocked

Maximum isolation. Cross-origin popups (most OAuth providers) lose the opener relationship entirely, breaking many flows.

live opener check

no popups yet

migration recipe

If you have an OAuth implicit-flow popup, swap window.opener.postMessage for a BroadcastChannel or for a redirect that posts the token back through a fetch. Then deploy the new COOP value to neutralise the attack surface without breaking the flow.

// Old (works under unsafe-none, broken under noopener-allow-popups):
window.opener.postMessage({ token }, "*");

// New (works everywhere):
const bc = new BroadcastChannel("auth");
bc.postMessage({ token });
bc.close();

see also