Dealing with Token Timeout in Windows Identity Foundation
When a Security Token Service (STS) creates a token, that token has an absolute expiration.
Usually this is about 60 minutes, after which the Relying Party (RP) has to send the
user back to the STS to acquire a new token. When using Windows Identity Foundation
(WIF) in ASP.NET (so for passive federation), this is the default behavior, because
the SessionAuthenticationModule stores the token in the FedAuth cookie and checks
that token on each request.
What’s the problem?
I hear you thinking “Cool, WIF takes care of all that for me”, and that is cool. But
there’s also a nasty side effect, one which all web developers have encountered in
a different context: sessions. If a user logs in, starts filling out some form, and
then gets interrupted by a phone call, chances are that the session expires. This
can lead to problems when the user submits the form, because the user has to log in
again. The original posted data gets lost in the process, much to the frustration
of the user. Because sessions use sliding expiration the problem is minor. But with
absolute expiration, expiration can wreak havoc even if the user posts a form just
a minute after the form was presented to the user.
Solving the problem
There are several ways to solve the problem outlined above:
-
Don’t use the SessionAuthenticationModule.
-
Modify the stored token to implement sliding expiration.
-
Force reacquiring a token in the background, so you can control when this happens.
Ditching the SessionAuthenticationModule
Not using the SessionAuthenticationModule appears to be simple. Just remove it from
web.config and you’re done. However, if you remove it, you have to ensure a user stays
logged in after receiving the token, and you need to keep track of the received claims.
This means you need to use a cookie or session data to keep track of the user. Basically
you would be recreating what the SessionAuthenticationModule does for you for free.
Making token expiration sliding
This is actually easier than ditching the SessionAuthenticationModule. All you have
to do is handle the SessionSecurityTokenReceived event and modify the ValidTo property
of the token, as shown in this MSDN
Forums post.
Tip: You can hookup the event in the Application_Start event in global.asx using FederatedAuthentication.SessionAuthenticationModule
to point to the module.
Force reacquiring a token
The above methods work fine, but have some drawbacks. One of these is the fact that
Single Sign-On (SSO) no longer works properly if a user spends too long in a single
application. This is because the login session with the Identity Provider will expire
at some point. Another issue arises if you want to use delegation or impersonation
when you call a web service. WS-Trust 1.4 supports delegation (ActAs) and impersonation
(OnBehalfOf). Even though WIF officially implements WS-Trust 1.3, WIF does support
these constructs. When calling web services from a web application, using delegation
is a recommended practice, because it greatly improves the security. The reason is
that in order for the web application to make the web service call on your behalf,
it needs to acquire a token from the STS, based on the original token. This means
a malicious user would need that token in order to make the web service call. Breaking
into the web application is not enough.
So, how can you ensure you get a new token once in a while? Although the implementation
is somewhat more difficult, the principle is simple: logout of the RP. That triggers
the RP to re-authenticate the user by redirecting to the STS. As long as the user
is still known in the STS, a new token is transparently given out. You can do this
in roughly two ways: in an invisibl