Adding authentication to an existing multi tier .Net Core application

Adding built in authentication when you are creating a project is a simple process. It is literally a radio button.

We have all been their when the project starts authentication is not in scope and then all of a sudden it is the most important thing. Fortunately there is a way to add .Net authentication to an existing application. We are going to add .Net authentication into an application that has a seperate data layer project.

We are going to use the the PubReviews app whic we created in this article https://medium.com/@r.beggs/creating-a-multi-project-net-core-database-solution-a69decdf8d7e. Here we have 2 projects a PubReviews.Web and a PubReviews.Data. As you might expect the PubReviews.Web contains the front end website and the PubReviews.Data contains the database. You don’t need to use this project as the principles are the same for all multi tier projects.

Firstly we need to add the following Nuget to the PubReviews.Data project

- Microsoft.AspNetCore.Identity.EntityFrameworkCore

In our PubReviewsContext.cs file we can now extend what we are inheriting. Instead of inheriting the DBContext we can inherit the IdentityDbContext which also extends the DbContext.

We can now scaffold the frontend part of the authentication. On the PubReviews.Web project right click and select Add-> New Scaffold Item…

On the left hand side select “Identity” and then select “Identity” from the centre panel.

We select the the exiting _layout.cshtml page. Now we have to decide what functionality we need/want. I have decided to include everything but that is probably over kill. If you don’t need 2 factor authentication there is no point in including it. Lastly and most importantly we get to give our database context this is the PubReviewContext in the PubReviewsData project that we have just updated to inculde the IdentityDbContext.

After we hit “Add” we will see that it has created a number of new folders and files. These are the pages that will be used to manage the authenication accounts on the front end.

Now we need to create a migration to create the tables required for the stroage of the account details. In the package managment console type

Add-Migration Create_Indentity

This will create a new migration Create_identity.cs

We have to run it to the update commant to update the database. In the package manager console type
Update-Database Create_Indentity

If we open the database in SQL Management studio we can see that the new tables have been created. The default table names start AspNet this can be changed to make the tables anything. I have included an article in the further reading if this is something you require. How now we will stick to the default naming.

In the _Layout.cs page we want to add the _loginPartial.cshtml so it appears on the top of each page that uses the _Layout.cs. The _Layout.cs page will be located on the in “Views”-> “Shared”

In the PubReviews.Web project we need to add the NuGet.
- Microsoft.AspNetCore.Identity.UI

Once this is installed we can inject the IdentityUsers and IdentityRoles. In the ServiceCollectionExtensions.cs in the PubReviews.Data project we need to add the following code to the RegisterDataServices method.

services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<PubReviewsContext>()
.AddDefaultTokenProviders();

We need to configure the application to deal with the new Razor pages that have been created to manage the accounts in the Identity section. To do this we need to add “endpoints.MapRazorPages();” to the the Startup.cs in the PubReview.Web project.

We also need to add “services.AddMvc();” to the ConfigureServices method. within the Startup.cs

One other change we need to make to the StartUp.cs is to add app.UseAuthentication();

app.UseAuthentication();

This tells the application that we want to use the authentication.

Setting up the Email Sender

One of the features offered by the default account functionality is the ability to send emails for confirmation or to send out a forgetten password. To get this to work we need to set up an email sender. In an enterprise solution we would have 3 tiers, presentation, business, and data. For simplicity we have not including the business tier. Ideally this is where the EmailService would go but for the sake of simplicity we will add it it to the presentation tier. We need to implement IEmailSender. To do this we will need to create a new class EmailSender.cs. We will create this in the PubReviews.Web project. We will create a new folder “Services” and create the “EmailSender.cs” there.

The class will implement the IEmailSender interface. We will need to include the following usings:

  • Microsoft.AspNetCore.Identity.UI.Services

We need setup the dependency injection for the EmailSender.cs. This is done the same way as any other dependency injection in the StartUp.cs. We need to add services.AddTransient<IEmailSender, Services.EmailSender>(); to the ConfigureServices method.

This will wire up the email sender but it will not do anything until we add some functionality to it. At the minute all it does is throws a NotImplementedException.
We need to populate the email settings, and the best place to store them is in the appSettings.json file. The easiest way to pass these in is when the EmailSender is being injected. We will add the settings to the class and then populate them from the constructor.

We now need to add the email settings to the appSettings.json file. We create a new section “EmailSettings” and store the settings in here. This means we can have different setting depending on the enviroment that the application is deployed to. You would not what you dev tests using the production mail box.

We now need to pass these settings into the emailSender constructor. In the StartUp.cs we can get the EmailSettings from the configuration object.

services.AddTransient<IEmailSender, Services.EmailSender>(e =>
new EmailSender(Configuration["EmailSettings:host"],
Configuration.GetValue<int>("EmailSettings:port"),
Configuration.GetValue<bool>("EmailSettings:enableSSL"),
Configuration["EmailSettings:UserName"],
Configuration["EmailSettings:Password"]));

Now that we have all our email settings available in the EmailSender.cs we can populate the SendEmailAsync method with some functionality. We need to add the following usings.

  • using System.Net.Mail;
var client = new SmtpClient(host, port)                 
{
Credentials = new NetworkCredential(userName,password),
EnableSsl = enableSSL
};
return client.SendMailAsync(
new MailMessage(userName, email, subject,htmlMessage)
{ IsBodyHtml = true }
);

We need to create a SmptClient with the host and port that we have passed into the constructor. We then setup the network credentials for the email account sending the emails and setting the SSL. We can then send the email using the client.SendMailAsync and the parameters that are passed into the method already.

Now we start the application we should see a page like this.

With “Register” and “Login” in the top right hand corner. If we click on “Register” we can see this page.

Here we can register an account afterwards you will be redirected back to the home screen.

But the text in the top right hand corner will say your “Hello [emailaddress]” and “Log out”.

If we check the database we can see that the table has been populated with a user in the “AspNetUsers” table.

We can now test that the authentication is actually working. We will create a new page and make it only accessible to users that are authorized. We are using the PubReviews codebase so we can add the authorization to the Pubs index page but we can use any page. All we have to do is add the [Authorize] attribute to the Index method.

This will cause the authentication to fire when someone trys to access the Index page. However we need to setup authentication unsuccessful page so the user doesn’t just get a big error. The easiest thing to do is to redirect the user to the login page. This is done in the StartUp.cs. We will add the following code to our ConfigureServices method.

services.ConfigureApplicationCookie(options =>
{
// Cookie settings
options.LoginPath = “/Identity/Account/Login”;
options.AccessDeniedPath = “/Identity/Account/AccessDenied”;
});

There are more options that you can specifiy depending on your requirements. I have included links to articles that detail these options in the further reading section.

Now when we load application and try to access https://localhost:44356/Pub without logging in we will be redirected to the Login page.

We can see in the URL there is a redirectUrl property that will redirect us back to the page we tried to access before access was denied.

Now when we fill out the login screen we are taken to the Pubs page.

And we are logged in. We can develop this more and add links to the manage to the user account. These pages have already been generated in the Areas->Identity->Pages->Manage. This will allow further controll of the options available to the users.

Notes

There is an existing issue with the ResendEmailConfirmationModel.cshtml.cs you need to remove the abstract from the class. This may be fixed in later versions.

Further Reading

Adding custom fields to the identity object

Customizing Identity Tables

Configuring the authentication options

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store