On a flight from Sydney to S.Francisco. We were supposed to leave at 1:55pm, we took off almost at 9:00pm. I am really, really pissed off and the only thing that lightens up my mood is re-reading the great comments that the Australian TechEd attendees left on as feedback for the couple of sessions I gave this week. Thanks guys for the kind words, and again thank you for dealing so nicely with my difficult accent! I haven't seen the feedback from New Zealand, yet if it's half as nice as this one I'll ask for a vacation 🙂 More seriously: both events were great and well worth the long flights. I got a lot of questions, both about cloud scenarios and Zermatt, and some of those are starting to recur more and more often: I "blame" it on the fact that with Zermatt finally out people have the chance of experimenting, and the questions arise more naturally. In the next hours I'll try to address some of the most recurring doubts/misconceptions, at least as long as the laptop batteries keep Live Writer alive (and the Zune shields me from the usual unusual amount of kids I'm surrounded by).
Enough into already. The first monster I'd like to poke is delegation.
Why can't I reuse tokens?
Now this is a question that comes out very often. Let's say that you have two services, A and B. Let's also say that your business process requires that a client C calls A, and that in turn A calls B. Picture:
(yes, this time I am using ArtRage instead of OneNote. Remember, I am trying to fight boredom here :-)).
The right of accessing A will be determined on the basis of the identity of C (or of the subject who is operating through C); for example, if A exposes a method for printing documents then C will be required to have the rights to operate the printer of choice. You can easily imagine how such a scenario could be implemented with claims: maybe there is a group "printing department", and A will be happy if and only if C presents a token from the right issuer which contains a claim http://.../group whose value is "printing department".
Now let's turn our attention to B. What do we need for making B happy? We have 2 main scenarios:
B is happy if it knows that it has been called by A; trusted subsystems
Let's say that B turns on the printer warm-up procedure if the device was unused for a long time (silly, i know, but bear with me). In this case all we need to know for authorizing the call is that the request is coming from a legitimate printing application; we don't want to allow anybody to fry the printer by playing with the warm-up uselessly, but we know that if its' A calling then the warm-up happens for a reason and we are fine with it. In other words, A's application identity is what we need; B does not care if A is being called by a human or by a non-interactive process, for example for automated printing of the pay stubs on the night before payday. In this case the token that secures the call between A and B should prove A's identity; it could be an issued token, but it could also be something more static such as an X509; your choice. In fact, the above can be bent further. B may still care about who C is or about the value of some of C's attributes, but it may be willing to let A perform all the verifications about it. That may happen for many reasons, perhaps B is isolated from the external network and cannot perform direct verifications; or more simply, the architecture may describe a solution ad-hoc that does not justify investment in more complex designs. The pattern in which once we are past A we leave all skepticism behind is known as trusted subsystem, you can find a lot of literature about it. In that case B would receive info about C directly from A, maybe repackaged in a new token or simply as an attachment of the original token (provided that B can do anything with it, see below). If A is allowed to repackage, it can literally say whatever to B; it can even perform calls to B without the need of a solicitation from C, creating arbitrary contexts or reusing past ones; that's why, as its name implies, this pattern requires A to be trustworthy.
B is happy if it knows that C has the right to call it; delegation
This is a somewhat nastier situation, and yet extremely common. Let's say that, instead of implementing printer warm-up, B implements a method that retrieves employees' salary information; and that A invokes it as part of a dossier printing operation. In this case, it may not be enough for B to know that the caller is A. A could be used for printing all sorts of documents, including innocuous ones printable by everybody, and the gods of service orientation may not be pleased if we'd leak requirements through capability layers. It is B that deals with sensitive information, and that probably is owned by the right business unit for making authorization decisions about it. In other words, implementing this as trusted subsystem may not be an option. This example based on business requirements betrays my enterprise bias, however there are many other cases I can bring up. Perhaps the most classic situation is what I call protocol refraction: if A is a web site instead of a service, and the B service is part of the associated business logic, you have another case where you may want to authorize the use of B according to who is using the website but you don't trust the website itself to execute the necessary rules or to have gathered the correct info in the first place.
We are finally to the main point of this section: since B needs info about C, and since C did send a token to A already, why can't A reuse such a token when it invokes B? It may sound handy, but in fact in the general case this approach would not work. Some reasons:
- Cryptography. The token sent to A will normally be encrypted for A; hence B would not be able to read it anyway, unless A & B share the same key (possible but odd)
- Audience restriction. The SAML tokens have a nice mechanism in place which protects from malicious RPs. A token can contain an element which specifies the URI of the intended recipient; this prevents men in the middle, but also bad RPs, to replay the token with some other endpoint. Meaning: if you are www.Contoso.com and you receive a token whose audiencerestriction is www.fabrikam.com, you know that the token you received was not not intended to be sent to you at issuance time, and you should refuse the call. You would not accept as payment an ordered check where you are not the payee, would you.
- Issuer. Very often A and B expect tokens from different issuers. Think of the web site case: A will accept an "external" identity, while B will likely require tokens from some internal R-STS: the organization & application boundaries are at play here, and the traversals (hence trust relationships) necessary for A and B are different
- Claim set. Quite simply, A and B may need different claims. In the printing example, A may require the "printing department" claim value while B may require a claim CanPrintSalaryInfo to be of value "true". The problem of claim accumulation is another big hit, and will probably deserve exclusive focus in some future post
- Bearer/Holder. If we are in the case in which A is a web site, the token it will receive will likely have token bearer as confirmation method; but B is a service, hence it would be nice to be able to use an holder-of-key token for securing the corresponding call. Another refraction effect induced by the change of "medium"
All of the above conditions allow exceptions; for example, if C calls A with tokens from a non-auditing STS the audience restriction would not be there (the issuer would not know who the intended recipient is to begin with) and the token may not be encrypted (for the same reason). A and B may trust the same issuer, and they may need the same claims (or no claims at all for bare tokens); finally, they may all be services or sites hence there would not be confirmation method refraction. But it is clear that planets really need to align, and the result may be less than satisfying anyway (that is, if you worry about security at all :-)).
So: when traversing a multi layer architecture, reusing tokens across levels is plainly not possible or generally a bad idea (note that this is not in contradiction with the token caching sample, in that case there's one single level and policy matching).
I am sure that, while reading the above, the ASP.NET developers among you kept thinking of the word "impersonation"; and that the kerberos experts did the same with "delegation". You are right on the money, that concept is a very good tool for solving the problem at hand. If you have the luxury of knowing the infrastructure you are targeting, you can use the delegation mechanisms it offers (ie kerberos' constrained delegation); but then how could we justify the "meta" in "identity metasystem"?:)
Using just STSes and not mandating any specific technology: if we could enable A to obtain a token for B that would somehow take into account that the entire operation is happening because C initiated it, at that point the content of the token obtained by A for B may take into account C's characteristics. We say that A obtains a token for B on behalf of C. In this way, we overcome all the problems I listed above: A calls be using a brand new token, issued explicitly for being used with B while taking into account C's attributes. How do we do that? Well, A may include the token he got from C in its RST for getting a token for B. We are getting somewhere!
In fact, at this point I should tell you that not only Zermatt supports the trick above, but that there is a very good example of that in the SDK; you can find it under the intermediate examples, it's the Identity Delegation Scenario. It is pretty neat, in a single solution it shows both the active & passive cases. As you know this is a beta, so the object model may change here and there: however the idea of delegated issuance is rooted in WS-Trust itself (see OnBehalfOf), hence modulo syntactic sugar this stuff will be very useful.
The flight is still long, 8:13 hours, and typing in the Mantis position is pretty tiring. I'll save at this point for now, but I'm sure we'll be back on the topic (probably with some bits for illustrating the point)