13

Is it possible to protect a page with a single password? Instead of using crafts user features, can I allow an admin to set a specific password from the edit page? When a user visits that page, they have to type in that password to view its content.

We aren't protecting anything that requires a high level of security. But we would like to prohibit access from any old visitor.

One possibility I'm considering is to have a password text field associated with that entry. Then when the user submit the login form, we can do a simple logic check to see if the password is valid. If so, we can set a “logged-in” status to either a PHP session or a cookie.

https://github.com/lewisjenkins/craft-lj-cookies

https://github.com/stuartalexwhitehead/remember

I wonder if anyone else in the Craft community has tried to implement something like this.

2
  • 3
    Sorry if I'm being master of the obvious, but wouldn't it be easiest to just set up a dummy user account in a group with otherwise very limited access, that you can give out freely. Commented Feb 24, 2015 at 3:49
  • 1
    Yep, "set up a dummy user account" is probably the easiest way, but not possible without a Craft Pro licence.
    – carlcs
    Commented Feb 24, 2015 at 10:27

3 Answers 3

18

I've actually done this a couple of times... a very simple "password-only" page. Then there would be a large group of people who share that same password, and can gain access to that protected page.

Disclaimer: This example was built using the Business Logic plugin template.

There are many ways to manage your password, but it sounds like you'll want something as simple as a Global field. Create a Global field for your password, and keep track of the Global Set ID and the field handle (you'll need that info in a minute).

On your secure page, you'll want something like this:

{% if craft.businessLogic.accessGranted %}

    <!-- Your page content goes here -->

{% else %}

    <h1>Enter your access code:</h1>
    {% if error is defined %}
        <p class="error">{{ error }}</p>
    {% endif %}
    <form method="post" action="" accept-charset="UTF-8">
        <input type="hidden" name="action" value="businessLogic/authorize">
        <input type="text" name="pw" value="">
        <input type="submit" value="Log In">
    </form>

{% endif %}

Upon submitting that form, your plugin's Controller will authenticate the user, and set a cookie to remember the user.

Something like this for your plugin's Controller:

<?php
namespace Craft;

class BusinessLogicController extends BaseController
{
    protected $allowAnonymous = true;

    // Login authorization action
    public function actionAuthorize()
    {
        // Get password
        $pwSubmitted = craft()->request->getPost('pw');

        // Check access
        if ($this->_validPassword($pwSubmitted)) {

            // Set access cookie
            $expires = time() + (60*60*24*7); // One week
            setcookie(md5('cookieNameToBeHashed'), 1, $expires, '/');

            // Redirect
            $this->redirect(craft()->request->getPath());

        } else {

            // Output error message
            craft()->urlManager->setRouteVariables(array(
                'error' => 'Invalid access code'
            ));

        }

    }

    // Check if password is valid
    private function _validPassword($pwSubmitted)
    {
        // Get global set
        $globalSet = craft()->globals->getSetById(156);

        // Get target password
        $pwTarget = $globalSet->passwordFieldhandle;

        // Return whether password matches
        return ($pwSubmitted === $pwTarget);
    }

}

Your plugin's Variable can check if the user is already authorized.

Something like this for your plugin's Variable:

<?php
namespace Craft;

class BusinessLogicVariable
{

    // Check if access cookie is set
    public function accessGranted()
    {
        $cookieName = md5('cookieNameToBeHashed');
        return array_key_exists($cookieName, $_COOKIE);
    }

}

Which of course, brings it full circle. Your secure page will check if accessGranted before showing either the secure content, or the login form.

3
  • Great little plugin this is, thanks Lindsey!
    – carlcs
    Commented Feb 24, 2015 at 19:02
  • 1
    Thanks @carlcs! I would probably just bundle this up into a ready-to-go plugin, but there are way too many unique use-cases for something along these lines.
    – Lindsey D
    Commented Feb 24, 2015 at 20:04
  • wonderfully simple approach. Commented Sep 24, 2015 at 21:17
13

The easiest way I have found to password protect page content is as follows:

{% if craft.request.getParam('password') == 'password_you_want' %}
    <h2>This is your content</h2>
{% else %}
    <h2>Not authorized</h2>
    <form action="{{ craft.request.getUrl() }}" method="POST" accept-charset="utf-8" class="form-inline">
        {{ getCsrfInput() }}
        <input type="password" name="password" value="">
        <button type="submit">Submit</button>
    </form>
{% endif %}

You put your password protected content in the first half of the if loop and the second half of the loop appears the first time you load the page; prompting you with a password form that resubmits to the same page looking for the correct 'password_you_want'.

Sometimes simple is best.

3
  • this is great and simple solution Is there any way for 'password_you_want' that is defined inside entry in text field so its different for every entry
    – user4274
    Commented Oct 31, 2015 at 11:21
  • @Jozo: I'd recommend you ask a new question about that, since the answer is probably too long to put into comments. Feel free to link back to this thread for reference.
    – Lindsey D
    Commented Oct 31, 2015 at 21:15
  • The "password_you_want" could be set within a text field of the entry itself. Commented Nov 1, 2015 at 14:51
3

I’ve done this a few times before and this is how I usually get it done.

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:

  • Contains a field for your password
  • 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.

This is pulled from a blog post I wrote a while ago.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.