Message: ID4243: Could not create a SecurityToken.

2019-03-16 08:41发布

We're getting the exact same error as in this thread ... in our production environment. [WIF Security Token Caching

Does anybody have a fix to this error ? Message: ID4243: Could not create a SecurityToken. A token was not found in the token cache and no cookie was found in the context.

Here is some info about our setup:

• We‘re using built-in Windows Identity Framework with .NET Framework 4.5.1

• The problem is almost always associated with changing from RelyingParty#X over to RelyingParty#Y ( e.g. the moment user clicks the RP#Y he‘s SIGNED OUT without asking for it ) – when he logs in again after this event, he‘s taken right to the page he was asking for, inside RP#Y

• We‘re using e.SessionToken.IsReferenceMode = true; // Cache on server, to get a smaller cookie

• By using IsReferenceMode = true, our FedAuth cookie stores a „pointer“ to the actual Token which is stored inside our database

• We‘re using our own DatabaseSecurityTokenCache which is overriding the functions in SessionSecurityTokenCache. By using the the DatabaseSecurityTokenCache alongside the IsSessionMode = true, we‘re server-farm-friendly ( but we‘re also guaranteed to be within the same server through all our login-session ) so if the application pool for some reason dies, we‘re able to get the token from database through the DatabaseSecurityTokenCache. I‘ve verified this by completely killing IIS in the middle of a session ( with „net stop WAS“ and the restart it again with „net start W3SVC“ and we‘re still able to get the Token from the DatabaseSecurityTokenCache ). I‘ve also tried doing the same by simply using the out-of-the-box SessionSecurityTokenCache and that will fail respectivly ( as expected )

• Default token lifetime is 20 minutes ( but the user can change it to 40 or 60 minutes if he wants to ) – that will only be effective the next time the user logs in ( and 90% of our user are just using the default 20 minutes lifetime )

• We‘re using a certificate (same on all servers) to encrypt the FedAuth cookie, NOT a machine-key ( which would be catastrophic if using server-farm, with different machine-keys )

• so all the servers can decrypt cookies, which were encrypted from another server.

• We have a javascript with a countdown in our RelyingParty4 and RelyingParty5 ( two different relying parties ) which is used as a „timeout script“ in case the user leaves his computer unattended ... he will be signed out when the token is about to expire – (minus) 30 seconds ( e.g. 20 minutes – 30 sec = 19,5 minutes ) with idle time. This is protect our very sensitive banking information, so when the user comes back to his machine he will need to login again. e.g. We‘re also using sliding sessions ([http://www.cloudidentity.com/blog/2013/05/08/sliding-sessions-for-wif-4-5/]) and when we slide, the timing in the javascript of the client is also updated as well, to match the length of the token minus 30 seconds. These 30 seconds are used to make sure that the session is still alive when signing out, so it‘s a little bit shorter than the lifetime of the token/session. We currently sliding if this condition is met: total lifetime / 2 .... e.g. 20 / 2

• We‘re only sliding if there‘s any activity going on with the user ( i.e. he‘s moving around, doing some work ). We're sliding in minute10+ (if token lifetime is 20minuts) as the example above shows

• We‘ve debugged the problem multiple times and this is the WIF error we‘re getting: Exception: System.IdentityModel.Tokens.SecurityTokenException Message: ID4243: Could not create a SecurityToken. A token was not found in the token cache and no cookie was found in the context. Source: Microsoft.IdentityModel at Microsoft.IdentityModel.Tokens.SessionSecurityTokenHandler.ReadToken(XmlReader reader, SecurityTokenResolver tokenResolver) at Microsoft.IdentityModel.Tokens.SessionSecurityTokenHandler.ReadToken(Byte[] token, SecurityTokenResolver tokenResolver) at Microsoft.IdentityModel.Web.SessionAuthenticationModule.ReadSessionTokenFromCookie(Byte[] sessionCookie) at Microsoft.IdentityModel.Web.SessionAuthenticationModule.TryReadSessionTokenFromCookie(SessionSecurityToken& sessionToken) at Microsoft.IdentityModel.Web.SessionAuthenticationModule.OnAuthenticateRequest(Object sender, EventArgs eventArgs) at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

• We‘ve been able to re-produce the bug by using an old FedAuth cookie and this plugin: ( attention! We‘re not sure if this is the same thing that‘s happening on PROD, but at least it gives the same error in our Logging system ) This is good, but I think you should add the steps on how we‘re able to modify the content of the FedAuth cookie, to bring this problem to life, locally.- You can use this: It‘s simple by taking the Value of the FedAuth cookie from some previous sessions ( on the same machine! not from another machine, that won‘t work ) And pasting it into the Value of the FedAuth cookie and refreshing the page.-

Plugin used to modify the cookie, in Chrome is called „Edit This Cookie“: - If we change the content of this cookie to a value from a previous session, and hit the refresh ( CTRL + R in Chrome ) we get the infamous TokenSecurityException ID4243 and the RP calls for a immidiate FederatedSignout because we're unable to recover from this situation.

Signing out....

I should also probably mention that we took's Microsoft MSDN's article marked "Important" on IsReferenceMode seriously and added it also to our

SessionAuthenticationModule_SessionSecurityTokenCreated event:

e.SessionToken.IsReferenceMode = true;

taken from MSDN:

Important! To operate in reference mode, Microsoft recommends providing a handler for the WSFederationAuthenticationModule.SessionSecurityTokenCreated event in the global.asax.cs file and setting the SessionSecurityToken.IsReferenceMode property on the token passed in the SessionSecurityTokenCreatedEventArgs.SessionToken property. This will ensure that the session token operates in reference mode for every request and is favored over merely setting the SessionAuthenticationModule.IsReferenceMode property on the Session Authentication Module.

Below is our whole SessionAuthenticationModule_SessionSecurityTokenReceived, please examine the comments I put into it ... it explains what everything does:

void SessionAuthenticationModule_SessionSecurityTokenReceived(object sender, SessionSecurityTokenReceivedEventArgs e)
    {
        if (e.SessionToken.ClaimsPrincipal != null)
        {
            DateTime now = DateTime.UtcNow;
            DateTime validTo = e.SessionToken.ValidTo;
            DateTime validFrom = e.SessionToken.ValidFrom;
            TimeSpan lifespan = new TimeSpan(validTo.Ticks - validFrom.Ticks);

            double keyEffectiveLifespan = new TimeSpan(e.SessionToken.KeyExpirationTime.Ticks - e.SessionToken.KeyEffectiveTime.Ticks).TotalMinutes;
            double halfSpan = lifespan.TotalMinutes / 2;

            if (validFrom.AddMinutes(halfSpan) < now && now < validTo)
            {
                SessionAuthenticationModule sam = sender as SessionAuthenticationModule;

                // This will ensure a re-issue of the token, with an extended lifetime, ie "slide". Id deletes the current token from our databasetoken cache (with overriden Remove of the SessionSecurityTokenCache ) and writes a new one into the cache with the overriden AddOrUpdate of the SessionSecurityTokenCache. 
                // it will also write the token back into the cookie ( just the pointer to the cookie, because it's stored in database-cache ) because the IsReferenceMode = True is set
                e.ReissueCookie = true; // Will force the DatabaseSecurityTokenCache'ið to clean up the cache with this, handler.Configuration.Caches.SessionSecurityTokenCache.Remove(key); internally in WIF's SessioAuthenticationModule

                e.SessionToken = sam.CreateSessionSecurityToken(
                    e.SessionToken.ClaimsPrincipal,
                    e.SessionToken.Context,
                    now,
                    now.AddMinutes(lifespan.TotalMinutes),
                    false); // Make persistent, þannig að kakan lifir EKKI af browser-close / tab-lokun:
                {
                    e.SessionToken.IsReferenceMode = true; // Cache on server
                }

                // Not needed, because if ReissueCookie = true;  is set, it WILL to a WriteSessionTokenToCookie internally in WIF
                //FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(e.SessionToken); // <---- er þetta e.t.v. bara það sem við þurfum ? Nei, á ekki að þurfa, er gert þegar tóki er búinn til með CreateSessionSecurityToken
            }
            else if (validTo < now)
            {
                // Fix
                // http://blogs.planbsoftware.co.nz/?p=521                    

                var sessionAuthenticationModule = (SessionAuthenticationModule)sender;
                sessionAuthenticationModule.DeleteSessionTokenCookie(); // <--- is this really needed like the article says ? http://blogs.planbsoftware.co.nz/?p=521
                e.Cancel = true; // This will allow a silent-login if the STS cookie is still valid, e.g. switching between RP's where we're switching from an active RP to a RP which has it's cookie outdated, but the STS's session is still alive. We don't want to prompt the user for a new login, beucase the STS session is still OK!
            }
    }

2条回答
beautiful°
2楼-- · 2019-03-16 09:05

---------- UPDATE, This is how Lord02 fixed the proplem -----------

The problem was that when users are coming in with stale cookies ( from a previous session, i.e. if they did NOT sign out from our system ... but instead just closed the tab ) and then logged in again, our cookie which was in SessionMode = true ... tried to go to the DatabaseTokenCache to GET the whole token from database, but as I said our SSIS process deletes all Tokens which are OLDER than 12 hours old (outdated tokens!) so we don't have loads of orphan tokens, which are outdated in our database and are unusuable ... just taking up space in our database. So after this deletion is done, each night, the DatabaseTokenCache GET‘s function would not return a valid Token ... and the user was signed out because of : ID4243: Could not create a SecurityToken. A token was not found in the token cache and no cookie was found in the context.

So instead of NOT deleting the Tokens inside our database I created a special handler, which intercepts this error on the RP‘s site ... and redirects the user back to the STS – which will then Create a brand new token and Write that down to the DatabaseTokenCacheStore, like this below

The exception with ID4243 is thrown when the cookie is set as “reference mode” AND the token is not present in the cache – I can confirm that is by-design and also by-design WIF does not redirect the call to the STS (to start over the authentication process)

To overcome this problem I intercept this exception and react properly. I redirect to the issuer if this error comes up inside a customSessionAuthModule I created for this:

public class CustomSessionAuthenticationModule : SessionAuthenticationModule
{
    protected override void OnAuthenticateRequest(object sender, EventArgs eventArgs)
    {
        try
        {
            base.OnAuthenticateRequest(sender, eventArgs);
        }
        catch (SecurityTokenException exc)
        {
            // ID4243: Could not create a SecurityToken. A token was not found in the token cache and no cookie was found in the context.
            if (exc.Message.IndexOf("ID4243", StringComparison.OrdinalIgnoreCase) >= 0)
            {
                // Returning directly without setting any token will cause the FederationAuthenticationModule
                // to redirect back to the token issuer.
                return;
            }
            else
            {
                throw;
            }
        }
    }
} 
查看更多
甜甜的少女心
3楼-- · 2019-03-16 09:19

this post helped me, so it can help you and others those have this kind of error.

void Application_OnError()
{
  var ex = Context.Error;
  if (ex is SecurityTokenException){
     Context.ClearError();
     if (FederatedAuthentication.SessionAuthenticationModule != null){
         FederatedAuthentication.SessionAuthenticationModule.SignOut();
     }
   Response.Redirect("~/");
  }
}

From this link.

Hope it was useful!

查看更多
登录 后发表回答