Restricted access

Let's imagine, you need to have a portion of your site secured so that only certain users can access it.

With Sitecore this is really easy to accomplish. All you need to do is:

  1. Deny read access to the extranet\anonymous user. This is an exception to the usual security best practices (i.e. don't use explicit denies and don't put permissions on users). However in this case this is OK as the extranet\anonymous user is a funny kind of user. One that acts almost as a pseudo-role, as it is the user mapped to all request from non-authenticated visitors.
  2. Provide a login page in the website definition This is so you can benefit from Sitecore's help. If a non-authenticated user requests a page for which it has no read permission; Sitecore will redirect to the login page specified and will append the original url as a query string parameter so you can redirect back to the requested page after succesfully authenticating the visitor
  3. Go have a beer. Well, strictly speaking it does not have to be a beer. You are done - that's all that's required.

Just a quick note: make sure your login page is served under HTTPS. If you have any links to the login page, make sure you are also using HTTPS there. Hell, nowadays your entire site should be run over a secure connection.

So this is an easy scenario. But what if we want the entire site to require authentication?

The website definition also has a requireLogin attribute. If we turn that to yes, indepentenly of security permissions, Sitecore will require authentication before it allows anybody to access such site. If you have not denied read access to the login page, then Sitecore will be clever enough to let the anonymous user access such page to be able to log in.

It all seems too easy...

Until you realiase you can't prescind of the user permissions. If you don't want extranet\anonymous to access any page on the site, you need to deny read access. Why? Because Sitecore's multi-site implementation is a little bit colourful; you may think http://hostname/ will be the only way of getting to your homepage. Try this on a blank sitecore: http://hostname/sitecore modules/web/sitecore/content/home

Yes, it works. And what's worse, the context site is modules web. And that context site does not have requiredLogin turned to yes. So unless you deny read access to extranet\anonymous, your unauthenticated visitors can have a ball on your secure site, if they have a bit of imagination to figure out the path of the home item.

So, we need to use security permissions, and that means we need to move the login page somewhere else.

The challenge here is that you need to put the login page elsewhere but it needs to be accessible through that same host name. As an example, this would be the sequence of events:

  1. Request https://mysecuresite.com
  2. Sitecore redirects to https://mysecuresite.com/login?returnUrl=%2F
  3. Authenticate user, redirect to /

So we need the second request to the login page to have the same hostname.

We can do that through another site definition. We will not set the hostName, but we will use the virtualFolder and physicalFolder attributes. And we will make sure Sitecore finds this new definition before it finds the one for the secured site.

<site name="login_website" virtualFolder="/login" physicalFolder="/login" rootPath="/sitecore/content" startItem="/login" inherits="website" />  
<site name="website" requireLogin="true" loginPage="/login" enableTracking="true" virtualFolder="/" physicalFolder="/" rootPath="/sitecore/content" startItem="/home" database="web" domain="extranet" allowDebug="true" cacheHtml="true" htmlCacheSize="50MB" registryCacheSize="0" viewStateCacheSize="0" xslCacheSize="25MB" filteredItemsCacheSize="10MB" enablePreview="true" enableWebEdit="true" enableDebugger="true" disableClientData="false" cacheRenderingParameters="true" renderingParametersCacheSize="10MB" />  

notice also the use of the inherits attribute, to save me having to declare all the attributes again

So now Sitecore will redirect to the /login page, which is picked up by the login_website site definition, and maps it to a different item in the tree which is not under home.

This will work fine for any page in the site except for the home page. Why? You get a layout not found error:

Well, I can't tell for sure, it looks to me like a bug. The solution is easy though, we need to change the site definition of website so it's set up with:

  • rootPath="/sitecore/content/home"
  • startItem="/"

This will make everything work as desired. But as many times with Sitecore, things are not that easy.

There is a bug in some versions (it's certainly fixed in 8.1 update 2), which will make the LinkManager break with such configuration. The solution is overriding the LinkProvider. I am sure Sitecore support will be eager to help - or you can ping me, I have the cure too.