Protecting Content

It's common to want to put a site online before it's ready to be viewed by the public. Statamic has built-in ways of making this very easy for you.

Overview

You may deny front-end access to your content on a per-page or site-wide basis.

There are a number of drivers for protecting content available out of the box:

  • auth for only allowing authenticated users.
  • ip_address for allowing specific IP addresses.
  • password will force users to enter a specified password.

You can also create your own drivers.

Whichever approach you choose, know that it’s designed to help you out. We’ve tried to keep the syntax as simple as possible while allowing for flexibility. Because of this, if Statamic sees you’ve set protect, but the scheme has been configured incorrectly, all users will be denied.

This feature only applies to the front-end of your site on pages routed through Statamic (like entry URLs). Custom PHP/Laravel-defined routes and the Control Panel will be unaffected.

Basic Usage

To protect a page, add a protect variable with a corresponding scheme name.

For example, Statamic comes pre-configured with a logged_in protection scheme that only shows the content to authenticated users. You might have an entry like this:

---
title: Members Only
protect: logged_in
---
When visiting this entry's URL, logged in users will see it,
but logged out users will be redirected to a login page.

Configuring Schemes

The configuration file is located at config/statamic/protect.php. In this file you may specify a number of different schemes which you can reference throughout your content files.

You are free to use the same driver in multiple schemes, configured in different ways. Check below for details on how to configure each driver.

IP Address

Add the IP address(es) you wish to allow to the aptly named allowed array.

'ip_address' => [
    'driver' => 'ip_address',
    'allowed' => ['127.0.0.1', '192.168.0.10'],
]

Authentication

Adding this scheme to a page will redirect to a login page unless the user is already logged in as a Statamic user.

'logged_in' => [
    'driver' => 'auth',
    'login_url' => '/login',
    'append_redirect' => true,
]
A Statamic 3 password protected page
The default password protected login screen.

If the login_url has not been defined the user will see an “Access Denied” page instead of a login screen. In this case, the user could log in through the Control Panel and then come back.

The append_redirect setting will add ?redirect=/the-protected-url to your login_url. This pairs with the user:login_form tag’s allow_request_redirect parameter which will redirect the user to the intended page once successfully logged in.

This protection method does not take into account any user roles. They are simply either logged in or they’re not.

Password

This is perfect for times when you want to password-protect one or more files but don’t want to set up user accounts for this one purpose. This scheme does not relate to member accounts in any way.

'password' => [
    'driver' => 'password',
    'allowed' => ['secret', 'confidential'],
    'form_url' => null,
]

Password Form

You’ll need to provide a way for people to enter passwords for URLs. Statamic has a built-in generic password entry form. If you want to customize it, you have two options:

Override the view

Override the view by creating vendor/statamic/auth/protect/password.antlers.html in your views directory, and use the protect:password_form tag to build a form. No config change required. For example:

{{ protect:password_form }}
    {{ if no_token }}
        No token has been provided.
    {{ else }}

        {{ if error }}
            <div class="error">{{ error }}</div>
        {{ /if }}

        <input type="password" name="password" />

        {{ errors:password }}
            <div class="inline-error">{{ value }}</div>
        {{ /errors:password }}

        <button>Submit</button>

    {{ /if }}
{{ /protect:password_form }}

The protect:password_form tag is going to wrap everything between the tags in an HTML form tag pointing to the appropriate place.

The HTML of the form itself is up to you. The only requirement is to name the password input password. You can do anything else you want.

Custom Form URL

If you would like more control over the location of the password form, you may change form_url in the scheme’s config:

'form_url' => '/password-entry'

You can create a page or a route for this. In the corresponding view, you can build a form as described above.

Validation Errors

When a validation error is encountered, error and errors variables will be available to you.

The error variable will be a string with the first error, useful if you want to display an error at the top of your form.

The errors variable will be an array keyed by field names, each containing an array of messages. This is useful for inline errors.

Token

When visiting a password protected page, Statamic will generate a token and append it to the form’s URL. Without a token, the form cannot function correctly. In the example above, you can see the no_token boolean will be populated for you. This may happen if you visit the form URL directly.

Invalid Passwords

If someone submits a password and it isn’t valid, Statamic will redirect back with the appropriate validation error. Valid passwords can vary from piece of content to piece of content. This one form is smart enough to handle all password management between password-protected URLs.

Valid Passwords

A valid password is anything matching one of the passwords in the allowed list as configured on the page (or site-wide). This means that you can send three people three different passwords to access the same URL, each having their own way in. Additionally, you could also set just one password and send that to 100 people and they can all use the same password.

As always with online security, be careful with who you share passwords with or you’ll find yourself changing them often.

This protection method is only meant for short-term access control. For example, showing a client your progress without the public or to prevent Google from indexing a staging site. It’s secure like a curtain over an open window: just good enough for passerbys.

Password Expiration

Each user’s passwords will expire along with their session. To manually invalidate a password, remove it from the list of allowed passwords on the page. The next time a user with that password visits this page they’ll be redirected to the password form just like everyone else.

Endgame Protection

If you want to protect a page from anyone - regardless of authentication status, IP address, time of day, weather, or beverage preference - you can simply add protect: true to the entry’s front-matter.

One may find this useful to quickly disable something.

Site-wide Protection

To protect your whole site at once, add a scheme name to default in your protect.php configuration file.

For example, to make sure your whole site is only accessible to a single IP address, you could add:

'default' => 'test',

'schemes' => [
    'test' => [
        'driver' => 'ip_address',
        'allowed' => ['127.0.0.1']
    ]
]

Custom Drivers

Writing the Driver

To create your custom protection driver, you should extend the Statamic\Auth\Protect\Protector class and add a protect method.

The protect method should typically:

  • Call abort(403) to deny access.
  • Call abort(redirect($url)) to redirect somewhere (eg. how the auth driver redirects to a login page)
  • Do nothing, which would allow access.

Here’s a silly example that will randomly allow or deny access:

<?php

use Statamic\Auth\Protect\Protectors\Protector;

class CoinFlip extends Protector
{
    public function protect()
    {
        $heads = (bool) random_int(0, 1);

        abort_if($heads, 403);
    }
}

Within this class, you have a number of properties available to you.

$this->scheme; // The name of the scheme.
$this->config; // The configuration array of the scheme.
$this->url;    // The URL the protection was triggered on.
$this->data;   // The data object (eg. the Entry) being protected.

Registering the Driver

Inside a service provider’s boot method, you can use the extend method on the protector manager class.

use Statamic\Auth\Protect\ProtectorManager;

app(ProtectorManager::class)->extend('coin_flip', function ($app) {
    return new CoinFlip;
});

The first argument passed to the extend method is the name of the driver. This will correspond to your driver option in the protect.php configuration file. The second argument is a Closure that should return an Protector instance. The Closure will be passed an $app instance, which is an instance of the service container.

Once your extension is registered, update your protect.php configuration file’s driver option to the name of your extension.

Betterify this page on Github!