Skip to content

Single sign-out and IdentityServer3

February 8, 2016

Single sign-out (or single logout, or SLO) is the mechanism by which a user is able to sign-out of all of the applications they signed into with single sign-on (SSO) including the identity provider. The OpenID Connect set of specifications contain three different specifications for how to handle single sign-out. Each provides a different approach to solve the problem based upon the nature of the application architecture that’s involved in the SSO process. The fact that there are three different approaches to dealing with the problem should be a hint that performing single sign-out is complex.

One, known as the “session management specification“, is targeted at JavaScript-based/SPA-style applications (and thus also known as the JavaScript-based sign-out spec). Another one, known as the “HTTP-based logout specification“, is targeted at server-side web frameworks (such as ASP.NET), and is also known as the “front-channel” approach. The last one, known as the “back-channel logout specification“, also targets server-side applications but takes a different approach than the “front-channel” spec. Each has their pros and cons.

Since its first release IdentityServer3 has supported the JavaScript-based “session management specification”, and as of build 2.2 from November of 2015 alsos implements the “front-channel” specification.

The JavaScript-based approach in essence requires a client application to load an <iframe> to an endpoint in IdentityServer called the “check_session_iframe” (whose value is available from the metadata endpoint). This <iframe> (given that it is from the IdentityServer’s origin) can access the session cookie managed by IdentityServer and can detect when the user’s login session has changed (meaning the user has signed out, or has signed in as another user). Once the user’s session has changed then the JavaScript-based application hosting the <iframe> can be notified that the user is no longer logged into IdentityServer. At that point, it’s up to the application to decide what to do, but at least it knows the user’s login status has changed.

The “front-channel” spec takes a different approach. It allows a client application to preregister a “logout URL” with IdentityServer. When the user signs out of IdentityServer, then on the “signed out” page of IdentityServer an <iframe> is then rendered into the page for each client application that has a registered “logout URL”. The request in the <iframe> allows the client application to receive a notification in the context of the user’s browser session that they are now logged out. In this request the client application can decide how to cleanup the user’s session and the most common approach would be to revoke the user’s session cookie at the client application.

You can read more about IdentityServer’s single sign-out support here.

DEVintersection Orlando, April 2016

January 19, 2016

I will be speaking at DEVintersection in Orlando, FL in April, 2016. I will be doing our 2-day workshop on “Identity and Access Control and Modern Web Applications”, as well as a session on “Security in ASP.NET 5 and MVC 6”, er, I mean “Security in ASP.NET Core and MVC Core”.

Hope to see you there!

 

Demos — NDC London 2016

January 16, 2016

Slides and demos for my “Introduction to IdentityServer” talk from NDC London 2016 are here: http://1drv.ms/1PLU4DV. Thanks for attending!

BrowsR helper for integration testing in ASP.NET 5

January 1, 2016

Dominick and I were working together on some unit tests for an ASP.NET 5 project. These tests were integration tests so we used the the TestServer from ASP.NET 5 TestHost project to load an entire ASP.NET pipeline. This allows you to use HttpClient to make HTTP calls into the running server. The main problem we had was that we needed to simulate a browser client that could hold onto cookies and follow redirects, so based upon Damian Hickey’s excellent OwinMessageHandler, we crufted up this for ASP.NET 5 (and of course had to give it a witty name):

public class BrowsR : DelegatingHandler
{
    private CookieContainer _cookieContainer = new CookieContainer();

    public bool AllowAutoRedirect { get; set; } = true;
    public bool AllowCookies { get; set; } = true;
    public int AutoRedirectLimit { get; set; } = 20;

    public BrowsR(HttpMessageHandler next)
        : base(next)
    {
    }

    protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var response = await SendCookiesAsync(request, cancellationToken);

        int redirectCount = 0;

        while (AllowAutoRedirect && (
                response.StatusCode == HttpStatusCode.Moved
                || response.StatusCode == HttpStatusCode.Found))
        {
            if (redirectCount >= AutoRedirectLimit)
            {
                throw new InvalidOperationException(string.Format("Too many redirects. Limit = {0}", redirectCount));
            }
            var location = response.Headers.Location;
            if (!location.IsAbsoluteUri)
            {
                location = new Uri(response.RequestMessage.RequestUri, location);
            }

            request = new HttpRequestMessage(HttpMethod.Get, location);

            response = await SendCookiesAsync(request, cancellationToken).ConfigureAwait(false);

            redirectCount++;
        }
        return response;
    }

    protected async Task<HttpResponseMessage> SendCookiesAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (AllowCookies)
        {
            string cookieHeader = _cookieContainer.GetCookieHeader(request.RequestUri);
            if (!string.IsNullOrEmpty(cookieHeader))
            {
                request.Headers.Add("Cookie", cookieHeader);
            }
        }

        var response = await base.SendAsync(request, cancellationToken);

        if (AllowCookies && response.Headers.Contains("Set-Cookie"))
        {
            var responseCookieHeader = string.Join(",", response.Headers.GetValues("Set-Cookie"));
            _cookieContainer.SetCookies(request.RequestUri, responseCookieHeader);
        }

        return response;
    }
}
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // ...
    }

    public void Configure(IApplicationBuilder app)
    {
        // ...
    }
}

To use it, then you’d do something like this:

var startup = new Startup();
var server = TestServer.Create(null, _startup.Configure, startup.ConfigureServices);
var handler = server.CreateHandler();
var client = new HttpClient(new BrowsR(handler));
var result = await client.GetAsync("...");

The requests will now follow redirects and hold onto cookies across those redirects. Too bad this isn’t built into the ASP.NET TestHost itself.

Enjoy.

Demos — Tech Intersection, September 2015

September 15, 2015

Here are the slides and demos from my session at Tech Intersection (from September 2015) on securing JavaScript based apps:

http://1drv.ms/1F0SVar

Thanks!

IdentityServer3 2.0.0 released

August 13, 2015

Just a quick note that IdentityServer3 2.0.0 has been released. We’ve taken a lot of good feedback from customers and tried to improve things in 2.0.0. Since we use semantic versioning, this release technically contains breaking changes, but depending on what you were using you may or may not see those changes. Dominick already explained some of the changes and the release notes sum up the rest.

You can get the NuGet here, and (as always) we appreciate questions and feedback via the issue tracker.

Thanks and enjoy!

 

Tech Intersection, September 2015

July 14, 2015

I will be speaking at the upcoming Tech Intersection conference (specifically the Security Intersection part) in Monterey, CA in September 2015. I have three sessions and a one-day workshop:

Workshop:

  • Identity and access control for modern web and mobile applications

Sessions:

  • Modern authentication for ASP.NET MVC 6 applications
  • Building secure JavaScript and Web API applications with OAuth2

Hope to see you there!