SSO with Windows Identity Foundation: Part VI - Single Sign Off
Edits / Updates:
I was using the term "realm" incorrectly semantically, realm means the target system/domain the RP wants to login to.
Example code no longer requires you to install any certificates
Preface
By democractic vote, our topic today is Single Sign Off using Windows Identity Foundation.
All of the code for this part is available here.
To start off simply. Take the exact code we ran in the previous tutorial, and call the following URL on either relying party:
http://client1.local/?wa=wsignoutcleanup1.0
That will open a mysterious image with a green tick mark for success.
What's more curious is that, if you call that endpoint, and then return to the root URL - you'll notice it logged you out.
It turns out, the HTTP Module we've already seen take care of most things for us in previous tutorials, WSFederationAuthenticationModule, is to blame for said menagerie.
It also turns out, said functionality is extremely convenient. If we can, say, call that URL from our STS after the user decides to send a log out signal to it, then we'll achieve single sign off.
The sign-out process:
1.. Relying party calls http://sso.local/?wa=wsignout1.0&wreply=http://client1.local/
2. STS receives call, and removes user's data:
FederatedAuthentication.SessionAuthenticationModule.SignOut();
3. STS returns html which has a bunch of img-tags for each relying party we need to log the user out of
4. Little bit of javascript, which redirects to the wreply-address after the dom ready event fires, aka, the above images have loaded.
That's pretty simple so far.
Difference between wsignout1.0 and wsignoutcleanup1.0?
The wsignoutcleanup1.0 is meant for a client of any kind simply to sign out the user - nothing else. On the other hand, the wsignout1.0 action is likely to be paired with more logic than just sign out. So a reply-to -address might be required to be paired with it, and it might also call other domains and do other things on sign-out, which is exactly why the RP in our case calls the STS with wsignout1.0 and the STS calls our RP with wsignoutcleanup1.0?
Federated Passive Security Token Service Operations - Wait what?
You might remember from the previous tutorial this line:
Now this line basically achieved all of what we did before in one fell swoop, and if you've been paying attention you might ask yourself, can I just keep this bit of code in for signout, and things will basically just be automatically done for me.
And it turns out you can! If you pass a wsignout1.0 action to that function, you'll be signed out of the STS, and redirected back to the wreply-address. You won't however be logged out on the relying party. You'd have to do something extra to make that happen, for example having a wsignoutcleanup1.0-parameter in the replyto-address.
That'd be slightly convoluted: (http://sso.local/?wa=wsignout1.0&wreply=http://client1.local?wsignoutcleanup1.0) That is nasty... It also only works for one domain.
For most cases that might still work for you, but in our case, we want the user to be signed out from all domains that they have signed into. We want to have our STS know where the user is currently signed in, and we only want to call a signout for domains they ever signed into.
When in doubt, roll your own
So what we'll do is, upon receiving a request on the STS, we'll pull that request apart, and if it's a sign in request, we'll do as we did before, but for sign out, we'll return an MVC view, with the above img-tag magic.
Here's how we achieve this:
So we pull the request apart with a simple call to WSFederationMessage.CreateFromUri, and then go on two paths based on that.
If we're signing in, we do simply the same thing we did before in the previous tutorials, aside from one thing:
When a user signs in, we want to add to their information, where they have signed in, and we do this simply via:
user.AddClaim(new Claim(ClaimTypes.Uri, replyToAddress));
Later, when the user signs out, we run this bit of code:
Note, we simply figure out which URLs they have signed into, and pass those into our view. Here's what the view looks like:
That will log you out of all the the domains you were signed into, and then redirect back to the replyTo-address.
That's all there is to it.
Considerations:
- If you have your RP configured to have passiveRedirectEnabled to true, this will mean that whenever your RP reaches code that has an Authorize-attribute present, it will perform an automatic redirect to the STS. With the correct combination of wreply-address and wsignout, and the way our project now uses a mock identity to sign the user in every time they call the STS - you can make this infinitely loop between the RP and the STS. Make sure you know what you're doing.
- Check your anonymous access policies in your system.web/authorization. You will want to control when a redirect occurs, and denying all access will simply make the RP redirect to the STS for every single request until they login.
To view all the code for all of the above, and a working example, simply visit this tutorial's code in Github.
Windows Identity Foundation Tutorial
I have also included a .bat file which will set up IIS sites and set up your hosts file for you to run this example.
Cheers everyone for the support for this series, and I hope to challenge myself further with equally interesting topics in the future.