Creating an Authorization System with C#, ASP.NET MVC and Active Directory — Part 1

Exploring the good ol’ advice of “not reinventing the wheel”

Samuel Plumppu
My Adventure
6 min readNov 2, 2016

--

In this series of posts, we’ll explore the creation of a custom authorization system for an ASP.NET MVC application. This is the information I sought a month ago but couldn’t find. No matter if you’re working on a similar problem or if you just are curious, I hope these insights can help you.

Here’s what we’ll create:

This approach to authorization brings several benefits:

  • Users can do more of their work using their Windows account.
  • Active Directory domain administrators can easily manage user privileges by...
  • ...using tools they already are familiar with.
  • ...using a central account management solution.
  • Application development efforts can focus on the core functionality rather than re-implementing user authentication (verifying users' identities)

These reasons make this solution well suited for larger IT environments where the last thing you want is yet another application with it's own decentralized, custom tool for account management.

The design of this authorization system is focused on two things:
1. Clear separation of concerns between components.
2. Ease of configuration (Improved maintainability over time)

Both goals are achieved by using a central place to hide configuration details from the rest of the application.

The Contents of this Series:

This first post gives you an example of how general tools couldn’t solve a specific problem.

In the second post, you’ll get a high level overview of the authorization system I created and learn why it’s structured the way it is.

Finally, the third post gives you a more detailed walk-through of the system’s components and the code behind them.

But before we dive into this system, let’s start by exploring a good ol’ piece of advice

Solving problems as a software developer is a careful balance of tradeoffs. A common practice is to be mindful of already existing solutions and avoid reinventing the wheel. Generally speaking, this is a good advice that helps us programmers to work more efficiently by keeping focus on the most important problems. But as with most good advice, it tends to work best when used in moderation.

For general applications where problems are common across many different implementations, this advice is used to create a consensus around tools that can be reused in a broad range of domains. However, in contrast to application development, system development (where a system to me is an environment where several applications or services work together) more often feature highly specific problems.

Not only are specific problems so much harder to solve by using the general tools — they sometimes even seem impossible to solve if the tool makes assumptions that doesn’t fit the project’s requirements. Based on this, we can draw two conclusions:

  1. The decision to use or not to use existing solutions will be different depending on the problem in front of you.
  2. Limitations and advantages of existing solutions should be carefully evaluated to ensure that the “solution” actually won’t end up making your problem harder to solve.

With these thoughts in mind, let me tell you more about my situation a month ago

I was just about to start the first software development project of my internship at one of Europe’s biggest kitchen-companies. The project’s goal was to secure the web based administration tool for a business system in order to prepare it for release on the intranet. We saw this project as three distinct steps:

  1. Adding user accounts.
  2. Creating different user roles.
  3. Restricting the users’ actions based on their roles.

Since the company’s idea for user authentication was to use the already existing intranet Windows accounts for a single sign on solution, the first step was basically solved. Additionally, they also wanted to manage user roles for the administration tool with the same system used to manage the user accounts themselves, Active Directory. The second step was solved as well.

One way to accomplish this kind of user role management is to create specific Active Directory groups for each different user role and then adding users to them. This way, the application would determine whether or not a user should be allowed to perform a given action by verifying that they are a member of a specific Active Directory group.

All in all, this meant the problems were solved on a high level and that I could start implementing the solution. At this time, I saw the solution as two parts:

  1. Connecting the application to Active Directory.
  2. Verifying that users were members of some specific Active Directory group whenever they perform actions within the application.

I knew that I wanted to make use of existing authorization solutions as much as possible and avoid reinventing the wheel.

However, I didn’t yet know that these two steps weren’t the only problems I had to solve.

So I started experimenting…

The problem’s first part was solved by adding an ADMembershipProvider as the authority for verifying user accounts and retrieving the Active Directory groups that we’ll use after next. Details for setting this up can be found in this post by Chris Schiffhauer. I’ll also cover this in the third post.

For the problem’s second part, I started out by using features built into the ASP.NET MVC framework. The main functionality for authorizing users would be the Authorize-attribute — metadata used to configure permissions required to access specific controllers or actions of the application.

To a start, this looked like a solid solution as it easily could verify that an user belonged to a specific Active Directory group. But as I started experimenting, I realized that this existing solution was limited to verifying users against one user role at a time — not against a specific role or any role in the user hierarchy that have higher permissions.

In addition, the Authorize-attribute had other limitations

Firstly, the role names had to be configured using strings known at compile time. This meant that no centralized variables could be used to reduce hard coded configuration spread across the code base.

Secondly, the structure of the administration application would require the attribute to be used for every controller or action to get the fine grain of control needed to configure the permissions. I thought about using the FilterConfig.RegisterGlobalFilters()-method since it can apply an Authorize-attribute as the default permission for the whole application (and reduce some of the spread out configuration). However, this couldn’t solve the whole problem.

When an out-of-the-box solution doesn’t suit your needs, the general advice is usually to see if there’s a way to create a solution based on the existing one

So as a next step, I created a custom attribute called AuthorizeAD that would solve the problem of verifying users against a lowest required permission or against any permission above in the user role hierarchy.

But I still couldn’t get around the problem that configuration had to be hard coded across the code base. Since neither of the solutions I had tried was working, I felt like there was something missing.

The decisive reason to take a step back and came when I eventually realized I hadn’t yet fully understood the problem.

And without having a clear definition of the problem, it’s impossible to be sure that you’re solving the right problem

This got me thinking. An additional reason to why the existing solutions didn’t work laid in that the structure of the administration application represented the underlying business system. The previous solutions verified users merely based on their roles — but in reality, authorization had to be done based on the role and a scope of access.

This additional requirement was necessary since there could be several users with the same role who should have access to different areas of the application. A scope of access was needed to prevent users with the same role from accessing or modifying information in each other’s areas.

I thought about using the AuthorizeAD-attribute with an additional parameter to specify the least required scope of access. But as mentioned earlier, it would still be hard to maintain this kind of solution because of the spread out, hard coded configuration.

I needed something different. It was time to “reinvent the wheel”

Thank you for reading!

This was the first part of my series on creating an authorization system using C#, ASP.NET MVC and Active Directory.

In the next post, we’ll explore the resulting system and it’s design rationale

If you want to read more like this, or want to share your thoughts — please get in touch! :)

--

--