Create Password Protected Pages in Craft

I recently set up some password protected pages in Craft, working with Chris Chiles here at Mighty. The client wanted something they could keep secure, yet provide access to indicated people. Here’s how I did it.

Set Per Entry Password

Within Craft create a new Plain Text field for the entry you would like password protected. The password for the entry can be a phrase, random string of numbers and letters, whatever.

Create a Login Template

In Craft, set up a route to the login template including the slug of the page you are password protecting. For example, blog/{slug}/login.

Within your login template create a form that:

  1. Contains a field for your password
  2. Posts to itself

It should look something like this:

    <form id="check-code" action="/{{ craft.request. getPath() }}" method="POST">
        <input type="text" name="access-code" id="access-code" />
        <input type="submit" value="Submit" class="submit" />
    </form>

Check Form Submission against Entry’s Password

Within the login template wrap the form in a conditional that checks for the password field’s POST value when the page is reloaded following successful form submission (I use Parsley for pre-submission form validation).

    {% if craft.request.getPost('access-code') %}

    {% else %}

        <form action="/{{ craft.request. getPath() }}" method="POST">
            …
        </form>

    {% endif %}

Now, select the entry using craft.request.getSegment() and compare its password field against the post data from our form submission. If they don’t match then create a duplicate of our form from below with some kind of error message (I’m sure there’s a way to include the forms, without repeating markup using a macro or something). This allows the user to try again if they got it wrong.

    {% if craft.request.getPost('access-code') %}

        {% set entry = craft.entries.section({entrySection}).slug(craft.request.getSegment(2)).first() %}

        {% if entry.passwordField == craft.request.getPost('access-code') %}    

        {% else %}

            <form action="/{{ craft.request. getPath() }}" method="POST">
                <p>Your access code was incorrect</p>
                <input type="text" name="access-code" id="access-code" />
                <input type="submit" value="Submit" />
            </form>

        {% endif %}

    {% else %}

        <form action="/{{ craft.request. getPath() }}" method="POST">
            …
        </form>

    {% endif %}

Now, if the password and the post data do much we need to set a cookie and redirect the user to the entry’s template. In order to set the cookie, I’m using Andrew Welch’s Cookies plugin. In this case I’m setting the cookie to expire with the current browser session, but you can set it to be whatever you want following PHP’s set cookie parameters.

    {% if craft.request.getPost('access-code') %}

        {% set entry = craft.entries.section({entrySection}).slug(craft.request.getSegment(2)).first() %}

        {% if entry.passwordField == craft.request.getPost('access-code') %}

            {{ setCookie( craft.request.getSegment(2), entry.passwordField, 0, "/") }}
            {% redirect "blog/"~craft.request.getSegment(2) %}

        {% else %}

            <form action="/{{ craft.request. getPath() }}" method="POST">
                …
            </form>

        {% endif %}

    {% else %}

        <form action="/{{ craft.request. getPath() }}" method="POST">
            …
        </form>

    {% endif %}

Check for Cookie in Entry’s template

At the top of your entry template check if the entry has a password set and if the browser does not have a cookie set, whose name matches the entry’s slug and whose value matches the entry’s password. If those conditions are met then the user has not logged in and will be redirected to the login page. Otherwise, the template will continue loading and the user will see the entry template.

    {% if (entry.passwordField != "") and (getCookie( entry.slug ) != entry.passwordField) %}

        {% redirect "blog/"~entry.slug~"/login" %}

    {% endif %}

Bonus

Once you’ve tested out that this process is working you, or other credentialed site users, may not want to have to enter in the password each time. Simply add another conditional to the entry template’s password/cookie check.

    {% if (entry.passwordField != "") and (getCookie( entry.slug ) != entry.passwordField) and (currentUser == false) %}

        {% redirect "blog/"~entry.slug~"/login" %}

    {% endif %}

Now, if the user is logged in to the site the conditional will be bypassed and they will be able to see the content without getting redirected to the login page.

Conclusion

The process of password protecting certain pages could serve many purposes. Here’s a few ideas:

  1. Content that you want to be able to distribute privately. Proposals, design comps, etc.
  2. Could serve as an intranet without the intranet for small organizations.
  3. You want to provide access to content editors who are not registered users so you can get feedback.
  4. RickRolling your friends.

Give it a whirl.

Craft CMS Twig HTML Forms Security