The State of Security in ASP.NET 5 and MVC 6: Claims & Authentication

Disclaimer: Microsoft announced the roadmap for ASP.NET 5 yesterday – the current release date of the final version is Q1 2016. Some details of the features and APIs I mention will change between now and then. This post is about beta 5.

Claims
I started talking about claims-based identity back in 2005. That was the time when Microsoft introduced a new assembly to the .NET Framework called System.IdentityModel. This assembly contained the first attempt of introducing claims to .NET, but it was only used by WCF and was a bit over-engineered (go figure). The claims model was subsequently re-worked by the WIF guys a couple of years later (kudos!) and then re-integrated into .NET with version 4.5.

Starting with .NET 4.5, every built-in identity/principal implementation was based on claims, essentially replacing the 12+ years old antiquated IIdentity/IPrincipal interfaces. Katana – but more importantly ASP.NET 5 is the first framework that now uses ClaimsPrincipal and ClaimsIdentity as first class citizens – identities are now always based on claims – and finally – no more down-casting!

HttpContext.User and Controller.User are now ClaimsPrincipals – and writing the following code feels as natural as it should be:

var email = User.FindFirst(“email”);

This might not seem like a big deal – but given that it took almost ten years to get there, shows just how slow things are moving sometimes. I also had to take part in a number of discussions with people at Microsoft over the years to convince them that this is actually the right thing to do…

Authentication API
Another thing that ASP.NET was missing is a uniform authentication API – this was fixed in Katana via the IAuthenticationManager and was pretty much identically brought over to ASP.NET 5.

AuthenticationManager hangs off the HttpContext and is a uniform APIs over the various authentication middleware that do the actual grunt work. The major APIs are:

  • SignIn/SignOut
    Instructs a middleware to do a signin/signout gesture
  • Challenge
    Instructs a middleware to trigger some external authentication handshake (this is further abstracted by the new ChallengeResult in MVC 6)
  • Authenticate
    Triggers validation of an incoming credential and conversion to claims
  • GetAuthenticationSchemes
    Enumerates the registered authentication middleware, e.g. for populating a login UI dynamically

Authentication Middleware
The actual authentication mechanisms and protocols are implemented as middleware. If you are coming from Katana then this is a no brainer. If your background is ASP.NET.OLD think of middleware as HTTP modules – just more flexible and lightweight.

For web UIs the following middleware is included:

  • Cookie-based authentication (as a replacement for good old forms authentication or the session authentication module from WIF times)
  • Google, Twitter, Facebook and Microsoft Account
  • OpenID Connect

WS-Federation is missing right now. It is also worth mentioning that there is now a generic middleware for OAuth2-style authentication (sigh). This will make it easier to write middleware for the various OAuth2 dialects without having to duplicate all the boilerplate code and will make the life of these guys much easier.

Wiring up the cookie middleware looks like this:

app.UseCookieAuthentication(options =>
{
    options.LoginPath = new PathString(“/account/login”);
    options.AutomaticAuthentication = true;
    options.AuthenticationScheme = “Cookies”;               
});

The coding style is a little different – instead of passing in an options instance, you now use an Action<Option>. AuthenticationType has been renamed to AuthenticationScheme (and the weird re-purposing of IIdentity.AuthenticationType is gone for good). All authentication middleware is now passive – setting them to active means setting AutomaticAuthentication to true.

For signing in a user, you create the necessary claims and wrap them in a ClaimsPrincipal. Then you call SignIn to instruct the cookie middleware to set the cookie.

var claims = new List<Claim>
{
    new Claim(“sub”, model.UserName),
    new Claim(“name”, “bob”),
    new Claim(“email”, “bob@smith.com”)
};

var id = new ClaimsIdentity(claims, “local”, “name”, “role”);
Context.Authentication.SignIn(“Cookies”, new ClaimsPrincipal(id));

Google authentication as an example looks like this:

app.UseGoogleAuthentication(options =>
{
    options.ClientId = “xxx”;
    options.ClientSecret = “yyy”;

    options.AuthenticationScheme = “Google”;
    options.SignInScheme = “Cookies”;
});

The external authentication middleware implements the authentication protocol only – and when done – hands over to the middleware that does the local sign-in. That’s typically the cookie middleware. For this purpose you set the SignInScheme to the name of the middleware that should take over (this has been renamed from SignInAsAuthenticationType – again clearly an improvement).

Also the pattern of having more than one cookie middleware to be able to inspect claims from external authentication systems before turning them into a trusted cookie still exists. That’s probably a separate post.

For web APIs there is only one relevant middleware – consuming bearer tokens. This middleware has support for JWTs out of the box and is extensible to use different token types and different strategies to convert the tokens to claims. One notable new feature is support for OpenID Connect metadata. That means if your OAuth2 authorization server also happens to be an OpenID Connect provider with support for a discovery document (e.g. IdentityServer or Azure Active Directory) the middleware can auto-configure the issuer name and signing keys.

One thing that is “missing” when coming from Katana, is the OAuth2 authorization server middleware. There are currently no plans to bring that forward. IdentityServer can be a replacement for that. I will dedicate a separate blog post to that topic.

Summary
If you are coming from Katana, this all does not look terribly new to you. AuthenticationManager and authentication middleware works almost identical. Learning that, was no waste of time.

If you are coming from plain ASP.NET (and maybe even WIF or DotNetOpenAuth) this all works radically different under the covers and is really only “conceptually compatible”. In that case you have quite a lot of new tech to learn to make the jump to ASP.NET 5.

Unfortunately (as always) the ASP.NET templates are not very helpful in learning the new features. You either get an empty one, or the full-blown-all-bells-and-whistles-complexity-hidden-by-extensions-method-over-more-abstractions version of that. Therefore I created the (almost) simplest possible cookie-based starter template here. More to follow.

This entry was posted in .NET Security, ASP.NET, IdentityServer, OAuth, OpenID Connect, WebAPI. Bookmark the permalink.

24 Responses to The State of Security in ASP.NET 5 and MVC 6: Claims & Authentication

  1. frankfajardo says:

    Excellent update. It is nice to read a summary of a huge change as this. Thanks Dominick!

  2. Sucks they’re removing OAuth2 Authz server middleware. Identity Server is excellent and I use it for many projects but for certain small/tiny apps I enjoyed rolling with more basic, built-in implementations.

  3. Jeff Putz says:

    This is a fantastic post, especially without any substantially correct documentation available yet. I do wonder if there’s a way to segment the cookie’d login to specific parts of the app, either on a controller or area basis (they did keep areas in MVC6, right?).

    • Yes – the new Authorize attribute allows that.

      • Jeff Putz says:

        Is that the AuthorizeFilter in MVC? I noticed how the inline docs on CookieAuthenticationOptions for the CookieName property imply this is possible: “Determines the cookie name used to persist the identity. The default value is “.AspNet.Cookies”. This value should be changed if you change the name of the AuthenticationScheme, especially if your system uses the cookie authentication middleware multiple times.” I get how you would wireup multiple middleware instances, but not sure I see how you determine which one to use on the other end.

  4. yes – You can specify the active authentication scheme on the filter – this then maps to a named middleware.

    Haven’t tried it myself.

  5. Andrés Romero says:

    Hi, great help! I used to do claimstransformation like this:
    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
    SlidingExpiration = false,
    CookieName = “xxxxx”,
    ExpireTimeSpan = TimeSpan.FromHours(2),
    Provider = new CookieAuthenticationProvider
    {
    OnResponseSignIn = ctx =>
    {
    ctx.Identity = TransformClaims(ctx); //my custom claimstransformation
    }
    }
    });

    app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions
    {
    MetadataAddress = ConfigurationManager.AppSettings.Get(“WsFederationMetadataAddress”),
    Wtrealm = ConfigurationManager.AppSettings.Get(“WsFederationRealm”),
    UseTokenLifetime = false,
    SignOutWreply = ConfigurationManager.AppSettings.Get(“WsFederationRealm”),
    }
    );
    This allowed me to renew the claims every two hours or the time i wanted. In aspnet rc1 i’m trying to do it like this:

    app.UseCookieAuthentication(options =>
    {
    options.Events = new CookieAuthenticationEvents
    {
    OnSignedIn = ctx =>
    {
    //some custom claims transformation
    ctx.HttpContext.User.AddIdentity(TransformClaims(ctx));
    return Task.FromResult(null);
    }
    };
    options.AutomaticAuthenticate = true;
    options.SlidingExpiration = false;
    options.CookieName = “xxxxx”;
    options.ExpireTimeSpan = TimeSpan.FromMinutes(5);

    });

    app.UseOpenIdConnectAuthentication(options =>
    {
    options.AutomaticChallenge = true;
    options.ClientId = “xxxxxxxx”;
    options.Authority = “xxxxxx”;
    options.PostLogoutRedirectUri = “https://localhost:44328/”;
    options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.UseTokenLifetime = false;

    });

    But i can´t set the identity or claims inside CookieAuthentication like i used to do. Which is the right way to do it having control of the time interval in which the claims are renewed. Thanks!!

  6. arghya says:

    How do we use RefreshTokenprovider with OpenIdConnectOptions class for OpenIdConnectServer? Is there any other class that allows us to generate Refresh Tokens? The .UseOAuthAuthorizationServer extension method seems to be no longer supported.

  7. arghya says:

    Thanks Dominick, for your reply. Your docs did help a lot.However, I am using native Katana and have found a way to use refresh token and renew access tokens using OpenIdConnectServer provider class . I think AuthenticationTicket plays an important role here, to renew access tokens from an existing refresh token.

  8. Steve Jackson says:

    I assume the below code goes in the Controller? However, my “Context” object doesn’t have an “Authentication” association. What is the fully qualified namespace for Context, and how am I to access it in my controller? Thank you.

    var claims = new List
    {
    new Claim(“sub”, model.UserName),
    new Claim(“name”, “bob”),
    new Claim(“email”, “bob@smith.com”)
    };

    var id = new ClaimsIdentity(claims, “local”, “name”, “role”);
    Context.Authentication.SignIn(“Cookies”, new ClaimsPrincipal(id));

    • It’s been renamed to HttpContext

      • Steve Jackson says:

        Awesome, thanks! I finally noticed your example project – that did it for me. You’ve brought me to a solution after days of searching – I never thought that I would get there. I wonder if MS recognizes the impact to the developer community for even small changes, much less sweeping changes such as these.

  9. Dan says:

    Hi Dominick,
    Are you going to create any course for Pluralsight on this subject and if yes how soon?
    Is it much different from web api course?
    Just starting on a new project with asp.net 5 replacing web api

  10. Ryan says:

    Final link in the post 404s :(

  11. Ross says:

    Hi Dominick
    I am coming from WIF so I am one of the ones with quite a bit to learn.

    You mention that
    “For web APIs there is only one relevant middleware – consuming bearer tokens”

    Could you elaborate a little more on what middleware you are referring to and if it might be suitable for an internal LOB application with user account data on premises as well.

    • Well . it is the JwtBearer middleware. If it is all purely Windows intranet you might get aways with Windows authentication – but that’s like duct tape going forward.

  12. guest says:

    Any idea, why the bearer token that comes back sometimes(not always) split into multiple lines with long trailing spaces and then when you use it to query the API , results in a 401 error? Even if you try to remove the trailing spaces between multiple lines, it doesn’t help.I have used fiddler to get the token back and I see it happening.Any solution for this

  13. guest says:

    Is there a way to reconfigure the token destination path from “connect/token” to just “/token” ?

Leave a comment