Protect an ASP.NET Web API

In this guide we will create a simple OWIN project and add to it’s pipeline a simple web API and then add a middleware that provides authentication for the API methods.

The complete source code for the application that will be created in this guide is available on GitHub.

Note

If you already have an OWIN web API, you can advance some steps and check the section Protecting an existing web API directly.

Creating an OWIN web API

Warning

If you are using a different version or another editor/IDE, please keep in mind that this guide was build using Visual Studio 2017, and some features might be missing or in different places in different versions.

Creating the project

Open your Visual Studio and create a new project. In the Add New Project dialog choose the ASP.NET Web Application (.NET Framework) project type.

../_images/1_newproject.png

Add New Project dialog.

The Visual Studio will ask you for a template. Select Empty.

../_images/1_aspnet_template.png

Template selection dialog.

Note

For convenience, this guides assumes the project is configured to run on the port 4000. You can set this in your project accessing the project’s Properties, selecting the tab Web and changing the Project Url field to http://localhost:4000/.

Creating the Startup file

First, you must install the following NuGet package: Microsoft.Owin.Host.SystemWeb.

Note

More information on installing NuGet packages can be found in the NuGet Quickstart.

In the Solution Explorer, right-click over the project and select Add > OWIN Startup class. A dialog will ask for the class name; this guide uses the name Startup.

Note

This menu item might not be available if using older versions of Visual Studio or other editors. More information on creating OWIN Startup files can be found in the respective ASP.NET Documentation.

Adding the WebApi middleware to the pipeline

Please, install the following NuGet package: Microsoft.AspNet.WebApi.Owin.

Then, you need to add this to Configure in the Startup file:

1
2
3
4
5
6
public void Configuration(IAppBuilder app)
{
    var httpConfig = new HttpConfiguration();
    httpConfig.MapHttpAttributeRoutes();
    app.UseWebApi(httpConfig);
}

Creating a simple controller

Create a new file named DemoController.cs and add the following code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Web;
using System.Web.Http;

namespace AuthExamples.ProtectedWebApi.Controllers
{
    [RoutePrefix("demo")]
    public class DemoController : ApiController
    {
        [HttpGet, Route("test")]
        public IHttpActionResult Test()
        {
            return Ok("Hello World");
        }
    }
}

Protecting an existing web API

Denying Unauthenticated Requests

If you alreay have a working web API that works on top of OWIN (e.g. the simple one that was build in the previous steps), you can avoid unwanted access by adding the attribute [Authorize] immediately before the action (method) or controller (class) that you want to protect. This will deny all unauthenticated access.

As an example, the simple controller created in the previous topics could become the following:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[Authorize]
[RoutePrefix("demo")]
public class DemoController : ApiController
{
    [HttpGet, Route("test")]
    public IHttpActionResult Test()
    {
        var claims = (User as ClaimsPrincipal).Claims;
        var result = claims.Select(x => new { x.Type, x.Value });
        return Ok(result);
    }
}

Note

For more information on using the Authorize attribute, check the proper documentation on MSDN.

Now, a request to the address http://localhost:4000/demo/test will be responded with the HTTP status code 401, that means Unauthorized, because authentication info was not provided in the request.

Accepting Bearer Tokens in the Authorization Header

The most common and recommended way to present a token to a protected API is to send a Bearer Token through the Authorization header.

After the client of your API obtains a token by using the one of the OIDC flows, it should present the token (e.g. tokenvalue0001) to your API in the Authorization request header field in the following format: Bearer tokenvalue0001.

Note

More info on Bearer Tokens are available in the RFC 6750.

Warning

The package IdentityServer3.AccessTokenValidation does not integrate with the ASP.NET Core 2.0 platform.

In order to accept and process the provided token, you must reference in your API the package IdentityServer3.AccessTokenValidation and add the following highlighted lines into the startup class:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public void Configuration(IAppBuilder app)
{
    const string AUTHORITY = "https://login-dev.sdasystems.org/";
    const string SCOPE_NAME = "demoapi";
    const string SCOPE_SECRET = "secret123"

    var idsrvAuthOptions = new IdentityServerBearerTokenAuthenticationOptions
    {
        Authority = AUTHORITY,
        ClientId = SCOPE_NAME,
        ClientSecret = SCOPE_SECRET,
        RequiredScopes = new[] { SCOPE_NAME },

        // validates the token in the server in order to provide single-sign-off
        ValidationMode = ValidationMode.ValidationEndpoint,
    };
    app.UseIdentityServerBearerTokenAuthentication(idsrvAuthOptions);

    var httpConfig = new HttpConfiguration();
    httpConfig.MapHttpAttributeRoutes();
    app.UseWebApi(httpConfig);
}

This way, when your API is called specifying a Bearer Token, your API will make a request to the IATec Authentication Server in order to “introspect” the token, that is, to retrieve the unmasked value for the token.

If the token is valid, the current thread Principal will be set, and therefore the [Authorize] attribute will not abort the request.

Warning

If you intend to call this API from a browser, you might need to activate CORS support in the API. Such is not in the scope of this guide. You may try the following package: Microsoft.Owin.Cors.

Retrieving token information in the API

By default, all tokens issued by the IATec Authentication Server are by reference, it means that its value is masked. As stated previously, on each API request, another request will be made to the authentication server and the resulting information will be stored in the current thread Principal.

Note

For more information about the ASP.NET Principal, check the official documentation on MSDN.

In order to access this, you can use the controller’s User property. Type-casting it to a ClaimsPrincipal will enable you to retrieve the Access Token claims.

The following modification on your controller exemplifies this. When requested,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[Authorize]
[RoutePrefix("demo")]
public class DemoController : ApiController
{
    [HttpGet, Route("test")]
    public IHttpActionResult Test()
    {
        var claims = (User as ClaimsPrincipal).Claims;
        var result = claims.Select(x => new { x.Type, x.Value });
        return Ok(result);
    }
}

Note

For samples values of Access Tokens, check the section Access Tokens.