After much abstract thought (very well received! thank you! :-)) it’s time to get back to some good old C#, or you’ll think I’ve gone soft 🙂
Say that you have a Web application protected via claims-based identity. Say that your application occasionally reaches out to a backend service, and that the service output (or even the ability of calling the service to begin with) depends on the current user of the Web application.
Sounds familiar? The need to flow identity through the layers of a solution is an extremely common one. The solution we’ve been offering through WIF, the ActAs flow, is extremely powerful but, as it turns out, also not the easiest thing to implement.
In this post I am going to describe a “low-tech” solution to the problem which can only be applied when very specific requirements are met, and is not nearly as powerful as full-fledged ActAs, but has the advantage of being substantially simpler to implement.
Flowing Identity Through Layers
I have written extensively about ActAs in the past (yes, I was already using a tablet in 2008; in fact, I was using one at least since 2004) and in the book; I also often spoke about it during breakouts (one of the most detailed explanations can be found here, from minute 54 on). I won’t repeat what I wrote then, but rather than sending you off to read tons of background material I’ll set a bit of context here.
The idea behind the ActAs approach to the problem described earlier in the post is pretty simple: when it needs to call the backend service, the Web application requests a token to the appropriate STS by presenting both its own application credentials and the token that the user presented in order to authenticate with the Web app itself (commonly referred to as the bootstrap token). The STS can then verify that the request is actually coming from the (well-known) Web application, and verify that the user is actually engaging with the Web app at the moment; hence, the STS can apply whatever logic it deems appropriate to manufacture (or not) a token that will fully reflect the situation (claims about the user, etc) and that is scoped precisely to the backend service.
That is a fantastic way of handling delegation though solution layers, far more robust and expressive than the classic trusted subsystem approach. In the most generic case, in which frontend and backend are operated by different & distinct owners, that is the right way of crossing the boundary between the two.
However, this is also quite complicated to set up. In WIF we made significant effort to support the scenario through the object model (CreateChannelActingAs, CreateChannelWithIssuedTokens, etc) however there are many moving parts you need to tame. Not every STS supports ActAs (ACS2.0, for one, doesn’t), creating your own ActAs STS entails grokking some pretty subtle concepts (like specifying which token handler will be used to process the user tokens) and various others. Troubleshooting ActAs scenarios is not the most fun you can have on Friday nights.
However in my experience the thing that developers have a hard time understanding is why can’t one just reuse with the backend service the same token that was presented by the user to authenticate with the Web application. It would be so simple! In fact, in the general case one cannot assume that that would work. In this post I give a long list of things that would prevent that approach from working in the most general case; here I’ll summarize with a simple analogy.
Imagine that you walk in a laundry shop and you leave there a bunch of shirts and a leather jacket; you write a bank check payable to the shop and you walk.
Say that the laundry takes care of the shirts in-house, but outsources the processing of leather goods to a specialized shop. Now, the laundry cannot pay the other shop by presenting the check they got from you, because the check is payable by the laundry shop only! Map the laundry shop to the frontend, the leather shop to the backend, the check to the token and the “payable to” field to the audience restriction element and the reason you can’t usually reuse tokens will be clear.
That said: not every laundry shop outsources leather goods processing! Say that the shirt and leather processing are simply assigned by different departments/cost centers of the same shop. In that case they would be both able to cash the check, which means that the check itself might move from the admin of the department who picked up the order to the admin of the leather processing center, and everything would still be fine. There you go: we found the special case in which reusing tokens might be a possibility.
Let’s map our analogy back to the digital world. If both frontend and backend are simply different aspects of the same solution, and belong to the same owner, then one could consider the differences between the two as mere implementation details. In identity terms: if the frontent and the backend belong to the same realm, then a token meant for one is also scoped for the other and vice versa. You’ll have to be careful that tokens delivered to the frontend cannot be stolen across sessions, and if you don’t want anybody to call the backend directly you better ensure that it is not publicly addressable, but in absence of further constraints in this case it should be OK for the frontent to invoke the backed by flowing the bootstrap token.
Even if people intuitively gravitated toward the token reuse approach, one of the technical reasons for which it was not implemented very often is what I was used to call protocol refraction. Just like light has different propagation characteristics in different mediums, in the WS-* world a token meant for a passive flow would be a bearer token and one for an active leg would be a holder-of-key, hence the token meant for the frontend would not have been fit for the backend. Well, that was 2008. In 2013, chances are that your backend will be implemented as REST services: and REST services are perfectly happy with bearer tokens.
The final barrier to a successful implementation was the sheer size of the tokens at play. Being in SAML format, a typical Web sign-in token would usually be too big to travel in HTTP header. Well, enter the JSON Web Token (JWT). With ACS you can do a classic WS-Federation web sign-on flow which carries a JWT instead of the canonical SAML. As long as the above restrictions hold and you are in the right scenario, that constitutes a great bootstrap token that is perfect to be used for invoking a REST service.
Got it? I like to call this approach the “prrom man”’s ActAs. Tha is not meant to be derogatory, it just expresses the fact that this approach tries to solve the same problem that we would have normally addressed with ActAs. Although it does not have the same generality and expressive power of ActAs (not even close), it does require with significantly less resources and, provided that your scenario allows for it (do you clean your leather in-house or do you outsource it?) it gets the job done,
Blah, blah, blah. I know, right? That’s called logorrhea, and I have it since I was little. Enough with the blabber, let’s hit Visual Studio and see how it’s done.
- Create a blank solution (I called mine “PoorMansActAs”).
- Add a new project of type “ASP.NET MVC 4 Web Application” as a frontent – I called mine “Frontend”. I picked the “Intranet Application” sub-type as it is more compact than others but still has enough surface to be a good starting point.
- Add a new project of type “ASP.NET MVC 4 Web Application”, “Web API” as a backend – you guessed right, I called mine “Backend”.
That sets the stage. Below I am going to go in the details of how to set up frontent and backend to implement the poor man’s actas flow. Also: I have noticed that lately in my posts I have left a lot as “exercise for the reader”, referring to published samples. Here I’ll try to be a bit more descriptive, and paste & comment code snippets even if those are already available elsewhere.
Let’s start by making our frontend outsource authentication to ACS and process JWT tokens. Thanks to the identity and access tool for VS 2012, this is going to be super-easy. Right click on the project, pick Identity ad Access… and choose ACS. Note: later we’ll use the email claim, hence you might want to select only identity providers that make it available via ACS (like Facebook).
Very good. By default, ACS wills end us a SAML token. We want to change that. Let’s hit the Windows Azure portal, select the ACS namespace of choice, pick the RP that the VS tool created for us and edit it as follows:
The main things to observe in the screen above are:
- The realm value. I have chosen a generic URI, which represents the logical identifier of my solution as a whole (as opposed to reusing the address of the frontend or the backend, the default choice). One day I’ll likely write a long rant about realm != return URL, but for today that’s enough 🙂
- The token format. I changed it ot JWT.
From now on ACS will do the right thing and send us JWTs. The next step is to teach to our application how to digest them! Right click on the solution and go to the Manage NuGet Packages,.. entry.
Search for identitymodel.tokens and you’ll land on the NuGet for the JWT handler. Don’t do like Kyle, do read the license terms and if you’re OK with them hit I Accept to install the package.
Great! Now you have the assembly with the right types available. head to the web.config and add few lines to take advantage of the JWT token handler.
2: <identityConfiguration saveBootstrapContext="true" >
4: <add value="urn:poormansactassample" />
7: <add type="Microsoft.IdentityModel.Tokens.JWT.JWTSecurityTokenHandler, Microsoft.IdentityModel.Tokens.JWT" />
9: <certificateValidation certificateValidationMode="PeerTrust"/>
12: <issuerNameRegistry type="System.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, System.IdentityModel, Version=126.96.36.199, Culture=neutral, PublicKeyToken=b77a5c561934e089">
14: <add thumbprint="C1677FBE7BDD6B131745E900E3B6764B4895A226"
15: name="https://lefederateur.accesscontrol.windows.net/" />
22: <cookieHandler requireSsl="false" />
23: <wsFederation passiveRedirectEnabled="true"
25: realm="urn:poormansactassample" requireHttps="false" />
The tool filled in almost everything you need. the only lines you have to add/modify are
Line 2: you want to opt in for saving the bootstrapContect. This will come in handy later, when we’ll call the backend service
Line 4: the tool autogenerated a realm for you, hence an expected audience value. You want to change that value to the realm we set in ACS.
Lines 6 to 11: this is boilerplate code to add the JWT handler in the handlers list.
Line 25: as for line 4, we need to update the realm to what we set in ACS
That’s it for the code meant to enabling JWT-based web sign-on. You still need to install in the trusted people the certificate needed to verify ACS’ token signatures. See here (and specifically “Using the JWT Handler With WIF Applications”) for hints on how to do that.
If you want you can give the project a spin now, just to verify that the sig-in works. We’ll get back to this project later to add the logic to perform the call to the backend.
We need to add the JWT handler NuGet to the backend project as well. Done that, let’s add some logic to the Web API project to expect and validate incoming tokens via the OAuth bearer calling style. As shown in all the JWT/AAL samples, we do that by implementing a single DelegatingHandler that deserializes and validates the token.
Let’s head to the global.asax and start with the following:
1:internal class TokenValidationHandler : DelegatingHandler
5: private static bool TryRetrieveToken(HttpRequestMessage request, out string token)
7: token = null;
8: IEnumerable<string> authzHeaders;
9: if (!request.Headers.TryGetValues("Authorization", out authzHeaders) || authzHeaders.Count() > 1)
11: return false;
13: var bearerToken = authzHeaders.ElementAt(0);
14: token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
15: return true;
this very basic code retrieves the token, if any, from the http Authorization header.
The real meat is in the following method:
1: protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
3: HttpStatusCode statusCode;
4: string token;
6: if (!TryRetrieveToken(request, out token))
8: statusCode = HttpStatusCode.Unauthorized;
9: return Task<HttpResponseMessage>.Factory.StartNew(() =>
14: X509Store store = new X509Store(StoreName.TrustedPeople,
16: X509Certificate2 cert = store.Certificates.Find(
20: JWTSecurityTokenHandler tokenHandler =
21: TokenValidationParameters validationParameters =
23: AllowedAudience = "urn:poormansactassample",
24: ValidIssuer = "https://lefederateur.accesscontrol.windows.net/",
25: SigningToken = new X509SecurityToken(cert)
28: Thread.CurrentPrincipal =
30: return base.SendAsync(request, cancellationToken);
32: catch (SecurityTokenValidationException e)
34: statusCode = HttpStatusCode.Unauthorized;
36: catch (Exception)
38: statusCode = HttpStatusCode.InternalServerError;
40: return Task<HttpResponseMessage>.Factory.StartNew(() =>
What’s happening here? Quick walkthrough:
Line 6: retrieve the token via the method defined earlier
Lines 14 to 17: retrieve the X509 certificate used for verifying ACS’ token signatures
Lines 20 to 26: initialize a new JWT handler with validating parameters including the expected audience (the realm), the expected issuer and the just-retrieved certificate
Line 28: validate the token; if successful, create a ClaimsPrincipal and assing it to the current thread for the main API logic to have access to it
|IMPORTANT UPDATE: Dominick rightfully points out that with Web API just assigning the newly minted ClaimsPrincipal to Thread.CurrentPrincipal s not enough. He gives the whole story here, but in you’re in a hurry: you’ve got to assign it to HttpContext.Current.User, too.|
OK, it’s still security, but as protocol/security code goes I’d daresay this is pretty straightforward,
Now: we need to get this puppy in the We API pipeline. We do so by adding it to the HttpConfiguration:
1:static void Configure(HttpConfiguration config)
3: config.MessageHandlers.Add(new TokenValidationHandler());
We just need to call Configure in Application_Start, and we’re all set.
Alrighty. We could do all sorts of interesting things with claims in our Web API, but here I’d just like to do something that proves we did have access to them. I’ll ignore every common decency and return the value of the email claim without doing any error checking.
1: // GET api/values/5
2: public string Get(int id)
4: return ClaimsPrincipal.Current.FindFirst(ClaimTypes.Email).Value;
5: //return "value";
Perfect! Our API is all ready to be securely invoked. Let;s get back to the frontend and make it happen.
Flowing the Caller’s Identity
Back to the frontend. Let’s pick one of the default actions in the home controller and add the necessary logic to retrieve the bootstrap token and use it to invoke the backend service. There you go:
1:public ActionResult About()
4: BootstrapContext bc =
5: JWTSecurityToken jwt = bc.SecurityToken as JWTSecurityToken;
7: string rawToken = jwt.RawData;
9: HttpWebRequest request =
WebRequest.Create("http://localhost:49007/api/values/5") as HttpWebRequest;
10: request.Method = "GET";
11: request.Headers["Authorization"] = "Bearer " + rawToken;
12: request.ContentType = "application/json";
14: string responseTxt = String.Empty;
15: using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
17: var reader = new StreamReader(response.GetResponseStream());
18: responseTxt = reader.ReadToEnd();
22: ViewBag.Message =
"I just called the backend, and I got back " + responseTxt;
24: return View();
What’s happening here:
Line 4: we retrieve the bootstrap context. For details, see this post.
Line 5: we cast the bootstrapcontext content to a JWT
Line 7: we retrieve the JWT in encoded form, ready to be put on the wire
Lines 9, 10 and 12: we prepare an HttpWebRequest to invoke the backed service
Line 11: we add the bootstrap token in the appropriate header
Lines 14-20: we invoke the service and retrieve the results
Line 22: we surface the result in the UI
That’s all you need. Hit F5; authenticate with the frontend; hit about and observe that the frontend successfully called the backend using the bootstrap token.
In the general case, if you need to make delegated calls between layers of your solution you should consider using ActAs.
That said: if you own both frontend and backend, frontend and backend can truly be considered components of the same app, your backed is REST, your web sign-in token format allows for it, you are OK with your backed to be callable without going though the frontend (or there are “physical” barriers in place to prevent that) then the poor man’s ActAs can provide a simpler – albeit more limited – alternative. Have fun!
P.S.: kudos to my wife for the laundry example, my original example was pretty lame! 🙂
Vittorio Bertocci is a developer, speaker, published author, avid reader, troublemaker, foodie, Italian expat, and other things that would not be wise to mention here. This is Vittorio's personal blog. The views and opinions expressed here are his, and not those of his employer.Follow @vibronet
@Steve_Lockstep @bobblakley Hey Steve, happy to discuss specifics if you want! @azuread supports multiuser sessions and OIDC’s select_account, that should make it possible to handle multiple concurrent sessions /cc @bobblakley
- September 2017 (1)
- August 2017 (1)
- March 2017 (1)
- January 2017 (2)
- December 2016 (1)
- October 2016 (1)
- September 2016 (1)
- July 2016 (1)
- June 2016 (1)
- April 2016 (1)
- January 2016 (2)
- December 2015 (2)
- September 2015 (2)
- August 2015 (4)
- July 2015 (1)
- June 2015 (1)
- May 2015 (2)
- April 2015 (1)
- March 2015 (3)
- February 2015 (4)
- January 2015 (1)
- November 2014 (4)
- October 2014 (5)
- September 2014 (4)
- August 2014 (2)
- July 2014 (8)
- June 2014 (1)
- May 2014 (4)
- April 2014 (4)
- March 2014 (6)
- February 2014 (5)
- January 2014 (1)
- December 2013 (4)
- November 2013 (2)
- October 2013 (9)
- September 2013 (3)
- August 2013 (3)
- July 2013 (5)
- June 2013 (8)
- May 2013 (4)
- April 2013 (17)
- March 2013 (16)
- February 2013 (6)
- January 2013 (4)
- December 2012 (3)
- November 2012 (5)
- October 2012 (1)
- August 2012 (3)
- July 2012 (6)
- June 2012 (9)
- April 2012 (1)
- March 2012 (7)
- February 2012 (1)
- December 2011 (1)
- November 2011 (1)
- October 2011 (2)
- September 2011 (1)
- August 2011 (2)
- July 2011 (5)
- June 2011 (2)
- May 2011 (15)
- April 2011 (9)
- March 2011 (2)
- February 2011 (5)
- January 2011 (8)
- December 2010 (3)
- November 2010 (3)
- October 2010 (6)
- September 2010 (6)
- August 2010 (8)
- July 2010 (2)
- June 2010 (9)
- May 2010 (13)
- April 2010 (4)
- March 2010 (6)
- February 2010 (1)
- December 2009 (3)
- November 2009 (16)
- September 2009 (3)
- August 2009 (5)
- July 2009 (6)
- June 2009 (7)
- May 2009 (10)
- April 2009 (11)
- March 2009 (4)
- February 2009 (2)
- January 2009 (6)
- December 2008 (3)
- November 2008 (9)
- October 2008 (3)
- September 2008 (5)
- August 2008 (7)
- July 2008 (8)
- June 2008 (6)
- May 2008 (6)
- April 2008 (11)
- March 2008 (10)
- February 2008 (9)
- January 2008 (12)
- December 2007 (6)
- November 2007 (5)
- October 2007 (10)
- September 2007 (4)
- August 2007 (1)
- July 2007 (1)
- June 2007 (17)
- May 2007 (8)
- April 2007 (10)
- March 2007 (10)
- February 2007 (4)
- January 2007 (6)
- December 2006 (2)
- November 2006 (3)
- October 2006 (5)
- September 2006 (3)
- August 2006 (7)
- July 2006 (3)
- June 2006 (7)
- May 2006 (4)
- April 2006 (6)
- March 2006 (8)
- February 2006 (3)
- January 2006 (2)
- December 2005 (5)
- November 2005 (2)
- October 2005 (6)
- July 2005 (12)
- June 2005 (6)
- May 2005 (3)
- April 2005 (8)
- March 2005 (4)
- February 2005 (14)
- January 2005 (9)
- December 2004 (5)
- November 2004 (1)
- October 2004 (3)
- June 2004 (3)
- May 2004 (3)
- April 2004 (3)
- March 2004 (1)
- February 2004 (2)
- January 2004 (3)
- December 2003 (5)
- November 2003 (5)
- October 2003 (5)
- September 2003 (5)
- July 2003 (2)
- June 2003 (4)
- May 2003 (1)
- April 2003 (9)