Skip to main content

ASP.NET Core middleware

Aserto Authorization middleware for ASP.NET core.

Overview#

This package provides allows .NET ASP applications to use Aserto as the Authorization provider.

Installation#

Aserto.AspNetCore.Middleware is provided as a NuGet package.

It can be installed:

  • Using Package Manager:
Install-Package Aserto.AspNetCore.Middleware
  • Using .NET CLI
dotnet add package Aserto.AspNetCore.Middleware

Configuration#

The following configuration settings are required for Aserto.AspNetCore middleware. You can add them to your appsettings.json:

// appsettings.json
"Aserto": {    "AuthorizerApiKey": "YOUR_AUTHORIZER_API_KEY",    "TenantID": "YOUT_ASERTO_TENANTID",    "PolicyID": "YOUR_ASERTO_POLICY_ID",    "PolicyRoot": "YOUR_POLICY_ROOT"}

This settings can be retrieved from the Policy Settings page of your Aserto account.

The middleware accepts the following optional parameters:

Parameter nameDefault valueDescription
EnabledtrueEnables or disables Aserto Authorization
ServiceUrl"https://authorizer.prod.aserto.com:8443"Sets the URL for the authorizer endpoint.
Decision"allowed"The decision that will be used by the middleware when creating an authorizer request.

Identity#

To determine the identity of the user, the middleware checks the following Claim types:

NameDescriptionURI
E-Mail AddressThe e-mail address of the userhttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
NameThe unique name of the userhttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
Name IdentifierThe SAML name identifier of the userhttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier

These can be overwritten by passing other claim types to the AsertoDecisionRequirement:

// Startup.cs
    public void ConfigureServices(IServiceCollection services)    {        //..
        services.AddAuthorization(options =>        {            options.AddPolicy("Aserto", policy =>             policy.Requirements.Add(new AsertoDecisionRequirement(new List<string>             {                 "mytype1",                 "mytype2"             })));        });
    //..    }

URL path to policy mapping#

By default, when computing the policy path, the middleware:

  • converts all slashes to dots
  • converts any character that is not alpha, digit, dot or underscore to underscore
  • converts uppercase characters in the URL path to lowercases

This behavior can be overwritten by providing a custom function to the PolicyPathMapper AsertoAuthorization option:

// Startup.cs
public void ConfigureServices(IServiceCollection services){   //..
   // Adds the Aserto Authorization service   services.AddAsertoAuthorization(options =>   {      Configuration.GetSection("Aserto").Bind(options));      options.PolicyPathMapper = (policyRoot, httpRequest) =>      {          return "custom.policy.path";      };   }   //..  }

Quickstart: Aserto Authorization on a ASP.NET Core MVC application#

Prerequisites

Creating and setting up the project

In this quickstart we want to add API access support using Aserto Authorization using Auth0 Authentication.

Setting up the ASP.NET Core application

First create a directory for the application, then use a template to create a ASP.NET application:

md dotnetmvccd dotnetmvc
md srccd src
dotnet new mvc -n QuickstartMVC

This will create a .NET MVC application that exposes the following endpoints and http methods:

  • GET on "/"
  • GET on "/Home/Privacy"

You can run the app using the following command:

dotnet run --project dotnetmvc/src/QuickstartMVC/QuickstartMVC.csproj

Adding authentication

note

This guide assumes that you already have an Auth0 account setup up and an Auth0 Regular Web Application created.

For authentication we will use the Cookie and OpenID Connect(OIDC) authentication middleware. For this you need to add Microsoft.AspNetCore.Authentication.OpenIdConnect package to your application. This can be done using NuGet by running the following command in your project directory:

dotnetmvc\src\QuickstartMVC> dotnet add package Microsoft.AspNetCore.Authentication.OpenIdConnect

To enable authentication, you will need to update the ConfigureServices in your Startup class and:

  1. Configure Cookie for HTTPS
  2. Make a call to the AddAuthentication method and set the:
    • DefaultAuthenticateScheme to CookieAuthenticationDefaults.AuthenticationScheme
    • DefaultSignInScheme to CookieAuthenticationDefaults.AuthenticationScheme
    • DefaultChallengeScheme to CookieAuthenticationDefaults.AuthenticationScheme
  3. Add the CookieExtension
  4. Configure the OIDC authentication handler by adding a call to AddOpenIdConnect and setting the:
    • Authority to the Auth0 Domain
    • ClientId your Auth0 Client ID
    • ClientSecret to your Auth0 Client Secret
    • ResponseType to OpenIdConnectResponseType.Code
    • ClaimsIssuer to "Auth0"
  5. Handle the logout redirection

Below you've got a code sample that enable OIDC authentication:

// Startup.cs
//..using Microsoft.AspNetCore.Http;using Microsoft.AspNetCore.Authentication.Cookies;using Microsoft.IdentityModel.Protocols.OpenIdConnect;using Microsoft.AspNetCore.Authentication.OpenIdConnect;//..
        // This method gets called by the runtime. Use this method to add services to the container.        public void ConfigureServices(IServiceCollection services)        {                // Cookie configuration for HTTPS            services.Configure<CookiePolicyOptions>(options =>            {                options.MinimumSameSitePolicy = SameSiteMode.None;            });                    // Add authentication services            services.AddAuthentication(options => {                options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;                options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;                options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;            })            .AddCookie()            .AddOpenIdConnect("Auth0", options => {                // Set the authority to your Auth0 domain                var domain = "YOUR_AUTH0_DOMAIN";                options.Authority = $"https://{domain}";
                // Configure the Auth0 Client ID and Client Secret                options.ClientId = "YOUR_AUTH0_CLIENT_ID";                options.ClientSecret = "YOUR_AUTH0_CLIENT_SECRET";
                // Set response type to code                options.ResponseType = OpenIdConnectResponseType.Code;
                // Configure the scope                options.Scope.Clear();                options.Scope.Add("openid");                options.Scope.Add("profile");
                // Set the callback path, so Auth0 will call back to http://localhost:5001/callback                // Also ensure that you have added the URL as an Allowed Callback URL in your Auth0 dashboard                options.CallbackPath = new PathString("/callback");
                // Configure the Claims Issuer to be Auth0                options.ClaimsIssuer = "Auth0";
                options.Events = new OpenIdConnectEvents                {                    // handle the logout redirection                    OnRedirectToIdentityProviderForSignOut = (context) =>                    {                        var logoutUri = $"https://{domain}/v2/logout?client_id={options.ClientId}";
                        var postLogoutUri = context.Properties.RedirectUri;                        if (!string.IsNullOrEmpty(postLogoutUri))                        {                            if (postLogoutUri.StartsWith("/"))                            {                                // transform to absolute                                var request = context.Request;                                postLogoutUri = request.Scheme + "://" + request.Host + request.PathBase + postLogoutUri;                            }                            logoutUri += $"&returnTo={ Uri.EscapeDataString(postLogoutUri)}";                        }
                        context.Response.Redirect(logoutUri);                        context.HandleResponse();
                        return Task.CompletedTask;                    }                };            });
            services.AddControllersWithViews();        }
//..

To add the authentication middleware to the middleware pipeline add a call to the UseAuthentication method in your Startup's Configure method:

// Startup.cs
//..
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)        {            if (env.IsDevelopment())            {                app.UseDeveloperExceptionPage();            }            else            {                app.UseExceptionHandler("/Home/Error");                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.                app.UseHsts();            }            app.UseHttpsRedirection();            app.UseStaticFiles();
            app.UseRouting();
            // Adds the Authentication middleware to the middleware pipeline,            app.UseAuthentication();
            app.UseEndpoints(endpoints =>            {                endpoints.MapControllerRoute(                    name: "default",                    pattern: "{controller=Home}/{action=Index}/{id?}");            });        }//..

To handle authentication requests, we're going to add a new controller that can mange login and logout requests. This new controller is going to be named AccountController and needs to be added under the Controller folder:

// Controllers/AccountController.cs
using Microsoft.AspNetCore.Authentication;using Microsoft.AspNetCore.Authentication.Cookies;using Microsoft.AspNetCore.Http;using Microsoft.AspNetCore.Mvc;using System.Threading.Tasks;
public class AccountController : Controller{    public async Task Login(string returnUrl = "/")    {        await HttpContext.ChallengeAsync("Auth0", new AuthenticationProperties() { RedirectUri = returnUrl });    }
    public IActionResult AccessDenied()    {        return View();    }
    public async Task Logout()    {        await HttpContext.SignOutAsync("Auth0", new AuthenticationProperties        {            // Indicate here where Auth0 should redirect the user after a logout.            // Note that the resulting absolute Uri must be added to the            // **Allowed Logout URLs** settings for the app.            RedirectUri = Url.Action("Index", "Home")        });        await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);    }}

Next we're going to add the login and logout buttons to the shared layout. To do this, you need to edit the Views/Shared/_Layout.cshtml and add the following lines:

@* Views/Shared/_Layout.cshtml *@
<body>    <header>        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">            <div class="container">
            @* ... Code omitted for brevity ... *@
                <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">                    <ul class="nav navbar-nav navbar-right">                        @if (User.Identity.IsAuthenticated)                        {                            <li><a id="qsLogoutBtn" asp-controller="Account" asp-action="Logout">Logout</a></li>                        }                        else                        {                            <li><a id="qsLoginBtn" asp-controller="Account" asp-action="Login">Login</a></li>                        }                    </ul>                </div>            </div>        </nav>    </header></body>
@* ... Code omitted for brevity ... *@
note

The callback url(https://localhost:5001/callback) configured in the ConfigureServices method of Startup.cs needs to be set as an Allowed Callback URL in Auth0. Similarly the logout url(https://localhost:5001/) needs to be set in the Allowed Logout URLs list.

Creating policy for QuickstartMVC

To add authentication to your backend application, first you need to create an Aserto policy that contains the authentication rules for the APIs provided by the application. For the QuickstartMVC application created earlier, the policy folder structure should look like this:

.โ”œโ”€โ”€ Makefileโ”œโ”€โ”€ README.mdโ””โ”€โ”€ src    โ”œโ”€โ”€ .manifest    โ””โ”€โ”€ quickstartmvc        โ”œโ”€โ”€ account        โ”‚ย ย  โ”œโ”€โ”€ login        โ”‚ย ย  โ”‚ย ย  โ””โ”€โ”€ get.rego        โ”‚ย ย  โ””โ”€โ”€ logout        โ”‚ย ย      โ””โ”€โ”€ get.rego        โ”œโ”€โ”€ get.rego        โ””โ”€โ”€ home            โ”œโ”€โ”€ privacy            โ”‚ย ย  โ””โ”€โ”€ get.rego            โ””โ”€โ”€ profile                โ””โ”€โ”€ get.rego

Where the ./src/.manifest file contains our policy roots:

{    "roots": ["quickstartmvc"]}

the ./src/quickstartmvc/profile/get.rego policy file contains rules for the GET http method on /profile URL path:

package quickstartmvc.GET.home.profile
default allowed = false
allowed {    caller = input.user
    caller.identities[i].verified == true}

This policy allows access to /profile on GET only to users that have their identities verified.

On the rest of the endpoints we're going to allow anonymous access, so the rest of the *.rego files will contain:

allowed = true
note

If you've created the policy from the default template, you need to create a tag in order to push it to Aserto.

Adding Aserto authorization To enable Aserto authorization you need to add a dependency to the Aserto dotnet middleware:

$dotnetmvc/src/QuickstartMVC$ dotnet add package Aserto.AspNetCore.Middleware

Configure the Authorizer API Key, Tenant ID, Policy root and Policy ID in the appsettings.json

"Aserto": {    "AuthorizerApiKey": "YOUR_AUTHORIZER_API_KEY",    "TenantID": "YOUT_ASERTO_TENANTID",    "PolicyID": "YOUR_ASERTO_POLICY_ID",    "PolicyRoot": "quickstartmvc"}

Configure the Aserto Authorization Service and add an authorization policy in the ConfigureServices method of Startup.cs

// Startup.cs
//..using Microsoft.AspNetCore.Authorization;using Aserto.AspNetCore.Middleware.Policies;using Aserto.AspNetCore.Middleware.Extensions;//..
        public void ConfigureServices(IServiceCollection services)        {            //.. Code omitted for brevity
            // Adds the Aserto Authorization service            services.AddAsertoAuthorization(options => Configuration.GetSection("Aserto").Bind(options));                        // Adds the Aserto policy and configures it as the default Authorization policy            services.AddAuthorization(options =>            {                // User is authenticated via a cookie.                var policy = new AuthorizationPolicyBuilder(CookieAuthenticationDefaults.AuthenticationScheme);                policy.AddRequirements(new AsertoDecisionRequirement());                options.DefaultPolicy = policy.Build();            });
            services.AddControllersWithViews();        }//..

Add the Authorization middleware to the middleware pipeline by adding a call to the UseAuthorization method in your Startup's Configure method

// Startup.cs
//..        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)        {            //.. Code omitted for brevity
            app.UseAuthentication();            app.UseAuthorization();
            app.UseEndpoints(endpoints =>            {                endpoints.MapControllerRoute(                    name: "default",                    pattern: "{controller=Home}/{action=Index}/{id?}");            });        }//..

Next you need to create the Profile view that will display the claims of a logged in user. To do this, add a new file Profile.cshtml under the Views/Home/ folder:

@* Views/Home/Profile.cshtml *@
@using Microsoft.AspNetCore.Authentication
@{    ViewData["Title"] = "Profile";}<h1>@ViewData["Title"]</h1>
<p>User profile</p>@if (Context.User.Identity.IsAuthenticated){    <h2>Claims</h2>
    <dl>        @foreach (var claim in User.Claims)        {            <dt>@claim.Type</dt>            <dd>@claim.Value</dd>        }    </dl>
    <h2>Properties</h2>
    <dl>
        @foreach (var prop in (await Context.AuthenticateAsync()).Properties.Items)        {            <dt>@prop.Key</dt>            <dd>@prop.Value</dd>        }

    </dl>}else{    <h2>User not authenticated.</h2>}

Add a new navigation bar that will call the view in Views/Shared/_Layout.cshtml:

@* Views/Shared/_Layout.cshtml *@
<body>    <header>        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">            <div class="container">
                <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
                    @* ... Code omitted for brevity ... *@
                    @if (User.Identity.IsAuthenticated)                    {                        <li class="nav-item">                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Profile">Profile</a>                        </li>                    }                </div>
            @* ... Code omitted for brevity ... *@            </div>        </nav>    </header></body>
@* ... Code omitted for brevity ... *@

And finally, add the Profile action in the HomeController.cs:

// Controllers/HomeController.cs
//..using Microsoft.AspNetCore.Authorization;//..
        [Authorize]        public IActionResult Profile()        {            return View();        }
//..

You can find this and other examples on how to use the Aserto .NET Core Middleware here: https://github.com/aserto-dev/aserto-dotnet/tree/main/examples