External authentication with Claims and WS-Federation in MVC4 .NET4.5 Part 4: Single SignOut and Single SignOn
March 18, 2013 26 Comments
In the previous post we left off with the shortcomings of the Logout function: we log out of the web application but the session is still alive on the STS. Would like to end all our sessions when we press Logoff on the main screen. Similarly if there are multiple web sites that share the same STS then we would prefer to log in once on the STS login page and then be able to use all those applications.
These techniques are called Single SignOn and Single SignOut.
We will build on the MVC4 application we have been working on in this series of blog posts.
Single SignOn
Single SignOn is really nothing else but an application of the widely used “Remember Me” function. Say you have two web applications, SiteA and SiteB that share the same STS. You start the day by logging in to SiteA and do some work there. You’ll of course use the STS login page. The STS will establish a login session with the client. The browser will send the FedAuth cookie with all subsequent requests.
Then at some point you need to use SiteB. SiteB also needs authentication and redirects the user to the same STS. However, the STS will recognise the user’s FedAuth cookie and will issue another token for SiteB without having to log in again.
Therefore we get Single SignOn accross all applications that use the same STS.
Single SignOut
This requires some more steps. It is necessary to clean the local session cookie but it’s not enough. We need to inform the STS that we want to sign out, so please dear STS, end the auth session at your side too. The STS will need to clear its own session cookie so that the Single SignOn session is terminated. Optionally it can also tell the other relying parties that the user wants to sign out, but this is not mandatory. If you only clear the local session cookie of SiteA and the session at the STS, then when you go over to SiteB the STS will not recognise you any more as you killed the auth session. So you will need to sign in again.
If you want to end the auth sessions in all relying parties you have accessed before you click the Logoff button then the STS will need to keep track of all apps you have used since your first log in. Then when you log out of SiteA then the STS will ‘contact’ SiteB to end the auth session there too.
You’ll recall from our discussion of the URL format for logging that it had the section ‘?wa=wsignin1.0’. There is a similar URL format for signing out: ‘?wa=wsignout1.0’. The client will send a GET request to the STS with this URL. The STS in turn will look up in its records which relying parties the user has logged in to since they first logged on. The STS will respond the client with a HTML that may look like this:
<p> <img src="https://relyingparty1/?wa=wsignoutcleanup1.0" /> </p> <p> <img src="https://relyingparty2/?wa=wsignoutcleanup1.0" /> </p>
And possibly many possible solutions exist as to how to send the signoutcleanup message, the above HTML is not unique at all.
The main bit is the signoutcleanup message: http://relyingparty1/?wa=wsignoutcleanup1.0
The client browser will contact all the relying parties in the message and send them a signoutcleanup message.
To recap:
- The client clear his/her local auth cookie
- The client contacts the STS
- The STS clears its own session cookie
- STSs replies with a page similar to the above markup
- The client uses that message to inform the other relying parties about the logoff event
- The relying parties clear their own auth cookies
Demo
Open the MVC4 appluication we’ve been working on in this series. Run the application and press F12 to open the Developer Tools just as we did before in the Claims series. Select Start capturing on the Network tab. On the home page of the MVC4 application select the About link to trigger a sign in. This time check the Remember Me checkbox:
The STS will set two cookies: idsrvauth and wsfedsignout:
The idsrvauth cookie is the logon session with the STS itself. The wsfedsignout cookie is a tool for the STS to keep track of the relying parties the user has logged into.
The STS will issue a cookie to establish a logon session with the client. You can see the FedAuth cookie issued by the STS in Developer Tools:
Close the browser running the MVC4 app on localhost and re-run the application. We do this in order to simulate two things:
- The user wants to re-enter the same web app after closing it first
- The user is trying to enter another relying party which uses the same STS for authentication
Click the About link to trigger the authentication. You should see the following:
- You are redirected to the STS login page
- The login page never actually loads
- You are redirected to the About page automatically
This is the effect of the Remember Me checkbox. The STS recognises the previously established logon session with the user and does not require the login name and password again. The STS simply issues a new auth token.
This is the essence of Single SignOn and this is the solution for multiple relying parties on different web servers.
We now have to implement Single SignOut. Locate the LogOff action within AccountController.cs. Currently it may look similar to the following:
[HttpPost] [ValidateAntiForgeryToken] public ActionResult LogOff() { FederatedAuthentication.WSFederationAuthenticationModule.SignOut(); return RedirectToAction("Index", "Home"); }
This will only end the auth session in the MVC4 web app but not at the STS. Test it for yourself: run the application, click on ‘About’ and after logging in click the Log off link in the top right hand corner. You have successfully logged out of the MVC4 app. However, click About again. You will be redirected to the MVC4 without having to log in. This may be a good thing in some cases, but a Log off should mean LOG OFF, right?
Let’s update the LogOff action as follows:
[HttpPost] [ValidateAntiForgeryToken] public ActionResult LogOff() { WSFederationAuthenticationModule authModule = FederatedAuthentication.WSFederationAuthenticationModule; //clear local cookie authModule.SignOut(false); //initiate federated sign out request to the STS SignOutRequestMessage signOutRequestMessage = new SignOutRequestMessage(new Uri(authModule.Issuer), authModule.Realm); String queryString = signOutRequestMessage.WriteQueryString(); return new RedirectResult(queryString); }
We first get a reference to the WS Federation authentication module. Then we clear the local cookie using the SignOut method. Then we construct a signout request message. The constructor requires the Issuer Uri and the Realm which will be read from the web.config. The WriteQueryString() method will form a valid ‘wsignout’ URL which will be understood by the STS. It will ‘know’ that the user wants to log out. You can set a breakpoint at ‘String queryString = signOutRequestMessage.WriteQueryString();’ to see what the signout request looks like.
Run the application and click the About link as usual. Click the Log off link and code execution should stop at the breakpoint within the LogOff action. You can now inspect the queryString variable and see what a signout URI looks like. We are redirected to the STS and we’re greeted with the following message:
The signout URI will look similar to the following:
https://andras1/idsrv/issue/wsfed?wa=wsignout1.0&wreply=http%3a%2f%2flocalhost%3a2533%2f
You’ll see the ‘wsignout’ command and the ‘wreply’ which provides a way to return to the calling application.
Inspect the HTML source of the SignOut page of the STS. You’ll find the wssignoutcleanup command embedded in an iframe:
Click ‘Return to the application” and the select the About link. You will see that you have to provide your username and password again on the STS homepage meaning that we successfully killed off the auth session at the STS as well. The session was ended at all relying parties in the STS records as well.
This finishes up this blog post. The next one which will be the last post in this series about Claims in .NET4.5 will discuss the following more advanced topics:
- Home realm discovery
- Federated Sign-In
- Resource STS and Federation Gateway
You can view the list of posts on Security and Cryptography here.
these articles have really helped me get Single sign on/sign out working with my WSFederated MVC4 application. I do have one problem. My logout is not working correctly. I have two websites logged in using the token issued by the STS server, and that works fine. Yet, when I go to log out, I’m only getting logged out of the web application on which I hit the logout button. After I end up at the sign out page on the STS server (I’m using Thinktecure Id Server 2.0 b/c I’m following these articles), the website on which I did not hit the logout button still think it’s logged in. I looked at the source view of the logout page on the STS server, and there is only one <img src embedded in an iFrame, and in this situation, there should be two. That means the STS server isn't tracking the relying parties properly? Or it could be a code/config thing on my end. Any ideas?
When you sign in using the thinktecture server it registers a cookie with all the applications it is currently tracking. You’ll find an example of that in the blog. Can you verify the contents of that cookie? What does it look like after you have logged in from both web apps?
The contents of the cookie are encrypted… so do you mean to verify the contents is just to eye up the encrypted cookie in Fiddler, or is there a better way to look? Also, does it matter that I’m running this locally in VS2012 using IIS Express and just two localhost web sites (aka, http://localhost:portnumer) or do I need to have an actual URL or real IIS web server in order to work with Thinktecture Identity Server correctly?
No, I meant the other cookie, wsfedsignout. What value do you see there?
here is the the full dump from Fiddler when signing out of one site starting with me clicking the logout button. This dump is from the first site only (http://localhost:55662), I haven’t manually logged out of the the second site yet:
1. request to localhost/Acccount/Logoff (GET /Account/LogOff HTTP/1.1) has these cookies in the request header:
– FedAuth=77u/PD94…;
– FedAuth1=UXhiU1Rh…;(Fiddler displays this cookie as FedAuth1 with a plus symbol “+” next to it)
2. the request to the Id Svr (GET /idsvr/issue/wsfed?wa=wsignout1.0&wreply=http%3a%2f%2flocalhost%3a55662%2f HTTP/1.1) has these cookies in the request header:
– idsrvauth=77u/PD94…; wsfedsignout=http://localhost:55662/
3. the request back to localhost (GET /?wa=wsignoutcleanup1.0 HTTP/1.1) has no cookies in the request or response headers
Two more questions for you.
1.Do I need to add all sites that will share the token to the section of web.config for each site?
2. when I’m trying to share the auth token from the id svr between two sites, is it required of the user to click the “remember me” checkbox on the login page in order for that auth token to be shared, or is that not required?
Thank again for your time and help with this. Mike
Here is the dump from manually logging out of the second site (locahost:55908). Sorry, forgot to include this:
1. request to locahost:55908/Account/Logoff(GET /Account/LogOff HTTP/1.1) has these cookies in the request header:
– FedAuth=77u/PD94…;
– FedAuth1=cEFoUG40…; (Fiddler displays this cookie as FedAuth1 with a plus symbol “+” next to it)
2. the request to the Id Svr (GET /idsvr/issue/wsfed?wa=wsignout1.0&wreply=http%3a%2f%2flocalhost%3a55908%2f HTTP/1.1) has no cookies in the request or response headers
3. the next request to the Id Svr (GET /idsvr/account/signin?ReturnUrl=%2fidsvr%2fissue%2fwsfed%3fwa%3dwsignout1.0%26wreply%3dhttp%253a%252f%252flocalhost%253a55908%252f&wa=wsignout1.0&wreply=http%3a%2f%2flocalhost%3a55908%2f HTTP/1.1) has this cookie in the request header:
– wsfedsignout=http://localhost:55908/
OK, I see. This requires some deeper debugging as the symptoms may be pointing at an issue within the Thinktecture identity server over which I have no control. The wsfedsignout cookie is supposed to be set at sign-in already. I’ve posted an enquiry on the Thinktecture issue page. I will let you know as soon as I’ve got a reply.
1.) no, each site using a single sign on server are independent. It is the responsibility of the identity server to hand out the correct cookies to each consuming website
2.) you need to select the rememberme checkbox to apply single sign-on. If you have siteA and siteB both consuming the login service, and you log on from siteA without applying the RememberMe option, siteB will not be logged on automatically. To make the identity server remember that you were there before you have to choose that checkbox.
//Andras
Andras. Thank for all the help. If it means anything, I’m running version 2.1, not version 2.0, so not too sure if 2.1 is really a final release or still considered a “beta”. Where is the link to the Thinktecture issue page? Also, are you a contributor to Thinktecture?
Mike, no, I’m a simple user like you 🙂
I chose Thinktecture’s solution because it’s for free and is relatively small in order to get started with an identity server. Commercial products, like that of Oracle, are too big for this purpose I think.
I opened this issue yesterday: https://github.com/thinktecture/Thinktecture.IdentityServer.v2/issues/197
You may need to have an account on Github.
If you feel like it you can download their sourcecode and play with it.
//Andras
I do have an account on GitHub. Have you tried to quickly create two web apps yourself and verify what I’ve found before posting to GitHub? If so, I’m assuming you’re seeing the same thing I am?
I currently work for a company where we need to add single sign on/out to the solution, I was the lucky recipient of this task which lead me to Thinktecture’s IdentityServer. The LocalSTS VS2012 extension is not that great, and I figured we’d eventually need to pick an Id Svr for when we deploy to our test servers.
Anyhow, I’ll keep an eye on the thread, and if there is anything I can do to help track down the problem, let me know.
Mike
Mike, yes, i tried the same thing and saw the same behaviour. I hope the Thinktecture guys can take a look at that soon.
//Andras
Andras, it looks like Dominic replied a day or two ago with his findings about this. He says he can’t reproduce it, then gives a downloadable Fiddler trace into what was happening when it was working for him. Since we’ve last talked, I had to change direction, and we’re now taking a shared domain authtentication route using FormsAuth and the same machineKey on each of our servers. I’ll try to double check his Fiddler trace against what I was seeing when it was not working for me to see if I can identify the problem, but I might not be able to get around to it any time soon.
Mike, yes, I’ve seen his reply as well. It might have been an error in the Visual Studio setup that I missed. I’ll redo the entire process as soon as I get the opportunity – it may take a while as this is strictly an off-the-job freetime activity from my side.
Still I think it was a valuable exercise and it doesn’t change the underlying infrastructure of single signon and single signout.
First of all, András, I want to congratulate to your blog. Best .NET how-to source so far 🙂 Is the following scenario supported: I have a web app (e.g. MVC4), an STS (trusted with web app), and a WCF service (also known by the STS – so trusted). I want to call a webservice operation from my web app, of course I want to be authenticated on Webservice as well.
I’ve implemented the following test solution (Console App calls a web service operation and using localSTS): http://msdn.microsoft.com/en-us/library/jj161104.aspx
but I get an exception with the following message:
The channel is configured to use interactive initializer ‘System.ServiceModel.Security.InfocardInteractiveChannelInitializer’, but the channel was Opened without calling DisplayInitializationUI. Call DisplayInitializationUI before calling Open or other methods on this channel.
I could not found any further relevant information regarding this message. Have you ever done similar scenario?
Thanks for your reply,
Szevasz Bence,
Thanks for your kind words.
Unfortunately I’m not familiar with WCF too much – I used it before in .NET3.5 but it was before I got going with claims and before I discovered Web API to build web services.
Can any of these links be of help maybe?
http://social.msdn.microsoft.com/Forums/vstudio/en-US/16192692-868e-46cb-9bed-1bf60539c823/wcf-routingservice-cannot-pass-token-to-wifsecured-service?forum=wcf
http://msdn.microsoft.com/en-us/library/bb246060.aspx
http://code.msdn.microsoft.com/windowsdesktop/WCF-Interactive-Channel-ec0caaa8
Good luck,
András
Thank you for your reply. Actually I’ve catched the original exception of the MSDN sample project (“No version of the CardSpace service was found to be installed on the machine. Please install CardSpace and retry the operation.”). Someone mentioned the same error on Identity and Access tool Reviews section, but no one wrote a solution for that. I keep finding further for the right configuration.
However I’m interested in Web API as well. Do you restrict your Web API controllers to GET POST PUT DELETE operations, or you have multiple GET operations, and you configure your web api routes to be aware of “actions”, so it looks like an MVC controller? Why is it easier or comfortable to use Web API instead of WCF in an enterprise architecture?
Köszi,
Bence
Web API is a large topic to describe in a comment field, but you can start here: Web Api hom page. You can have multiple Get() actions in the same controller: Get(), Get(int id), Get(RequestArguments args), etc. The same goes for Post(), Put(), etc. As long as you go with the default routes you won’t need to touch them at all.
I have a series of posts where I build up a model application whose ultimate consuming layer is a Web API web service. It starts here. If you want to jump to the Web API related bits then look at this post.
Out of the top of my head I can mention the following advantages of Web API compared to WCF:
All in all I think web API provides a very modern HTTP based web service solution that can be scaled and deployed the same way as a “normal” MVC web app. If you absolutely must support other protocols such as TCP then WCF is still the way to go. Also, WCF offers some more complex technologies, like Duplex messaging, that are not available in Web API – at least I’m not aware of anything like that.
//András
I’ve already worked with Web API, but never thought to use it instead of wcf services. Maybe it’s time to discover its real power. It was convenient to let svcutil generate proper proxy class for my webservice.
Have you already had similar kind of project, where you had a front-end web app with outsourced login to an STS, and the front-end called a back-end service, which had to know the identity of caller. (Of course I don’t want to send the userID as a method parameter in every call.) I want to use the power of security token, that was given to the front-end from the STS. I’ve found a couple of samples where the client requested for an ‘ActAs’ token, but all of them used UserName credentials, where the client app asked for login/pass.
As a clue, I can tell, the back-end service is internal, a trusted subsystem.
Köszi,
Bence
Sort of, yes, not quite the same scenario as the one you’ve described but similar. We send along the necessary claims as key-value pairs to the back-end system, but in our case the back-end is not an internal, trusted subsystem. We also send along the encrypted username / pw combination in the http header so that the back-end can further check the identity of the caller.
//András
at least, I could get rid of this token & STS nightmare. I’ve implemented a quite good back-end service using Web API. Now I have to get familiar writing an elegant task-based proxy class for it. Thank you for your answers.
Köszi,
Bence
Can’t figure out, where these iframe markup generations are from? Cannot manage to receive them, trying to generate manually, but after calling FederatedSignOut all view data is ignored…
public ActionResult SignOut()
{
var replyingPartyLinks = new List();
replyingPartyLinks.Add(string.Format(“http://localhost:52895?wa={0}”, CleanUpSignOutParameter));
// Sign out of STS.
WSFederationAuthenticationModule.FederatedSignOut(new Uri(ConfigurationManager.AppSettings[“IssuerName”]), new Uri(“http://localhost/loginpoint.dev”));
return View(replyingPartyLinks);
}
Hi Andras!
Thanks for so brilliant explanation!
Please, correct me if I`m wrong: there are two ways to approach SSO experience. The first I would call “one cookie” and the flow is: when ws-federation dance starts user don’t click “Remember me” and SAM creates a cookie with default “FedAuth” name for App1 and encrypts it with machine key instead of DPAPI, App2`s SAM happily decrypts the cookie (both apps share the same key) and authenticates user. Pros: no need a passive redirection on App2 side, very well fit for active JS client and REST API (JS client sees 401 and starts authentication), only one cookie, very simple sign out. Cons: apps have to share a machine key, if App1 made some claims transformation rest of applications never know it. The second is standard flow when user clicks “Remember me”, each SAM creates its own cookie. Pros: no need to share a machine key, each app is free to do everything with claims. Cons: WIF on REST API app side has to redirect to ip-sts instead of return 401, sign out is harder.
What do you think, is the first approach correct?
Logout not happening in Internet explorer but works fine in chrome and firefox ,instead my session remain active even when signout page of adfs has come using https://{DNS_name_of_RP_STS}/adfs/ls/?wa=wsignout1.0
kindly help on this .been in this mess from a very long time
Hello Andras,
I get FedAuth and FedAuth1 cookies from ADFS. However, How can use this cookie to get the user’s identity? The relying party application (ASP.NET) needs it to initialize and get the user details from DB
Thanks,
Kiran
Hello Andras,
I get FedAuth and FedAuth1 cookies from ADFS. However, How can use this cookie to get the user’s identity? The relying party application (ASP.NET) needs it to initialize and get the user details from DB
Thanks,
Kiran