McAuthz 0.3.0

dotnet add package McAuthz --version 0.3.0
                    
NuGet\Install-Package McAuthz -Version 0.3.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="McAuthz" Version="0.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="McAuthz" Version="0.3.0" />
                    
Directory.Packages.props
<PackageReference Include="McAuthz" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add McAuthz --version 0.3.0
                    
#r "nuget: McAuthz, 0.3.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package McAuthz@0.3.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=McAuthz&version=0.3.0
                    
Install as a Cake Addin
#tool nuget:?package=McAuthz&version=0.3.0
                    
Install as a Cake Tool

McAuthz

A library that allows using policy based rules for authorization in asp.net core.

Kinds of Authorization

Web api requests can be evaluated by policy in two ways: by principal and http method (GET, POST, etc) or by principal and resource type.


                       [ Authorization Middleware ]    [            Endpoint Middleware           ]
__________             ____________________________    ______________    __________________________
|        |             |   Authorization Policy   |    | Controller |    | Resource Authorization |
| Client | Request  -> | Inspect: Identity/Method | -> |            | -> |                        |
|        | Response <- |                          | <- |            | <- |    Inspect objects     |
‾‾‾‾‾‾‾‾‾‾             ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾    ‾‾‾‾‾‾‾‾‾‾‾‾‾‾    ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
                       [     Request Policies     ]    [             Resource Policies            ]

Policies

Policies are evaluated at runtime as predicates created from expression trees. If that's new to you, think of it as a Linq query that's generated at runtime. These predicates are generated by the McRule libary, see that package's Readme for information on the supported pattern matching grammar.

The general idea is that these predicates inspect a property on an object and return true or false. RequstPolicy and ResourcePolicy are the two general types and contain a List<Requirement> that encodes the rules.

ResourcePolicies include a TargetType property to determine scope. Requirements are usually a list of PropertyRequirements, as they evaluate against the properties of a model object.

If the simple language in this readme makes seem like a bot wrote this, that's not it. Usage of the library was intended to be simple, so the parts a developer or operator touches are simple.

McAttributes is the example app used while developing this library. It comes from something else I needed for work but is just a simple web api + razor pages app with a SQLite database. Test data is included.

Usage of the library assumes your application implementes IRuleProvider. There's a naive example in Program.cs of the example app, but you'll want to implement your own to load policies as data. You'll also want to examine service setup and middleware usings. Look to SayController.cs and SayKVController.cs for examples of ResourcePolicy usage.

Request Policies

These include a Route and Action property, which is used to determine whether it should be included for evaluation at authorization time. Requirement types for these policies generaly includes ClaimRequirement and RoleRequirements, both of these evaluate against the ClaimsPrincipal. RequestPolicy failure short circuits the pipeline and returns a 401 Unauthorized response.

Resource Policies

Resource policies evaluate at the middleware endpoint as part of the controller's processing. These policies are selected by inferring tyoe of the model being processed by a controller. The idea was inspired by the Resource Based Authorization articles that became part of Andrew Lock's book ASP.NET Core In Action. It's great, go buy it.

Filter Policies

These types are intended to be used as predicates in a query for a given record type. This allows for defining policies that restrict a subjects view of a database by appending a predicate to the user provided query.

The business use case I have is for providing HR information to multiple busines organizations. Each org has their own IT staff that automate processes with this data, some IT units are shared by mutiple organizations, others are not. The overall view of the data itself should be limited based on who's asking.

This is what a couple policies might look like for a shared services IT administrators accessing personell records:

var policySet = new [] {
    new FilterPolicy
    {
        Name = "SuperGeek IT.Admin users in the SharedServices org can access Customer A records.",
        TargetType = typeof(PersonellRecord).Name,
        Requirements = new List<Requirement> {
            new RoleRequirement("IT.Admin"),
            new ClaimRequirement("Agency", "SuperGeek"),
            new ClaimRequirement("Organization", "SharedServices"),
            new PropertyRequirement("Agency", "~Customer A")
        };
    }
    , new FilterPolicy {
        Name = "SuperGeek IT.Admin users in the SharedServices org can access Customer B records.",
        TargetType = typeof(PersonellRecord).Name,
        Requirements = new List<Requirement> {
            new RoleRequirement("IT.Admin"),
            new ClaimRequirement("Agency", "SuperGeek"),
            new ClaimRequirement("Organization", "SharedServices"),
            new PropertyRequirement("Agency", "~Customer B")
        };
    }
};

The Requirements stated include role, claim and property requirements. Role and claim requirements are matched against the principal making a request, property requirements are what become the predicate. Requirements inside a single policy are joined with an AND operator, while policies are joined with an OR. The conjoined policy set would end up looking something like:

x => (
    ((x.Agency != null) 
        AndAlso x.Agency.Equals("Customer A", CurrentCulture)) 
    OrElse Invoke(x => 
        ((x.Agency != null) 
            AndAlso x.Agency.Equals("Customer B", CurrentCulture)), x)
)

This is the generated expression tree passed to a ToString(). It's not quite how you'd write it by hand, 'x' for the parameter in multiple nested closures is a bit odd, but it works. The policy iteself generates an expression tree which is assembled recursively and using the same parameter name simplifies that. An important point about the expression is that it can be handed to Linq and EF will generate complementary SQL. Simply return return db.Set.Where<User>(filterExpression) from your controller and EF takes it from there. For bonus points, if you're using OData with asp.net core, the user's query is applied after returning from the controller so there's no additional work on your part.

Note: Case insensitive string matching for SQL generated queries depends on your backend database, out of the box. PostgreSQL for instance, is case sensitive by default. In order for case-insensitive matches to generate propery with the underlying McRule library, an ExpressionGenerator class must be implemented and used when generating the expression, example here. There isn't yet a way to inject those with this authorization library but it's on my TODO list.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.3.0 230 1/31/2025