Announcing Thinktecture IdentityServer v3 – Beta 1

It’s done – and I am happy (and a bit exhausted) – a few minutes ago I closed the last open issue for Beta 1.

What’s new
It’s been 424 commits since we released Preview 1 – so there is quite a lot of new stuff, but the big features are:

  • A completely revamped configuration system that allows replacing bits and pieces of the core server itself – including a DI system
  • A plugin infrastructure that allows adding new endpoints (WS-Federation support is a plugin e.g.)
  • Refresh tokens
  • Infrastructure that allows customizing views and UI assets for re-branding as well as extensibility to insert your own workflows like registration or EULAs
  • Support for CORS and HTTP strict transport security
  • Content Security Policy (CSP) & stricter caching rules
  • Protection against click-jacking
  • Control over cookies (lifetime, naming etc)
  • more extensibility points

Repositories
We also split up the solution into separate repos and nugets for better composability:

Samples and documentation
We also have a separate repo for samples and added quite a bit of content to the wiki.

With that I am now leaving for holidays! ;) Give IdentityServer a try and give us feedback. A big thanks to all contributors and the people that engaged with us over the various channels!!!

Have a nice summer!

Posted in .NET Security, ASP.NET, IdentityServer, Katana, OAuth, OpenID Connect, OWIN, Uncategorized, WebAPI | 1 Comment

NDC London: Identity and Access Control for modern Web Applications and APIs

I am happy to announce that NDC will host our new workshop in London in December!

Join us to learn everything that is important to secure modern web applications and APIs using Microsoft’s current and future web stack! Looking forward to it!

course description / ndc london / tickets

Posted in .NET Security, ASP.NET, IdentityModel, IdentityServer, Katana, OAuth, OpenID Connect, OWIN, WebAPI | 1 Comment

Updated IdentityServer v3 Roadmap (and Refresh Tokens)

Brock and I have been pretty busy the last months and we did not find as much time to work on IdentityServer as we wanted.

So we have updated our milestones on github and are currently planning a Beta 1 for beginning of August.

You can check the github issue tracker (or open new issues when you find bugs or have suggestions) or you can have an alternative view on our current work using Huboard.

I just checked in initial support for refresh tokens, and it would be great if you could give that a try and let us know if it works for you – see here.

That’s it – back to work.

Posted in ASP.NET, IdentityServer, OAuth, OpenID Connect, WebAPI | 6 Comments

Resource/Action based Authorization for OWIN (and MVC and Web API)

Authorization is hard – much harder than authentication because it is so application specific. Microsoft went through several iterations of authorization plumbing in .NET, e.g. PrincipalPermission, IsInRole, Authorization configuration element and AuthorizeAttribute. All of the above are horrible approaches and bad style since they encourage you to mix business and authorization logic (aka role names inside your business code).

WIF’s ClaimsPrincipalPermission and ClaimsAuthorizationManager tried to provide better separation of concerns – while this was a step in the right direction, the implementation was “sub-optimal” – based on a CLR permission attribute, exception based, no async, bad for unit testing etc…

In the past Brock and me worked on more modern versions that integrate nicer with frameworks like Web API and MVC, but with the advent of OWIN/Katana there was a chance to start over…

Resource Authorization Manager & Context
We are mimicking the WIF resource/action based authorization approach – which proved to be general enough to build your own logic on top. We removed the dependency on System.IdentityModel and made the interface async (since you probably will need to do I/O at some point). This is the place where you will centralize your authorization policy:

public interface IResourceAuthorizationManager

{

    Task<bool> CheckAccessAsync(ResourceAuthorizationContext context);

}

 

(there is also a ResourceAuthorizationManager base class with some easy to use helpers for returning true/false and evaluations)

The context allows you to describe the actions and resources as lists of claims:

public class ResourceAuthorizationContext
{
    public IEnumerable<Claim> Action { get; set; }
    public IEnumerable<Claim> Resource { get; set; }
    public ClaimsPrincipal Principal { get; set; }
}

 

Middleware
The corresponding middleware makes the authorization manager available in the OWIN enviroment:

public void Configuration(IAppBuilder app)
{
    var cookie = new CookieAuthenticationOptions
    {
        AuthenticationType = "Cookie",
        ExpireTimeSpan = TimeSpan.FromMinutes(20),
        LoginPath = new PathString("/Login"),
    };
    app.UseCookieAuthentication(cookie);
 
    app.UseResourceAuthorization(new ChinookAuthorization());
}

 

Usage
Since the authorization manager is now available from the environment (key: idm:resourceAuthorizationManager) you can get ahold of it from anywhere in the pipeline, construct the context and call the CheckAccessAsync method.

The Web API and MVC integration packages provide a ResourceAuthorize attribute for declarative checks:

[ResourceAuthorize(ChinookResources.AlbumActions.View, ChinookResources.Album)]

 

And several extension methods for HttpContextBase and HttpRequestMessage, e.g.:

if (!HttpContext.CheckAccess(
    ChinookResources.AlbumActions.Edit,
    ChinookResources.Album,
    id.ToString()))
{
    return new HttpUnauthorizedResult();
}

 

or..

var result = Request.CheckAccess(
    ChinookResources.AlbumActions.Edit,
    ChinookResources.Album,
    id.ToString());

 

Testing authorization policy
Separating authorization policy from controllers and business logic is a good thing, centralizing the policy into a single place also has the nice benefit that you can now write unit tests against your authorization rules, e.g.:

[TestMethod]
public void Authenticated_Admin_Can_Edit_Album()
{
    var ctx = new ResourceAuthorizationContext(User("test", "Admin"),
        ChinookResources.AlbumActions.Edit,
        ChinookResources.Album);
    Assert.IsTrue(subject.CheckAccessAsync(ctx).Result);
}

 

or…

[TestMethod]
public void Authenticated_Manager_Cannot_Edit_Track()
{
    var ctx = new ResourceAuthorizationContext(User("test", "Manager"),
        ChinookResources.TrackActions.Edit,
        ChinookResources.Track);
    Assert.IsFalse(subject.CheckAccessAsync(ctx).Result);
}

 

Code, Samples, Nuget
The authorization manager, context, middleware and integration packages are part of Thinktecture.IdentityModel – see here.

The corresponding Nuget packages are:

..and here’s a sample using MVC (if anyone wants to add a Web API to it – send me a PR).

Posted in ASP.NET, IdentityModel, Katana, OWIN, WebAPI | 6 Comments

Using Discovery and Katana Middleware to write an OpenID Connect Web Client

In the last post I showed how to write an OIDC web client from scratch – this requires to have knowledge of certain configuration parameters of the OIDC provider, e.g.:

  • the URL of the authorize endpoint (and logout endoint)
  • the issuer URI
  • the key material used to sign the identity token (as well as the signing algorithm)

To make all that information ‘discoverable’, the OIDC provider can publish metadata (see WS-Federation/SAML2p metadata). This is specified here.

OIDC discovery documents live on a well known URL relative to the base URL of the OIDC provider, e.g. https://www.myoidcprovider/.well-known/openid-configuration.

image

So IOW – an OIDC client library could read that information and automatically configure itself so you don’t have to provide all the configuration values. This also makes scenarios like signing key roll overs less ‘painful’.

The Microsoft OIDC middleware for Katana is such an client library – its setup boils down to providing the base URL of the provider, the client id, redirect URI, the scopes and response type.

app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions

    {

        Client_Id = “implicitclient”,

        Authority = Constants.BaseAddress,

        Redirect_Uri = https://localhost:2671/,

        Response_Type = “id_token”,

        Scope = “openid email”,

 

        SignInAsAuthenticationType = “Cookies”,

    });

 

Since this integrates with the MVC/Katana security model you simply need to return a 401 to trigger the authentication handshake (manually or via an [Authorize] attribute).

The middleware will be part of Katana 3.0 (currently in beta) and you can find a sample using IdentityServer as the provider here.

Posted in IdentityServer, Katana, OpenID Connect, OWIN | 6 Comments

DotNetRocks on OpenID Connect with Brock and Me

Recorded at NDC Oslo:

http://www.dotnetrocks.com/default.aspx?ShowNum=993

Posted in Conferences & Training, IdentityServer, OAuth, OpenID Connect, OWIN, WebAPI | 5 Comments

Writing an OpenID Connect Web Client from Scratch

OIDC is supposed to make things easier, so I thought it would be a good exercise to write a web application that uses OIDC to authenticate users – but without using any OIDC specific libraries.

I chose to use the implicit flow with the form post response mode – which is very similar to the WS-Federation or SAML2p POST profiles. Let’s see how much code it takes.

Initiating the Request
The first task is to create the authentication request for the OIDC authorize endpoint. This involves constructing a query string that contains the client id, scope, redirect URL, response type and response mode. In addition you need to create two random numbers for state and nonce. State is used to correlate the authentication response, nonce is used to correlate the identity token coming back. Both values need to be stored temporarily (I use a cookie for that).

public ActionResult SignIn()
{
    var state = Guid.NewGuid().ToString("N");
    var nonce = Guid.NewGuid().ToString("N");
 
    var url = Constants.AuthorizeEndpoint +
        "?client_id=implicitclient" +
        "&response_type=id_token" +
        "&scope=openid email" +
        "&redirect_uri=http://localhost:11716/account/signInCallback" +
        "&response_mode=form_post" +
        "&state=" + state +
        "&nonce=" + nonce;
            
        SetTempCookie(state, nonce);
        return Redirect(url);
}

 

Handling the Callback
When the OIDC provider is done with its work, it sends back an identity token as part of a form POST back to our application. We can fetch the token (and the state) from the forms collection, validate the token (more on that shortly) and if successful sign the user in locally using an authentication cookie.

[HttpPost]
public async Task<ActionResult> SignInCallback()
{
    var token = Request.Form["id_token"];
    var state = Request.Form["state"];
 
    var claims = await ValidateIdentityTokenAsync(token, state);
 
    var id = new ClaimsIdentity(claims, "Cookies");
    Request.GetOwinContext().Authentication.SignIn(id);
 
    return Redirect("/");
}

 

Validating the Token
There is an exact specification on how to validate identity tokens and the authentication response (see here) – this involves:

  • Validate the JWT
    • the audience must match the client id
    • the issuer must match the expected issuer name
    • the signing key must match the expected issuer signing key
    • the token must be in its validity time period
  • The state parameter on the response must match the state value that we sent to the provider on the initial request
  • The nonce claim inside the identity token must match the nonce that we sent to the provider on the initial request

Most of the work is done by the JWT token library from Microsoft (see here for alternatives on different platforms).

private async Task<IEnumerable<Claim>> ValidateIdentityTokenAsync(
string token, string
state)
{
    var result = await Request
        .GetOwinContext()
        .Authentication
        .AuthenticateAsync("TempCookie");
                
    if (result == null)
    {
        throw new InvalidOperationException("No temp cookie");
    }
 
    if (state != result.Identity.FindFirst("state").Value)
    {
        throw new InvalidOperationException("invalid state");
    }
 
    var parameters = new TokenValidationParameters
    {
        AllowedAudience = "implicitclient",
        ValidIssuer = "https://idsrv3.com",
        SigningToken = new X509SecurityToken(
           X509
           .LocalMachine
           .TrustedPeople
           .SubjectDistinguishedName
           .Find("CN=idsrv3test", false)
           .First())
    };
 
    var handler = new JwtSecurityTokenHandler();
    var id = handler.ValidateToken(token, parameters);
 
    if (id.FindFirst("nonce").Value != 
        result.Identity.FindFirst("nonce").Value)
    {
        throw new InvalidOperationException("Invalid nonce");
    }
 
    Request
       .GetOwinContext()
       .Authentication
       .SignOut("TempCookie");
                
    return id.Claims;
}

 

Done. This wasn’t too bad! The full sample using IdentityServer v3 as the provider can be found here.

In the next post I will show how you can simplify this by using the OpenID discovery document and OWIN middleware. Stay tuned.

Posted in Uncategorized | 15 Comments