Advanced OAuth2: Assertion Flow (how)

My last post described the mechanics and motivation for the OAuth2 assertion flow.

In this post I want to show you how you can use Thinktecture AuthorizationServer to implement an assertion flow scenario. For this specific example I will use Microsoft Account authentication on WinRT – but this could be substituted by any other authentication system (see my last post for more examples).

1 Microsoft Account authentication
The easiest way to do MSA authentication is to use the Live SDK.

For WinRT – you first need to register your application in the store. This will result in a client ID and a client secret. Next you need to associate your VS solution with that store app (right click the project in solution explorer –> Store –> Associate App with the Store).

The following code does the interaction with the MSA infrastructure:

var scopes = new string[] { “wl.signin” };

var authClient = new LiveAuthClient(http://www.thinktecture.com);

 

// try silent logon first

_loginResult = await authClient.InitializeAsync(scopes);

 

if (_loginResult.Status != LiveConnectSessionStatus.Connected)

{

    // need to (re) prompt

    _loginResult = await authClient.LoginAsync(scopes);

 

    if (_loginResult.Status != LiveConnectSessionStatus.Connected)

    {

        await new MessageDialog(“Access denied!”).ShowAsync();

        return;

    }

}

A few things to note here:

  • If the user uses his Microsoft Account to sign into Windows, there is no need to re-authenticate (single sign-on).
  • The first time this code runs, the user will be prompted for consent (see screenshot). Afterwards the consent decision will be remembered and the dialog will not be shown again.
  • The above code does not include the client ID – that is implicitly sourced from the application package identity (that’s why we needed to associate the VS solution with the store app).
  • The above code also does not include a client secret, this is not used at all on the client (it couldn’t be stored there securely anyways) – but later when it comes to validate the token in AuthorizationServer.

 

image

The login result actually contains two tokens: an access token for Microsoft’s backend that you can use to access the users’ data (profile, friends etc.) and an identity token.

image

The access token format is a private implementation detail, the identity token (or authentication token as they call it) is actually a JWT. We can peek inside the token using this tool:

image

As you can see – the token contains information about the issuer, expiration, the subject as well as the identity of the client. The token is signed by the client secret and can be used as input for the assertion flow.

2 Registering the client for assertion flow in AuthorizationServer
Before the client application can use the assertion flow with AuthorizationServer, you need to register it. Starting with version 1.1 we have a new option in the flows drop-down:

image

Also make sure the client is allowed to request at least one scope in the application.

3 Using Assertion Flow to request an access token
Requesting an access token using an assertion is easy. Our OAuth2Client library has direct support for that:

var client = new OAuth2Client(

    new Uri(https://as.local/users/oauth/token),

    “assertionclient”,

    “secret”);

 

_asTokenResponse = await client.RequestAssertionAsync(

    “urn:msaidentitytoken”,

    _loginResult.Session.AuthenticationToken,

    “read”);

 

This will create an (authenticated) POST to the token endpoint looking like this:

grant_type=urn:msaidentitytoken&
assertion=abcxyz&
scope=read

The urn:msaidentitytoken is something I came up with – it is used in the next step to give AuthorizationServer a hint how to validate the token. As mentioned in my previous post, there are also official grant types for SAML2 and JWT.

4 Validating the token
The last step is to tell AuthorizationServer how to validate the incoming token. For this purpose there is a new interface in AS called IAssertionGrantValidation:

public interface IAssertionGrantValidation

{

    ClaimsPrincipal ValidateAssertion(ValidatedRequest validatedRequest);

}

You can simply implement that interface and plug it into AS by either wiring up your code in autofacConfig.cs or in autofac.config (both in the WebHost project).

To validate Microsoft Account authentication tokens you can use the Live SDK, e.g.:

public class AssertionGrantValidator : IAssertionGrantValidation

{

    public const string MsaIdentityToken = “urn:msaidentitytoken”;

 

    public ClaimsPrincipal ValidateAssertion(
     
ValidatedRequest
validatedRequest)

    {

        if (validatedRequest.AssertionType == MsaIdentityToken)

        {

            var appId = “ms-app://s-1-15-2…1721910”;

            var appSecret = “jqR…xOxv”;

            var redirectUri = http://www.thinktecture.com;

 

            var authClient = new LiveAuthClient(

                    appId,

                    appSecret,

                    redirectUri);

 

            var msaId = authClient.GetUserId(validatedRequest.Assertion);

            var id = new ClaimsIdentity(“MSA”);

            id.AddClaim(new Claim(ClaimTypes.NameIdentifier, msaId));

 

            return FederatedAuthentication
              .FederationConfiguration
              .IdentityConfiguration
              .ClaimsAuthenticationManager
              .Authenticate(

                “AssertionValidation”,

                new ClaimsPrincipal(id));

        }

 

        return null;

    }

 

Here is where the MSA client secret comes in, it is used to validate the authentication token. Afterwards the extension creates a ClaimsIdentity, runs it through claims transformation and passes it back to AS. AS in turn will create the usual access token, sign it and return it to the client.

From that point the client can use the access token to access the resource servers just as usual.

image

Assertion flow support is new in AS v1.1 – give it a try and give us feedback if you like it or not.

This entry was posted in ASP.NET, AuthorizationServer, IdentityModel, OAuth, WebAPI. Bookmark the permalink.

5 Responses to Advanced OAuth2: Assertion Flow (how)

  1. Reblogged this on Peter's ruminations and commented:
    Note that the world of oath is heading for the world of ws-services, where in the four corner model that incoming token from the (NSA-governed) American IDP is translated in a (NSA-influenced) German RP-STS token.

    IN our model we did almost the same. TO the JSON response we added the SAML token that controlled the minting of the access-grant and JWT (in the first place). Then, we simply swap that SAML file using an RP-STS. This works well with Microsoft online, where exactly that model is required in order for an oauth-based WPF rich client to get the tokens needed to access the office365 APIs (using the token re-issued by microsoftonline)

  2. Is Assertion flow the best among all the choices offered in AuthorizationServer? Is it always the preferred way to doing WebAPIs? Does authorization code flow have any advantage over Assertion flow?

    I have this question because many big players like StackOverflow, Facebook offer only authorization code flow or implicit flow. Do they avoid Assertion flow for some valid reason?

  3. cotepatrice says:

    Hi Dominick !

    I know that the product is deprecated, but I do have a question about assertion flow. Since it’s not totally related to the product, but more with the way SecurityTokenHandlers validate tokens, I will risk asking it here.

    In the developpers’ environment here, we use the Identity And Access tool for VS 2012. So the SAML token I get comes from the LocalSTS and therefore, they are NOT SIGNED. Which cause quite a problem when I want to validate the token in my custom assertion validator class. I use the default SamlSecurityTokenHandler and I get an error on the ValidateToken method “ID4220: The SAML Assertion is either not signed or the signature’s KeyIdentifier cannot be resolved to a SecurityToken”.

    I know I could create my own implementation and override the method, but it would create a gap with the standard and I could easily forget something in the validation process I think.

    Would you suggest me to do that or to find a local STS that can sign the SAML token (I searched but found nothing yet) ?

  4. I can’t believe they are not signing their tokens. Anyways – DON’T let an implementation slip in your code base that skips signature validation ;)

    You can use IdSrv3 to have an embedded STS ;)

  5. cotepatrice says:

    We’re waiting for the UI to evaluate a migration to IdSrv3. It took us a few months just to get used to the old IdSrv and Authz, so my boss are a little concerned about spending time and money on migration. I’ll have to prove them that it would actualy save us time and money in the future :-)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s