Restricting access to your site
You can use Kirby’s user system to restrict access to parts of your website that should only be available to certain users, for example, a clients’ area.
In this recipe we will guide you through
- creating a login form for the front-end
- providing a logout link
- protecting pages from unauthenticated users
Do not use the file cache for pages with protected content, otherwise your content might be visible to unauthenticated users.
User management
All users, including front-end users, are managed via the Panel.
Creating roles without panel access
By default, Kirby provides a single admin role with access to the Panel and without any restrictions. Front-end users usually shouldn’t have access to the Panel at all so we first need to create a user role without Panel access.
In your /site/blueprints/users
folder, create a new file called client.yml
with the following content:
title: Client
permissions:
access:
panel: false
If you add a new user in the Panel and assign the client
role, this user cannot login to the Panel.
You can create as many roles without Panel access as necessary to determine which part of your front-end should be accessible to which role.
The login page
For the login page, we use an unlisted page with some basic information that gets its own template. Create a /content/login
folder with a login.txt
text file inside it. We use the text file to store the information for the form.
By creating a content file with these fields, we can make the form more dynamic and translate form labels and error messages in a multi-language installation if required. You could also hard-code this in your template instead.
Title: Login
----
Alert: Invalid user or password
----
Username: User name
----
Password: Password
----
Button: Log in
The corresponding blueprint for the Panel
To make this file editable in the Panel, create a blueprint for this page:
title: Login
icon: 🔐
fields:
alert:
label: Alert text
type: text
username:
label: Label for username
type: text
password:
label: Label for password
type: text
button:
label: Button text
type: text
The login template
In the login template, create the login form and a container for the error messages:
<?php snippet('header') ?>
<h1><?= $page->title()->html() ?></h1>
<?php if($error): ?>
<div class="alert"><?= $page->alert()->html() ?></div>
<?php endif ?>
<form method="post" action="<?= $page->url() ?>">
<div>
<label for="email"><?= $page->username()->html() ?></label>
<input type="email" id="email" name="email" value="<?= get('email') ? esc(get('email'), 'attr') : '' ?>">
</div>
<div>
<label for="password"><?= $page->password()->html() ?></label>
<input type="password" id="password" name="password" value="<?= get('password') ? esc(get('password'), 'attr') : '' ?>">
</div>
<div>
<input type="submit" name="login" value="<?= $page->button()->html() ?>">
</div>
</form>
<?php snippet('footer') ?>
The controller
To handle the form submission we create a login controller to keep the logic out of the template.
<?php
return function ($kirby) {
// don't show the login screen to already logged in users
if ($kirby->user()) {
go('/');
}
$error = false;
// handle the form submission
if ($kirby->request()->is('POST') && get('login')) {
// try to log the user in with the provided credentials
try {
$kirby->auth()->login(get('email'), get('password'));
// redirect to the homepage if the login was successful
go('/');
} catch (Exception $e) {
$error = true;
}
}
return [
'error' => $error
];
};
The login will redirect the user back to the homepage if it was successful. Otherwise the error variable is returned to the template as true and the alert is displayed. If you want to redirect the user to a different page, change the path in the go()
method.
The logout
For the logout we don’t need a real page. A simple URL to send logged-in users to is enough.
return [
'routes' => [
[
'pattern' => 'logout',
'action' => function() {
if ($user = kirby()->user()) {
$user->logout();
}
go('login');
}
]
]
];
By adding the code above to your config file, Kirby will register a new route to http://yoursite.com/logout
. When you open that URL, the action method is called and a logged-in user will be logged out. Afterwards the script will redirect the user to the login page.
Adding a logout link to the menu
As soon as the user logged in, we display a logout link in the menu or somewhere else on the page. Here is the menu from Kirby’s Starterkit with an additional li
element that appears when the user has logged in.
<nav id="menu" class="menu">
<?php foreach ($site->children()->listed() as $item): ?>
<?= $item->title()->link() ?>
<?php endforeach ?>
<?php if ($user = $kirby->user()): ?>
<li>
<a href="<?= url('logout') ?>">Logout</a>
</li>
<?php endif ?>
</nav>
Protecting Content
With the login and logout processes in place, we can finally protect our content.
Protecting entire pages
You can protect entire pages from unauthenticated users by adding the following line at the top of a template:
<?php if (!$kirby->user()) go('/') ?>
// rest of the template
This will redirect all unauthenticated visitors to the home page.
Instead of adding this code to each template, you can also put your logic into a route, for example, when restricting access by other criteria than the template.
Protecting parts of a page
In the same way, you can hide parts of a page from unauthenticated users:
<?php snippet('header') ?>
<h1><?= $page->title()->html() ?></h1>
<?= $page->text()->kirbytext() ?>
<?php if ($kirby->user()): ?>
Top Secret: the meaning of life is…
<?php endif ?>
<?php snippet('footer') ?>
Protecting content by role
The above examples don’t differentiate by user role but grant access to these pages to all logged-in users. If you have multiple front-end user roles and want to restrict access to certain pages or parts of pages to particular roles, you can ask for the current user’s role like this:
<?php if (($user = $kirby->user()) && $user->role()->id() === 'client'): ?>
This part of the page is only visible for
clients with the role clients
<?php endif ?>
Remarks
Note that this recipe only provides a basic example to give you an idea how to handle access restrictions. You can extend this into a powerful user area, with user sign-on, password reset, etc.
The process described here doesn’t prevent access to your assets (images, video, documents etc., which will still be accessible to anyone who guesses the URL to these files). We will deal with this in a separate recipe.