Implementing Authentication in Kentico Xperience 13
Brenden K
Posted on July 18, 2022
The Problem
During the development phases of your projects, clients sometimes want their site secured so it cannot be viewed without a valid login. Using previous versions of Kentico Xperience, this was done with a few button clicks and like magic, authentication was in force. Using the newest and greatest .NET technology, it's a bit more effort. The idea around this article is to help you implement authentication quickly and effortlessly.
TL;DR;
There is a GitHub repo with the supporting code you can check out with the code necessary to implement authentication.
A Solution
If you're looking for a more complex implemention to use with, for example, a membership portal or e-commerce site, you'll want to take the basics you'll learn here and expand on them.
We'll take this step by step. Along the way, you may find I've missed a step, have a syntax error, etc., if you find something like this, please send me a note or submit a pull request on GitHub. Let's get started!
Prerequisites
- You need your own instance of Kentico Xperience with a connected database. This example is using Xperience version 13.0.77.
- For testing purposes, you can import the page types I've included or create your own.
Step 1 - Verify Your Site Settings
Log into your Kentico Xperience instance and navigate to the Settings application. In the Settings application navigate to the Security & Membership section. In the Content section, verify the "Check page-level permissions" is set to "Secured areas".
Step 2 - Add Pages to your Content Tree
For this step you'll need some pages in your content tree. If you do not have any page types created, check out the 2 simple page types I've created in the GitHub repo that you can import into your project. Both page types are simple, they have a field called "PageName" and they have all the page type features enabled.
I've added a few testing pages to my content tree.
Step 3 - Secure a Section
We'll be securing the /Page-2 section of the site. If you want secure the whole site, you'll just perform the below actions on the root of the site vs. the Page 2 page. Keep in mind, the settings you put on the parent page will replicate through the child pages automatically.
Select the Page 2 page in the content tree, then select the General > Security tab on the editor side. Scroll to the bottom of the Security page and check Yes for Requires authentication and save the page.
Upon saving the page you will have immediately secured that page and all it's child pages. However, if you were to navigate to that page, you'd have a very poor user experience and receive an ugly 403 error.
I had you do these steps before implementing any code because this is a very quick way to lock things down IF you have an immediate need. The problem is there's no graceful fallback to handle any of the errors, login, redirects, etc., so lets look at how we can get the front facing website to work with what we just implemented.
Step 4 - Implement the Code
Let's take a look at the project structure.
The files of importance are the following:
/Controllers/AccountController.cs
/Models/Users/Account/SignInViewModel.cs
/Views/Account/PermissionDenied.cshtml
/Views/Account/SignIn.cshtml
/Views/Account/WaitingForApproval.cshtml
/Views/Shared/\_SignInLayout.cshtml
/StartUp.cs
The files I mention above have the necessary code needed to implement security. The project in GitHub has everything needed to get an example site, with authentication, up and running in less than 30 minutes. You will most likely not need all the extra files, but they are there for you to use.
Step 4A - Implement the Account Controller
public class AccountController : Controller
Take a look at the /Controllers/AccountController.cs file. This file has a few optional items in it but for the most part it can be implemented line for line. You'll notice I've commented out all the code in this file and subsequent files that enable the "remember me" functionality. If you wish to use this functionality, please read the comments and update the code accordingly.
Step 4B - Implement the SignInViewModel
As I mentioned before, the "Remember Me" functionality is commented out so if you're interested in that functionality, uncomment it in these files as well as the AccountController file.
[Required(ErrorMessage = "Please enter a user name")]
[Display(Name = "User name")]
[MaxLength(100, ErrorMessage = "The User name cannot be longer than 100 characters.")]
public string UserName { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Password")]
[MaxLength(100, ErrorMessage = "The password cannot be longer than 100 characters.")]
public string Password { get; set; }
Step 4C - Implement the Views
There are 4 views you'll need to add:
Sign in layout view - this holds a stripped down view of the _Layout.cshtml file that is used for all the "authentication" views. The following 3 views will inherit this view.
Sign in view - this is the form the user will fill out to log in.
Permission denied view - this has a simple message to the user stating they cannot access the page, most likely due to roles that were implemented (this is another blog post).
Waiting for approval view - this view displays a message to the user stating they have not been approved by the admin yet.
You'll notice below were using a pretty simple Bootstrap 5 implementation for the form and throughout the rest of the site. One very important piece which caused me several hours of headache was the asp-route-returnurl
attribute and value on the Sign In view. If you do not include this, your users will not be redirected back to where they were trying to go.
<h2>Sign in</h2>
<div asp-validation-summary="ModelOnly">
<span>Please correct the following errors</span>
</div>
<form asp-controller="Account" asp-action="SignIn" method="post" class="row g-3" asp-route-returnUrl="@Context.Request.Query["ReturnUrl"]">
<div class="col-12">
<label asp-for="UserName" class="form-label"></label>
<input asp-for="UserName" class="form-control" required />
<span asp-validation-for="UserName" class="text-danger"></span>
</div>
<div class="col-12">
<label asp-for="Password" class="form-label"></label>
<input asp-for="Password" class="form-control" required />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
@* Enable if you want the "remember me" feature to work *@
@*<div class="col-12">
<span class="logon-remember-me-checkbox checkbox">
@Html.EditorFor(model => model.RememberMe) @Html.LabelFor(model => model.RememberMe)
</span>
</div>*@
<div class="col-12">
<input type="submit" value="Sign in" class="btn btn-primary" />
</div>
</form>
Step 4D - Implement the Startup.cs Code
The following method is the main area of importance. There are a few other minor areas and you'll see them as you check out the code file, but this method is the most important piece you'll want to make sure is implemented.
/// <summary>
/// Method used to configure authentation for a Kentico Xperience site
/// </summary>
/// <param name="services"></param>
private static void ConfigureMembershipServices(IServiceCollection services)
This method has all the necessary pieces you'll need to properly implement a "quick" authentication option within your site. Again, these are the minimum pieces needed. If you want to enable more features or functionalty, I'd suggest reading the Kentico Xperience documentation or checking out the Kentico Xperience Training sample site on GitHub (this code repo cooresponds with Kentico Xperience Developer Training).
Step 5 - Implement a Sign Out Button
While I didn't mention this file in a previous step, it is worth noting if we're allowing a user to sign in, we should also allow them to sign out.
Check out the Footer view. At the bottom of the page you'll find a very simple logical statement with a form and a button inside it. This will only show if the user is authenticated.
@if (User.Identity.IsAuthenticated)
{
@\* Give the user a signout button if signed in \*@
@using (Html.BeginForm("SignOut", "Account", FormMethod.Post))
{
@Html.AntiForgeryToken()
<button type="submit" class="btn btn-link">Sign Out</button>
}
}
Putting It All Together
Below are a few screenshots of the basic site and how it works.
Non-Secured Page:
Trying to access a secured page, forcing login:
After logging in, you have access to the "secure" content:
This is a very simplistic implementation. It does NOT:
- implement roles,
- implement any kind of registration,
- implement any kind of password retrieval or resetting abilities,
- implement any kind of area specific permissions.
What it does do is the following:
- requires a user to be created in the Kentico Xperience User's application,
- allows that user to log into the public site,
- allows a user to access pages that require authentication,
- allows a user to log out if they are logged in.
In the event you no longer need authentication on your site, you can set the security settings for that page or section of pages to Requires authentication = NO or Inherit (NO). There's no real issue leaving the code in place either except that all the "/Account" pages will be accessible.
And that's it! While it may seem like a daunting task to get done, you should be able to get this example up an running within 30 minutes even if you have to install a new instance of Kentico Xperience.
Looking forward to seeing your comments and improvements to this on GitHub!
Best of luck and Happy Coding!
Posted on July 18, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.