Skip to content
Team Ward Labs
Go back

Kiosk Watchdog: A Tiny Chrome Extension for Abandoned Kiosk Sessions

Edit page

We run a few self-service kiosks in our teaching labs. The flow is straightforward: a student walks up, swipes their card, gets bounced through SSO and MFA, and lands back on a kiosk page that lets them check out a hot plate or sign up for a lab section.

The flow breaks in one specific way that drove me crazy: a user starts the transaction, gets redirected to the SSO login page, realizes they don’t have their phone for the multi-factor prompt, and walks away. The browser is now stuck on a half-completed login screen tied to their university account. The next person who walks up sees that screen.

That is bad.

I went looking for an off-the-shelf fix. Existing kiosk-mode tools (Windows Assigned Access, Chrome’s kiosk flag, etc.) are great at locking down a browser, but none of them solve “the user got pulled out of the kiosk URL by an SSO redirect, and now we need to bounce them back home if they don’t finish in time.” That’s a much narrower problem, and the answer turned out to be a tiny browser extension.

What it does

Kiosk Watchdog is a ~150-line Manifest V3 Chrome extension. It does exactly one thing:

EventAction
Tab is on a URL starting with the configured kiosk URLCancel any pending reset for that tab
Tab navigates off the kiosk URL (e.g. an SSO login page)Start a per-tab timer
Timer fires and the tab is still off-kioskNavigate the tab back to the kiosk home URL
Tab returns to the kiosk URL before the timer firesCancel the timer
Tab closesClear its pending timer

Two settings, both stored in chrome.storage.sync so they travel with a managed Google profile if you want them to:

That’s it. Two inputs, one output.

How it works under the hood

The whole thing is a service worker that listens to three Chrome APIs:

chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
  if (changeInfo.url || changeInfo.status === 'complete') {
    evaluateTab(tabId, tab.url);
  }
});

chrome.tabs.onActivated.addListener(/* ... */);
chrome.tabs.onRemoved.addListener(/* ... */);

When evaluateTab decides a tab is off-kiosk, it schedules a chrome.alarms alarm named after the tab ID:

const alarmName = (tabId) => `kiosk-reset-${tabId}`;

await chrome.alarms.create(alarmName(tabId), { delayInMinutes: timeoutSeconds / 60 });

When the alarm fires, the listener double-checks the tab is still off-kiosk (the user might have completed login since the alarm was set) and then redirects:

chrome.alarms.onAlarm.addListener(async (alarm) => {
  // ...
  const tab = await chrome.tabs.get(tabId);
  if (!isOnKiosk(tab.url, kioskUrl)) {
    await chrome.tabs.update(tabId, { url: kioskUrl });
  }
});

A few things matter here:

Installation

Install it from the Chrome Web Store, or load it unpacked from the GitHub repo — both options are documented in the README.

For a lab fleet, the right move is Chrome Enterprise policy. The README has the recipe:

Try it without a real kiosk

If you want to see it work before deploying, the README has a 4-step demo using example.com (which is IANA-reserved for exactly this kind of thing — RFC 2606). Set the kiosk URL to https://example.com/, set the timeout to 15 seconds, navigate the tab anywhere else, wait. The tab snaps back. No infrastructure, no test environment.

Source and license

If you run kiosks and have hit this same problem, give it a try. Issues and PRs welcome.


Edit page
Share this post on:

Next Post
Photo Backup and Organization