The excellent class library FluentSecurity allows you to easily secure an ASP.NET MVC application. By using it, you can benefit from the following main advantages:
- Authorization rules can be specified for both single action methods or entire controllers.
- Security is specified in a centralized place in a readable and maintainable way, thus making it unnecessary to decorate your controllers with
[Authorize]attributes cluttering your code.
- FluentSecurity forces you by default to explicitly specify authorization rules for all action methods to prevent you from forgetting to secure them.
- You can extend the library and implement your own authorization rules and violation handlers.
- The specified security configuration is unit testable; thus, you can verify that it is working correctly.
Integration into an MVC Application
The quickest way to integrate FluentSecurity into an ASP.NET MVC application is installing the NuGet package FluentSecurity, so open up the NuGet Package Manager Console, make sure your MVC project is selected in the Default project dropdown list, and run the following command:
Your project now references the assembly FluentSecurity — besides that, nothing has been changed.
Retrieving the User's Authentication Status
To let FluentSecurity handle authorization globally within your application, open the Global.asax file and add the
HandleSecurityAttribute to the global filter collection within the
RegisterGlobalFilters method (make sure that you have imported the namespace FluentSecurity):
filters.Add(new HandleSecurityAttribute(), 0);
It is important to set the attribute's filter run order to 0 so that FluentSecurity can enforce security rules before anything else in the request pipeline is executed.
Furthermore, add the following security configuration shown below to the Application_Start method before the
RegisterGlobalFilters method is called — otherwise, FluentSecurity will throw an exception stating that no security rules have been specified:
// Tell FluentSecurity where to obtain the user authentication status from
The above example instructs FluentSecurity to call the specified
Func<bool> delegate – which is querying the
HttpContext.User.Identity.IsAuthenticated property used by ASP.NET Forms Authentication – to retrieve the current user's authentication status.
Note that, when you run the application, you will receive a
ConfigurationErrorsException — this is by design! By default, FluentSecurity throws that exception whenever an action method for which there is no security explicitly specified is called. If you don't like this feature, you can easily turn it off:
However, I strongly recommend not to ignore missing configurations, for the thrown exception prevents you from forgetting to secure action methods (or controllers) by accident.
Specifying Security Policies
So far, we have configured authentication information, but we haven't specified any authorization rules yet. FluentSecurity uses the concept of Policies to configure authorization rules for either entire controllers or single action methods.
To secure your
HomeController from unauthenticated access, add the following line to the configuration:
DenyAnonymousAccess extension method registers the
DenyAnonymousAccessPolicy for the entire
HomeController. If an unauthenticated user attempts to call any action methods living inside the controller, a
PolicyViolationException is thrown. An authenticated user, on the other hand, will pass.
You can also add the same policy to all controllers in your application:
// Secure all action methods of all controllers
// Make sure that users can still log on
configuration.For<AccountController>(ac => ac.LogOn()).Ignore();
The lambda expression
ac => ac.LogOn() restricts the
IgnorePolicy to the
LogOn action method. At that point of time, only parameterless methods can be selected, but a future release of FluentSecurity is likely to include support for parametrized methods.
In the current version 1.4.0, the following policies are available out of the box:
DelegatePolicy— The specified delegate must return true or a success result.
DenyAnonymousAccessPolicy— The user must be authenticated.
DenyAuthenticatedAccessPolicy— The user must be anonymous.
IgnorePolicy— All users are allowed.
RequireAllRolesPolicy— The user must be authenticated with all of the specified roles.
RequireRolePolicy— The user must be authenticated with at least one of the specified roles.
Implementing a Custom Policy
If none of the existing policies fulfills your needs, you can create your own policy by implementing the
ISecurityPolicy interface and with it its
Enforce method. The following example shows the implementation of a custom policy that restricts access to a controller to requests on weekends:
public class WeekendsOnlyPolicy : ISecurityPolicy
public PolicyResult Enforce(ISecurityContext context)
DateTime now = DateTime.Now;
bool isWeekend = now.DayOfWeek == DayOfWeek.Saturday
|| now.DayOfWeek == DayOfWeek.Sunday;
: PolicyResult.CreateFailureResult(this, "Access denied!");
Handling Policy Violations
When a policy is violated, FluentSecurity will throw a
PolicyViolationException. You can, of course, catch the exception regularly and do with it whatever you like. However, the cleaner approach would be to register a Policy violation handler which has to meet certain criteria:
- It has to implement the
IPolicyViolationHandlerinterface (a single
Handle methodaccepting a
PolicyViolationExceptionand returning an
- The handler name has to match the format
<PolicyName>ViolationHandler, since FluentSecurity uses a naming convention to locate the correct policy violation handler.
The recommended way to register custom policy violation handlers is by using an IoC container. Please refer to the documentation page for more information on how to create and register policy violation handlers using a dependency injection framework.
Testing Your Security Configuration
To ensure that your security rules are configured correctly, you can test them in a very readable, fluent way using the NuGet package
Given that you have encapsulated the security configuration in the
ConfigureFluentSecurity method of a separate
Bootstrapper class, possible expectations for the security configuration created before could look like the following:
var results = SecurityConfiguration.Current.Verify(expectations =>
expectations.Expect<AccountController>(ac => ac.LogOn()).Has<IgnorePolicy>();
bool isValidConfiguration = results.Valid();
Has extension method, there is also a
DoesNotHave version expecting that a certain policy is not assigned to an action method or a controller. For more information on how to test your security configuration, please have a look at the corresponding documentation page.
If you are interested in reading more about the project or its author, here are some interesting references: