Always check storage access before placing test cookie (#23393)
This commit is contained in:
parent
de5aa2e74d
commit
1b6cb7b2a9
4 changed files with 42 additions and 44 deletions
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<script type="module">
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
Loading…
Reference in a new issue