I've been working on this for some time, reading around and seeing various posts and answers, conjecture and speculation.
This solution does have a significant caveat - Popup windows ("BURN HIM!!!" I hear you cry) are required, as is JavaScript. However, given that you have a need to implement this so securely, I'd hazard a guess that the users would accept this to maintain security, and you could code for JavaScript being enabled.
STEP 1 - Verify that popups are enabled - If not, redirect to instructions on how to enable
in the Global.asax
Setup a session variable to verify if popups have been checked:
void Session_Start(object sender, EventArgs e)
{
// Code that runs when a new session is started
Session["popupsChecked"] = false;
}
in Page.aspx / masterPage.master
Check the session variable, if false (first visit to the site this session), inject the JavaScript to check for popup availability:
if (!IsPostBack)
{
if (!(bool)Session["popupsChecked"])
{
Page.Header.Controls.Add(new LiteralControl("<script src=\"/js/popupCheck.js\" type=\"text/javascript\"></script>"));
}
}
The popupCheck.js file (The file "enablePopups.aspx" is instructions on how to enable popups for the site in question)
$(function () {
result = window.open("/static/popupCheck.aspx", "popped", "width=10, height=10, location=no, menubar=no, status=no, toolbar=no, scrollbars=no, resizable=no");
if (result != null) {
// Not blocking
if (window.location.pathname != "/static/enablePopups.aspx") {
window.location = "/";
};
}
else {
//blocking
if (window.location.pathname != "/static/enablePopups.aspx") {
window.location = "/static/enablePopups.aspx";
};
}
});
And finally, the popupCheck.aspx
<head runat="server">
<title>Popup Checker</title>
<script language="javascript" type="text/javascript">
window.close();
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
Popup windows are enabled, please close this window.
</div>
</form>
</body>
</html>
With the following in the codebehind - This will stop any further checks to see if popup windows are enabled:
protected void Page_Load(object sender, EventArgs e)
{
Session["popupsChecked"] = true;
}
So at this point we now have "forced" the user to enable popups for this site - As said, the site content should justify this annoyance in my opinion
STEP 2 - Check for where they're going, if they're off, popup the logoff
In your main javascript block, file, data, or however you're doing it now days
var siteLinkClicked = false;
var isAuthenticated = false;
$(function() {
// Check if user is authenticated
if ($("#someElementThatOnlyAppearsWhenLoggedIn").length) { isAuthenticated = true; };
// Check if any of the links clicked are in the site
$("a, input").click(function () { // -- You may need more then "a" and "input" tags, or exceptions
siteLinkClicked = true;
});
$(window).bind("beforeunload", function (event) {
if (siteLinkClicked == false && isAuthenticated == true) {
// popup the logout page
window.open("/popupLogout.aspx", "popupLogout", "status=0,location=0,directories=0,menubar=0,scrollbar=0,resizable=0,width=400,height=200")
};
});
});
And the popupLogout.aspx
We have some javascript to check for the parent (opener) location, if it's the same as this popup (i.e. the user has clicked refresh - Or you've missed an element for siteLinkClicked) then just close the window without doing anything:
$(function () {
setTimeout(checkParent, 5000); // Give some time for the new window to load if they've navigated away - A counter could be added, logging off in 5... 4... 3... You get the idea.
window.blur(); // And stick this to the back
});
function checkParent() {
var openerLocation = null;
try {
openerLocation = window.opener.location.hostname
} catch (e) {
openerLocation = null;
}
if (openerLocation == window.location.hostname) {
window.close();
} else {
$("#<%= cmdLogout.ClientID %>").click();
// setTimeout(window.close(), 1000); // Removed and add a redirect to static "loggedOut.html page
};
};
If the location is different / undefined then fire off a button click, then close the window:
<asp:Button Text="logout..." runat="server" ID="cmdLogout" OnClick="cmdLogout_click" />
And the button then triggers the following code:
protected void cmdLogout_click(object sender, EventArgs e)
{
System.Web.Security.FormsAuthentication.SignOut();
Session.Abandon();
Response.Redirect("~/static/loggedOut.htm"); // Added, can self close if needed in the HTML
}
Final limitation reiteration
Without JavaScript or popups this method will not work, and for me at least, I'll be working in a controlled environment where security is paramount so this solution is worth the annoyance of the end users.
If you have to go to these extreme ends, then I can only assume that the data and application are sensitive enough to force the users to have to enable javascript and have to enable popups.