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

NDC Oslo 2014 Slides, Samples and Videos

As always – NDC was a great conference! Here’s the list of resources relevant to my talks:

IdentityServer v3 preview: github

Web API Access Control & Authorization: slides / video

OpenID Connect: slides / video

 

Posted in ASP.NET, Conferences & Training, IdentityServer, OAuth, OpenID Connect, WebAPI | 2 Comments

10th Anniversary

…seems that this blog is now ten years old. Who would have thought.

Posted in Uncategorized | 3 Comments

Claims-based Authentication does not exist (for crying out loud)

…as much as there is no “role-based authentication”.

Rather use “claims-based identity” or “token-based authentication”

kthxbye

Posted in Uncategorized | 1 Comment

100k Downloads of Thinktecture IdentityModel

Amazing!

nuget100k

Thanks for all the feedback – but keep in mind that this package is deprecated. For Web API => v2 and MVC >= 5 please use the new Thinktecture.IdentityModel.Core and family.

Posted in IdentityModel | 2 Comments

IdentityServer v3 Nuget and Self-Hosting

Thanks to Damian and Maurice we now have a build script for IdSrv3 that creates a Nuget package *and* internalizes all dependencies. So in other words you only need to reference a single package (well strictly speaking two) to self host the STS (including Autofac, Web API, various Katana components etc). Pretty cool. Thanks guys!

A picture says more than 1000 words (taken from the new self host sample in the repo).

image

Posted in IdentityServer, Katana, OAuth, OpenID Connect, OWIN, WebAPI | Leave a comment

Web API 2 Excel File Export With OAuth2 Implicit Flow

Originally posted on Software Engineering:

This article demonstrates how to set up a Web API 2 excel file download using OAuth2 Implicit Flow. The application requires an Authorization Server and Identity Server V2 from Thinkteckture and also the excel Media Formatter from WebApiContrib. leastprivilege.com provided a lot of blogs which helped complete this article. Thanks for those blogs. The article should help as a simple Howto for this scenario.

Code: https://github.com/damienbod/ExcelFileExportWithOAuth2ImplicitFlow

OAuth2 Implicit Flow
The application uses the OAuth2 Implicit flow. This flow is defined here:

http://tools.ietf.org/id/draft-ietf-oauth-v2-31.html#rfc.section.4.2

Resource Server

The resource server is a simple MVC application which hosts a Web API 2 service. The api has one single method for exporting excel data. This export uses the WebApiContrib.Formatting.Xlsx library from Jordan Gray. The api method forces that excel is always returned no matter what is set in the Accept Header. This is not usually good practice as the client should decide in which format…

View original 1,529 more words

Posted in Uncategorized | 2 Comments