Add Model Validation to Your ASP.NET Core Web API

If you’re used to creating traditional Web APIs in ASP.NET, you’ve probably become accustom to using System.ComponentModel.DataAnnotations such as the [Required] or [StringLength] data attributes. Using these in your models that are parameters to your API project would return the proper 400 response code when the input didn’t match the requirement.

In ASP.NET Core 2.0, this is no longer built in right out of the box, but setting it up is super easy! So if you don’t want to abstract your model validation in your business logic layer and want to get it out of the way right away when the request comes in, check this out.

There are only two steps to doing this:

  • Create an implementation of `IActionFilter`
  • Register this Filter class in your startup

Let’s start by creating a ValidatorActionFilter class:

ValidatorActionFilter.cs

/// <summary>
/// Filter for returning a result if the given model to a controller does not pass validation
/// </summary>
public class ValidatorActionFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (!filterContext.ModelState.IsValid)
        {
            filterContext.Result = new BadRequestObjectResult(filterContext.ModelState);
        }
    }

    public void OnActionExecuted(ActionExecutedContext filterContext)
    {

    }
}

What this does is check the current HttpContext.ModelState to make sure it is valid. This allows us to ensure we don’t have to make this check in each endpoint. Note, we don’t have to add anything in the OnActionExecuted method since this filter is used exclusively before the action is called.

In the OnActionExecuting call, if the model state is NOT valid, we need to return the 400 – Bad Request response as the result.

Now, let’s register this in our startup. We do this by applying it in the ConfigureServices call and specifically in the options of the AddMvc call:

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
    {
        options.Filters.Add(typeof(ValidatorActionFilter));
    });
}

And that’s it! So now your endpoints will return the proper response such as:

MyInputModel.cs

public class MyInputModel
{
    [Required]
    public string SomeRequiredValue { get; set; }
    public string SomeNotRequiredValue { get; set; }
}

MyController.cs

public class MyController : Controller
{
    public ActionResult DoSomeAction([FromBody]MyInputModel input)
    {
         return Ok("You did it"!)
    }
}

The request of:

{
    "someNotRequiredValue": "Hey"
}

Will return 400 - Bad Request - "The SomeRequiredValue field is required"

And the request of:

{
    "someRequiredValue": "Yo"
    "someNotRequiredValue": "Hey"
}

Will return 200 - Ok - "You did it!"

So go forth and validate those input models with ease!


If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

Advertisements

Adding a Simple Refresh Token to OAuth Bearer Tokens

If you’re using a .NET as your web platform and are looking to expand it to another platform such as mobile applications, and need to authenticate users from that external application, one of the best ways of going about it is through the use of OAuth Bearer Tokens.

James Randall has a great post here about getting started with the OAuth Bearer token Authentication. This post isn’t going to focus on getting started, but will use this example to expand upon.

Using Bearer (access) Tokens allows you to authenticate users without having to send their password through the pipes with each request. Using an access token in your header will let you authorize requests to your api as well as through SignalR or other web services.

Here is an example of the authorization header sent with a request to authorize a user:
“Authorize Bearer YOUR_ACCESS_TOKEN”

However, what happens when this token expires? Of course, you can set an outrageously long expiration date, but that is a security nightmare. You don’t want to store the users password locally to continuously send requests to get a new token. You also don’t want to require the user to re-login every time the token expires.

The solution? Refresh tokens! A refresh token will allow you to receive a new access token after it expires without sending the user’s password.

The first step is to create a RefreshTokenProvider that we can add during our Startup processing. Here is a simple Provider that will work for this example:


 public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider
 {
    private static ConcurrentDictionary<string, AuthenticationTicket> _refreshTokens = new ConcurrentDictionary<string, AuthenticationTicket>();

     public async Task CreateAsync(AuthenticationTokenCreateContext context)
     {
         var guid = Guid.NewGuid().ToString();

         // maybe only create a handle the first time, then re-use for same client
         // copy properties and set the desired lifetime of refresh token
         var refreshTokenProperties = new AuthenticationProperties(context.Ticket.Properties.Dictionary)
         {
             IssuedUtc = context.Ticket.Properties.IssuedUtc,
             ExpiresUtc = DateTime.UtcNow.AddYears(1)
         };
         var refreshTokenTicket = new AuthenticationTicket(context.Ticket.Identity, refreshTokenProperties);

         //_refreshTokens.TryAdd(guid, context.Ticket);
         _refreshTokens.TryAdd(guid, refreshTokenTicket);

         // consider storing only the hash of the handle
         context.SetToken(guid);
     }

     public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
     {
         AuthenticationTicket ticket;
         if (_refreshTokens.TryRemove(context.Token, out ticket))
         {
             context.SetTicket(ticket);
         }
     }

     public void Create(AuthenticationTokenCreateContext context)
     {
         throw new NotImplementedException();
     }

     public void Receive(AuthenticationTokenReceiveContext context)
     {
         throw new NotImplementedException();
     }
 }

We can now use this provider to add the setting to our Startup.Auth.cs:


 OAuthOptions = new OAuthAuthorizationServerOptions
 {
     TokenEndpointPath = new PathString("/Token"),
     Provider = new ApplicationOAuthProvider(PublicClientId, UserManagerFactory),
     AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
     AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(60),
     AllowInsecureHttp = true,
     RefreshTokenProvider = new SimpleRefreshTokenProvider() //REFERENCE TO PROVIDER

 };

Now we have our access token that expires every hour, and a refresh token that expires every year. The time-spans to use for both of these is completely up to you.

Now we can use requests like these with our external application-

Authorize with username and password:

Method: POST
Url: http://yourdomain/Token?username=yourusername&password=yourpassword&grant_type=password

Response:

{

"access_token":"VSt0JjzP-9PO6OFk-i_dp7Xs7RA4JTai_nv1FXxTiZ-iMoYjCt42Jw8eJqV66EouqAxnsHIUzDucHKSQUhEch9tftf_dNgi0pDKFUZn5UVJ0rybZ8keG4LjT2oI851D1OnE0Ij0KnEr5ox_RNFpYW5Srqj_4Uy4uYkhrOLKxo3TEt_nBFNhVsvTAxoY5ggDdTK_th945XzeZeXjRSX-j8clYJpaxAUmA-Z38qhbyXiq29wSZKswhloaHcIVIJDXe9Fhpfe1nM4IfJT5Lwy1tjYH4XIphd7UX_nprX4JEwlJUFENJE9E-Gq6y7deXQa7j3JXIg8YBtvcR0Mj0Fjxhj6Bdaq2hCE1Ot6KgZUxOzzRkiuJlkMoQgmg8T2MM6STfQnX-cEd328n6oYgYBxg34kLbi8NGSHiAKEtxcF8Fuj7gizMOCK91iaVQTf_7UsJIkW6KFGeGLz0MG8A71jj-kNjzSFApYGo6VCoQJqXzREY",

"token_type":"bearer",

"expires_in": 3600,

"refresh_token":"969c9b04-afe5-48a3-9353-62509f71e906",

"userName":"yourusername",

".issued":"Thu, 30 Apr 2015 02:21:08 GMT",

".expires":"Thu, 30 Apr 2015 02:22:08 GMT"

}

Authorize with username and refresh token:

Method: POST
Url: http://yourdomain/Token?username=yourusername&refresh_token=969c9b04-afe5-48a3-9353-62509f71e906&grant_type=refresh_token

Response:

{

"access_token":"SAPmj6kWat4KcwhASsTkkuQ0hxeIZaq4ztZBduHV_Mr-0SoxzQZ61ojdiXDtUIo_ptfbuzx5sA9_3-GpPWZhQ702qvAXdYSnMy_OUVVypfVkP-9mUsS7iR_4uFd67MFrSVEfQ4Er1Tm9AiFLC1j4kR7WjAmZgn6YuhU1Z3NNOFMu6UGEutJEWZte4mcnHinYKxskwVt_45DBGqEaLQ1OQoPhYwLTPGIhAcvsjiVLOxHCWp46bfYOyP5tVBoZxoaftuYyQfEgOkeU44TRWdGRZBh6vKWKdjWqa-qpy8fCNoJkwpSSjWYGEyhG4IkDyRCRGpCfMHP5rbP6dfaWAAchk7qQCmcia_vuEFoZWFWER6_LFe58avh_ZqfmJQhl7lVaM4z5SEKmKP4RPXgK32T4jQEqoisOGi66bcueLzRGmCsW2BlBnxPC1QloY_VQR8bEoCqK7_C0haMH7t30sJz_2Cz9CgnMnIjeyVhdcQsg_4U",

"token_type":"bearer",

"expires_in": 3600,

"refresh_token":"9a773700-0b48-411e-9138-1fc0e266d8a9",

"userName":"yourusername",

".issued":"Thu, 30 Apr 2015 02:21:08 GMT",

".expires":"Thu, 30 Apr 2015 02:22:08 GMT"

}

This is obviously very simplified and lacks typical uses with things like Client Ids and handling proper storage and error handling, but it will hopefully help you get started with your Authorization layer for any external applications through Web API.