Best Practices: Secure Zone Login

I have done a few secure zones so I thought I would share my experiences and post them as a best practice. If anyone has some other or better options please feel free.

When you set up a secure zone it asks for a landing page. This landing page needs to be in the secure zone so your login form needs to be on a separate page not in your secure zone. Let’s say our secure zone folder is /secure and our landing page is /main so our path is /secure/main. Lets put our login form on /login.

Let’s start off with the basic login form from the component menu.

<form action="/public/api/members/login" method="POST">
    <label for="email">Email</label>
       <input type="text" name="Email">
   <label for="password">Password</label>
       <input type="password" name="Password">  
   {% if request.request_data.is_mobile == 1 %}
     <div class ="captcha-holder">
		<div class="g-recaptcha" data-size="compact" data-sitekey="6Lf2cGcUAAAAABbL4aDrclASNZx9S3uaI9EvpvlI"></div>
	 </div>
   {% else %}
	 <div class =" captcha-holder">
	    <div class="g-recaptcha" data-sitekey="6Lf2cGcUAAAAABbL4aDrclASNZx9S3uaI9EvpvlI"></div>
	 </div>
   {% endif %}
   <input type="submit" value="Login">
   <a href="/request-reset-password">Forgot Password</a>
</form>

When you use this form to log in if looks like nothing happens. Actually, it changes the url and adds “?login=sucess” so your URL becomes…

https://website.com/login?login=sucess

To get the form go to your secure zone landing page you can add a redirect like

<input type="hidden" name="redirectURL" value="/secure/main">`

That’s the basics now let’s look at some issues and address them.

Issue 1 - Bookmarked page.
Users will bookmark your login page so they can log in in the future. If they didn’t log off or it hasn’t been 24 hours since logging in and they go to the login page it will look like they need to login again. To fix this we add the following code to the head of the /login page under the SEO tab.

{% if request.is_logged == 'true' %}<meta http-equiv="refresh" content="0;url=/secure/main" />{% endif %}

This will look to see if the user is logged in or not and if they are, redirect them to the landing page. This is not instant even with the refresh set to 0. So I add this code to the login form.

{% if request.is_logged == 'true' %}
   // Welcome or loading message  //
{% else %}
   //  Form contnet here //
{% endif %}

You can add any message that is appropriate for your site. You can also change the refresh to a different value if you want the message to be visible longer.

Issue 2 - Multiple Secure Zones
if you have multiple secure zones and for the most part the user will only be assigned to one secure zone you can do the following.

  1. Remove the redirect in your form.
  2. Replace the redirect in the head of the page under the SEO tab to…
{% if request.currentmember.securezones[0].id == '1' %}
<meta http-equiv="refresh" content="0;URL='/secure/main'" />    
{% elsif request.currentmember.securezones[0].id == '2' %}
<meta http-equiv="refresh" content="0;URL='/secure2/main'" /> 
{% elsif request.currentmember.securezones[0].id == '3' %}
<meta http-equiv="refresh" content="0;URL='/secure3/main'" /> 
{% endif %}

Note we are using an if statement to redirect each secure zone to its landing page. You will need to find the IIDnumber of your secure zones. You can find the ID number by going to the secure zone settings in the admin and look at the URL. There will be a number, for example

/admin/secure-zones/2/details

This will do two things. One redirects on login and two, redirect if the user goes to the login page and is already logged in.
NOTE: that if the user has access to more than one secure zone they will be logged in to the first one they have access to. So if the user has access to zone 1 and 3 they will always be logged in to zone 1 with this setup.

Issue 3 - Safari browser
So if you use the Safari browser on either macOS or iOS and you save your passwords you will notice a very annoying behavior. When you select your saved user id and password it automatically submits the form. This comes back as a failure because we didn’t click on the reCaptcha. Initially, I tried to set up the form so that if you use Safari it would place the reCaptcha on top of the form. I wasn’t able to accomplish this and I felt it looked wrong and users would still enter their email and password first. So I went another way and hide the email and password fields until the reCaptcha is validated. To do this I added

data-callback=“imNotARobot”

to the reCaptcha lines.

I put the form fields and submit button in a div for the show/hide

<div id="formapproved" style="display:none">
 //form content//
</div>

then added the following script to the form

<script type="text/javascript">
  var imNotARobot = function(response) {
    // console.info("Button was clicked");
    if(response=='')document.getElementById('formapproved').style.display="none";
    else document.getElementById('formapproved').style.display="block";
  };
</script>

Here is the complete form with the above added

{% if request.is_logged == 'true' %}
<p>Welcome or Loading message.</p>
{% else %}
<h1>title</h1>
<br>
<form id="login" action="/public/api/members/login" method="POST">
<input type="hidden" name="redirectURL" value="/prep-page/main">
{% if request.request_data.is_mobile == 1 %}
    <div class ="captcha-holder">
    	<div class="g-recaptcha" data-size="compact" data-callback="imNotARobot" data-sitekey="6Lf2cGcUAAAAABbL4aDrclASNZx9S3uaI9EvpvlI"></div>
    </div>
{% else %}
    <div class =" captcha-holder">
    	<div class="g-recaptcha"  data-callback="imNotARobot" data-sitekey="6Lf2cGcUAAAAABbL4aDrclASNZx9S3uaI9EvpvlI"></div>
    </div>
{% endif %}
<br>
<div id="formapproved" style="display:none">
<label for="email">Email</label>
<br>
    <input type="text" name="Email">
    <br>
<label for="password">Password</label>
<br>
    <input type="password" name="Password">  
    <br>
<input type="submit" value="Login">
    <a href="/request-reset-password">Forgot Password</a>
    </div>
</form>
{% endif %}
<script type="text/javascript">
  var imNotARobot = function(response) {
    // console.info("Button was clicked");
    if(response=='')document.getElementById('formapproved').style.display="none";
    else document.getElementById('formapproved').style.display="block";
  };
</script>

Issue 4 - Unauthorized
If a user dials directly to a secure page or bookmarks a secure page they will be presented with an “Unauthorized” message if they are not logged in. This also happens when they are auto-logged off after 24 hours they refresh or navigate to another secure zone page. This is actually a 401 page. Let’s go to Settings / System Pages / 401 and make some adjustments to this page. I basically change this page to the login page by adding the same login form we created for the login page. Usually I change the title to “Login Required” but you can do what you want.

Issue 5 - 401 Page redirect
If a user logs in via the 401 page they want to go to one of two places. The page they tried to dial directly or from a bookmark, or the page they tried to navigate to when they thought they were still logged in. You can accomplish this with the following redirect

<input type="hidden" name="redirectURL" value="{{request.request_url.href | split: 443 | last }}">

If you use {{request.request_data.refferer}} it doesn’t work if you dial direct. So I tried {{request.request_url.href}} but that didn’t work either because the URL looks like this

“href”: “https://website.com/401?401;https://website.com:443/secure/content?”,

So I split at the 443 and using last we get /secure/content? and that then redirects to the correct page.

Issue 6 - User access to multiple secure zones.
In Issue 2 we covered multiple secure zones but that was meant for users that are only assigned to 1 zone. One option is to allow access to the zone they want is with a form select drop-down menu. Then direct every option to the appropriate secure zone landing page. Once selected and entered it will force a 401 and provide the login. You can also have them log in from the login page and then provide Landing page options based on their secure zone access.

UPDATE: So I messed around with Issue 6 multiple secure zones access and this is what I think works best. Create a login page and redirect it to a selection page or making a selection visible on the login page once logged in. The reason I would have them log in first is because then we can present them with only the secure zone options that they have access too. Now there may be times where you want to present them all the options first like to sell access to other zones.

So here is the code to work with for making a selection after they log in.

{% for zone in request.currentmember.securezones %}
{% if zone.id == 0 %}
Link or content here href=“/secure/main”>Secure Zone
{% endif %}
{% endfor %}

As you can see we have to create a for loop to see all the zones they have access to. If we use the same code as in Issue 2 it will stop at the first zone it finds and then it will only provide one option.

Here is a dropdown menu option.

<select>
    {% for zone in request.currentmember.securezones %}
        {% if zone.id == 0 %}
            <option value="/secure/main">Secure Zone</option>
        {% elsif zone.id == 1 %}             
            <option value="/secure/main 2">Secure Zone 2</option>
        {% elsif zone.id == 2 %}
            <option value="/secure/main 3">Secure Zone 3</option>
        {% elsif zone.id == 3 %}
            <option value="/secure/main 4">Secure Zone 4</option>
        {% endif %}
    {% endfor %}
</select>

At this time (v5.9.5) you can’t get the zone URL with the liquid {{request}} only the name that means you have to hard code the landing page URLs. You could call the name with {{zone.name}} Adding <select onChange="window.document.location.href=this.options[this.selectedIndex].value;"> will trigger the redirect once a selection is made.

Here is a video showing all the above in action.

4 Likes

That’s super helpfull, @Rhatch. Thanks for that. We work on a site with multiple secure zones just now, so this comes at the right time. I will add any more info we find along the way :-).

@Rhatch Thank you so much for sharing. I really appreciate it.

@Adam.Wilson Do we have a good place to pin info like this? It would be useful to have this visible from the docs, or perhaps the treehouse site.

This is great, thanks @Rhatch. :+1:

I’ve added a link to this post as an external resource for the Secure Zone documentation article.

1 Like

Thank you @Rhatch. I’m presently building a multi-SZ site so your timing here was perfect! Saved me a bunch of hours. (I do believe I owe you a beer!).

This is great @Rhatch, thank you for sharing all of this! I am working on a site that has multiple secure zones (2 at the moment) and uses the Treepl CMS MAX theme, so the redirect logic for multiple secure zones is helping me set up a unique landing page redirect for each secure zone.

However, the MAX theme has (and of course the client likes) the log in option at the top of the screen in the top-right of the header on every page. I added the logic to the includes file and it worked, however it kept looping because the landing page used the theme’s default template, which of course uses the global head file. So I created a ‘head-alt.inc’ file without the logic and a ‘Secure Zone’ Content Template that uses the head-alt.inc and left the logic out. That worked great… for the landing page. However, once the user is logged in, hits the right landing page, and tries to navigate to any other page on the site, of course they go back to a page with the logic in place and that loops them right back to the landing page, creating an infinite loop always back to the landing page. So I get the issue and the logic behind the issue, but I’m not sure how to solve it.

Does anyone have any ideas what I can do to get the multiple secure zone landing page redirect logic to work with a site that has a login form on every page and not just a single page with a login form? For now, we’re going with the single page with a login form route and I left the logic in place to show the ‘log out’ or ‘register’ button in the top-right of the page so that users can log out. This appears to be working with the logic in this thread only added to the on the SEO tab of the login page, as I’d expect. But I think we’d like to find a way to be able to use the login at the top of the page again, I’m just not sure how to adapt the code to do so. Any ideas?

Thank you!

My code was for the login on its own page and not a modal. If you remove the modal and put it on its own page, it should work.

I should update this content as I have found other ways to do this. This may help you. Depending on the site, I have eliminated the multiple landing pages and when with on global landing page, if you will. On this landing page, I load content based on the Secure Zone or Zones they have access to. For example, I did a baseball club site, and they have three secure zones. One for Parents, Coaches, and Board Members. A CRM contact could just be a parent or a parent and coach, or any combination. By having a single landing page, I can display content for one or all three zones. In this case, the page is mostly just links to other pages, but it works.

To make this work, I added this code to my template to capture all the Secure Zone ideas they have access to.

{% capture portal %}{% for zone in request.currentmember.securezones %}{{zone.id}}{% endfor %}{% endcapture%}

Then on the landing page, I use a liquid if statement to display the content for each zone. Make sure you use contains and not == like this.

{% if portal contains "1334" %}

By adding the above Capture code to your template, you can also show/restrict content throughout the site. For example, the same baseball site has various content that is public or private as well as specific to a team. Based on the user and their secure zone, the correct information is displayed.