Always check storage access before placing test cookie (#23393)

This commit is contained in:
Jon Koops 2023-09-27 13:38:53 +02:00 committed by GitHub
parent de5aa2e74d
commit 1b6cb7b2a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 42 additions and 44 deletions

View file

@ -1275,12 +1275,17 @@ function Keycloak (config) {
if (event.data !== "supported" && event.data !== "unsupported") {
return;
} else if (event.data === "unsupported") {
logWarn(
"[KEYCLOAK] Your browser is blocking access to 3rd-party cookies, this means:\n\n" +
" - It is not possible to retrieve tokens without redirecting to the Keycloak server (a.k.a. no support for silent authentication).\n" +
" - It is not possible to automatically detect changes to the session status (such as the user logging out in another tab).\n\n" +
"For more information see: https://www.keycloak.org/docs/latest/securing_apps/#_modern_browsers"
);
loginIframe.enable = false;
if (kc.silentCheckSsoFallback) {
kc.silentCheckSsoRedirectUri = false;
}
logWarn("[KEYCLOAK] 3rd party cookies aren't supported by this browser. checkLoginIframe and " +
"silent check-sso are not available.")
}
document.body.removeChild(iframe);

View file

@ -1,66 +1,42 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta charset="utf-8">
</head>
<body>
<script type="module">
// Check if the browser has granted us access to 3rd-party storage (such as cookies).
if (await hasStorageAccess()) {
// If so, signal support to the page embedding this iframe.
window.parent.postMessage("supported", "*");
} else {
// Otherwise, attempt to place a test cookie to verify support.
const hasAccess = await hasStorageAccess();
if (hasAccess) {
// If so, attempt to place a cookie to test this assumption.
attemptWithTestCookie();
} else {
// Otherwise, signal that 3rd-party access is not supported.
signalSupport(false);
}
// See: https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API/Using
async function hasStorageAccess() {
// If the Storage Access API is not implemented, assume we don't have access.
if (!("hasStorageAccess" in document)) {
return false;
}
const hasAccess = await document.hasStorageAccess();
// If we have access to unpartitioned cookies, signal support.
if (hasAccess) {
return true;
}
// Otherwise, check whether unpartitioned cookie access has been granted to another same-site embed.
let permission;
try {
permission = await navigator.permissions.query({
name: "storage-access",
});
} catch (error) {
return false;
}
// If not, signal that there is no support.
if (permission.state !== "granted") {
return false;
}
// Otherwise, call requestStorageAccess() without a user interaction, and it should resolve automatically.
// But just to be sure, handle a possible exception in case this behavior changes in the future.
try {
await document.requestStorageAccess();
return true;
} catch (error) {
return false;
}
return document.hasStorageAccess();
}
function attemptWithTestCookie() {
// Place a cookie to test whether we can access cookies from 3rd-party storage.
document.cookie = "KEYCLOAK_3P_COOKIE_SAMESITE=supported; Max-Age=60; SameSite=None; Secure";
document.cookie = "KEYCLOAK_3P_COOKIE=supported; Max-Age=60";
// Then redirect to the page where we will read these cookies to confirm this.
window.location = "step2.html";
}
function signalSupport(isSupported) {
// Signal 3rd-party access support to the page embedding this iframe.
window.parent.postMessage(isSupported ? "supported" : "unsupported", "*");
}
</script>
</body>
</html>

View file

@ -1,7 +1,7 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta charset="utf-8">
</head>
<body>
<script type="module">

View file

@ -1,7 +1,7 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta charset="utf-8">
</head>
<body>
<script type="module">
@ -30,7 +30,14 @@
let init;
async function checkState(clientId, origin, sessionState) {
const cookie = getSessionCookie();
// Check if the browser has granted us access to 3rd-party storage (such as cookies).
const hasAccess = await hasStorageAccess();
// If we don't have access, signal an error.
// As we cannot read cookies, we cannot verify the session state.
if (!hasAccess) {
return "error";
}
// If not initialized, verify this client is allowed access with a call to the server.
if (!init) {
@ -48,6 +55,8 @@
init = { clientId, origin };
}
const cookie = getSessionCookie();
// Signal a change in state if there is no cookie, and the session state is not empty.
if (!cookie) {
return sessionState !== "" ? "changed" : "unchanged";
@ -63,6 +72,14 @@
return "error";
}
async function hasStorageAccess() {
if (!("hasStorageAccess" in document)) {
return true;
}
return document.hasStorageAccess();
}
function getSessionCookie() {
const cookie = getCookieByName("KEYCLOAK_SESSION");