Setup
For a Kirby site which needs a basic access restriction, I built a custom frontend authentication.
Since the site is intended for a closed group of visitors and should be as easy as possible to use, I would rather not mess around with user management. Instead, visitors are redirected to a simple login form with just a password field whenever they open any URL of the site. One password for all visitors is actually a sufficient level of security in this case. (The site is for a live event. And with this solution, I can simply print out the password and post it in each conference room.) Successful authentication is then either stored using the PHP session or using a custom cookie for permanent login – depending on the visitor’s choice.
Basically, this solution is just a prettier and simpler (because there is no username field) alternative to using .htpasswd
to protect the whole site.
In combination with the custom login/logout page, I use a route:before
hook in the config.php
to handle redirects to the login page in case a visitor is not yet authenticated.
"hooks" => [
"route:before" => function ($path) use (
$permanentLoginCookieName,
$permanentLoginCookieValue
) {
//Detect the preferred language of the website visitor
$detectedLang = kirby()->detectedLanguage();
$redirectPath = !empty($path) ? $path : $detectedLang->code();
// Determine the password slug based on the detected language
$passwordSlug =
$detectedLang && $detectedLang->code() === "de"
? "passwort"
: "password";
// Check if either the session or the permanent login cookie is set
// to determine if the user is authenticated
$sessionAuth = kirby()
->session()
->get("myevent-frontend-authenticated", false);
$cookieAuth = false;
if (Cookie::exists($permanentLoginCookieName)) {
$cookieAuth =
Cookie::get($permanentLoginCookieName) ===
$permanentLoginCookieValue;
}
// If the user is not authenticated, redirect to the password page
if (!$sessionAuth && !$cookieAuth) {
// Exclusions from the password page redirect:
// - the password page itself (to prevent infinite loops)
// - certain filetypes in the root directory
// - the fallback socialshare image
if (
$path !== $passwordSlug &&
$path !== "de/$passwordSlug" &&
$path !== "en/$passwordSlug" &&
!preg_match(
"/^[^\/]+\.(ico|png|svg|txt|webmanifest|xml)$/",
$path
) &&
!preg_match(
"/^media\/site\/.*\.(jpg|jpeg|png|gif|webp|avif)$/",
$path
)
) {
go(
($detectedLang ? $detectedLang->code() . "/" : "en/") .
$passwordSlug .
"?redirect_to=" .
urlencode($redirectPath)
);
}
}
},
],
This works fine and is very convenient for website visitors.
Problem
The solution has one downside: The pages cache is not available because of the use of sessions and cookies in the route:before
hook. Even trying to trick Kirby by caching pages manually using a custom cache in a route:after
hook didn’t work.
Since I don’t use sessions and cookies for any kind of personalization, and all website visitors are served the exact same content after they successfully authenticated, caching the content “after the access gate” would make sense. It is not absolutely necessary because Kirby is fast enough, even without the cache. But at least to be prepared for future bigger events, caching would be a good idea.
Question
Is there any way to bypass Kirby’s automatic cache deactivation triggered by the use of sessions and cookies without having to implement crazy hacks?