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.
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.
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:
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:
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.
Assertion flow support is new in AS v1.1 – give it a try and give us feedback if you like it or not.
Filed under: ASP.NET, AuthorizationServer, IdentityModel, OAuth, WebAPI
