.NET Framework Developer Center >
.NET Development Forums
>
Claims based access platform (CBA), code-named Geneva
>
Is it possible to have a local token cache service?
Is it possible to have a local token cache service?
I'm working on a scenario where we have a win forms app with a wide installbase, which will be issuing frequent calls to various services hosted by us centrally throughout it's operation.Out of the box, using the ws2007FederationHttpBinding the app can be configured to retrieve a token from the STS before each service call, but obviously this is not the most efficient way.Alternatively, I have implemented the code required to retrieve the token "manually" from the app, and then call the service providing this token (based on the WSTrustClient sample and help on this forum - thanks!), and that works well, but - I feel - is not very elegant as it requires building the WCF channel in code, moving away from the wonderful WCF configuration.I much prefer the ws2007FederationHttpBinding approach where by the client simply calls the service like any other WCF service, without knowing anything about Geneva, and the bdingings takes care of the token exchange.And then someone gave me [what I think is] a great idea - add a service, hosted in the app itself, using netpipes or something, to cache locally retrieved tokens.The local cache service would implement the same contract as the STS; when receiveing a request it would check to see if a cahced token exists, and if so would return it, otherwise it would call the 'real' STS retrive a token, cache it and return it.The client app could thenstill use ws2007FederationHttpBinding, but instead of having the STS as the issuer it would have the local cache;This way I think we can achieve the best of both worlds - caching of tokens without the service-sepcific custom code; our cache should be able to handle tokens for all RPs.I have created a very simple prototype to see if it works, and - somewhat not surprising unfortunately - I am slightly stuck -My local service (currently a console app) get's the request, and - first time around - calls the STS and retrieves the token.It then succesfully returns the token to the client, which uses it succesfully to call the RP. all works well.Second time around, however, my local cahce service tries to use the same token again, but the client side fails with a MessageSecurityException -"Security processor was unable to find a security header in the message. This might be because the message is an unsecured fault or because there is a binding mismatch between the communicating parties. This can occur if the service is configured for security and the client is not using security."Is there something preventing the same token to be used more than once? I doubt it because when I reused the token as per the WSTrustClient sample it worked well; what am I missing? is my idea possible? a good one?Here's the (very basic, at this stage) code of the local cache -static LocalTokenCache.STS.Trust13IssueResponse cachedResponse = null; public LocalTokenCache.STS.Trust13IssueResponse Trust13Issue(LocalTokenCache.STS.Trust13IssueRequest request) { if (TokenCache.cachedResponse == null) { Console.WriteLine("cached token not found, calling STS"); //create proxy for real STS STS.WSTrust13SyncClient sts = new LocalTokenCache.STS.WSTrust13SyncClient(); //set credentials for sts sts.ClientCredentials.UserName.UserName = "Yossi"; sts.ClientCredentials.UserName.Password = "p@ssw0rd"; //call issue on real sts STS.RequestSecurityTokenResponseCollectionType stsResponse = sts.Trust13Issue(request.RequestSecurityToken); //create result object - this is a container type for the response returned and is what we need to return; TokenCache.cachedResponse = new LocalTokenCache.STS.Trust13IssueResponse(); //assign sts response to return value... TokenCache.cachedResponse.RequestSecurityTokenResponseCollection = stsResponse; } else { } //...and reutn return TokenCache.cachedResponse;
Yossi Dahan | [To help others please mark replies as answers if you found them helpful]- Edited byYossi DahanMVPTuesday, December 30, 2008 1:19 PMForgot to add code
Answers
- "Out of the box, using the ws2007FederationHttpBinding the app can be configured to retrieve a token from the STS before each service call, but obviously this is not the most efficient way. "This is not true - the token gets retrieved once per proxy (ignoring token lifetime here). As long as you hold on to the proxy you can re-use the token.HTH
Dominick Baier - http://www.leastprivilege.com- Marked As Answer byYossi DahanMVPTuesday, December 30, 2008 2:20 PM
All Replies
- "Out of the box, using the ws2007FederationHttpBinding the app can be configured to retrieve a token from the STS before each service call, but obviously this is not the most efficient way. "This is not true - the token gets retrieved once per proxy (ignoring token lifetime here). As long as you hold on to the proxy you can re-use the token.HTH
Dominick Baier - http://www.leastprivilege.com- Marked As Answer byYossi DahanMVPTuesday, December 30, 2008 2:20 PM
- Now - this is properly embarrasing! (let's just say I was testing you...)I knew something didn't make sense, but missed the big point that I'm re-creating the proxy, when I don't need to.Anyway - looking at this I also found the Durable Issued Token Provider sample which I still need to look at more closely as it seems to be a very elegant of caching tokens through an endpoint behaviour on the client side, although now I may have slighlty less case for spending the time :-)Glad I asked! :-)
Yossi Dahan | [To help others please mark replies as answers if you found them helpful] - This is not true - the token gets retrieved once per proxy (ignoring token lifetime here). As long as you hold on to the proxy you can re-use the token.
Dominick Baier - http://www.leastprivilege.com
I have read this behavior, that the token is fetched once per proxy. This does not seem to be the case, I have a service defended with an IssuedToken endpoint. The endpoint allows it to be invoked via a delegated token. Given the following client code:
As you can see from the comment in the code snippet, the 2nd call over the same proxy generates a exception with the message: ID3266: The FederatedSecurityTokenProvider cannot support the FederatedClientCredentialsParameters. The FederatedClientCredentialsParameters has already provided the ActAs parameter.const string endpointName = "IssuedTokenEndpoint"; var principal = Thread.CurrentPrincipal as IClaimsPrincipal; ReadOnlyCollection<SecurityToken> callerTokens = principal.GetBootstrapTokens(); var factory = new ChannelFactory<IShoppingCartService>(endpointName); factory.Credentials.ClientCertificate.Certificate = CertificateHelper.GetCertificateFromStore("76 42 47 aa c6 be b5 4f 4d cf 18 8c 40 43 5c b5 0b 4e c3 72", X509FindType.FindByThumbprint, StoreName.My, StoreLocation.LocalMachine); factory.ConfigureChannelFactory(); var proxy = factory.CreateChannelActingAs(callerTokens[0]); try { lblProductDescription.Text = proxy.GetProductDescription("Called Once"); // This executes fine lblProductDescription.Text = proxy.GetProductDescription("Called Twice"); <strong>// This raises a FederatedSecurityTokenProvider exception</strong> } catch (Exception ex) { lblError.Text = ex.ToString(); }
a review of the stack trace, shows it is again attempting to fetch the token
at Microsoft.IdentityModel.Protocols.WSTrust.FederatedSecurityTokenProvider.SetupParameters()
at Microsoft.IdentityModel.Protocols.WSTrust.FederatedSecurityTokenProvider.GetTokenCore(TimeSpan timeout)
at System.IdentityModel.Selectors.SecurityTokenProvider.GetToken(TimeSpan timeout)
at System.ServiceModel.Security.SecurityProtocol.GetToken(SecurityTokenProvider provider, EndpointAddress target, TimeSpan timeout)
at System.ServiceModel.Security.MessageSecurityProtocol.GetTokenAndEnsureOutgoingIdentity(SecurityTokenProvider provider, Boolean isEncryptionOn, TimeSpan timeout, SecurityTokenAuthenticator authenticator)
at System.ServiceModel.Security.SymmetricSecurityProtocol.TryGetTokenSynchronouslyForOutgoingSecurity(Message message, SecurityProtocolCorrelationState correlationState, Boolean isBlockingCall, TimeSpan timeout, SecurityToken& token, SecurityTokenParameters& tokenParameters, SecurityToken& prerequisiteWrappingToken, IList`1& supportingTokens, SecurityProtocolCorrelationState& newCorrelationState)
at System.ServiceModel.Security.SymmetricSecurityProtocol.SecureOutgoingMessageCore(Message& message, TimeSpan timeout, SecurityProtocolCorrelationState correlationState)
at System.ServiceModel.Security.MessageSecurityProtocol.SecureOutgoingMessage(Message& message, TimeSpan timeout, SecurityProtocolCorrelationState correlationState)
at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message).
As a result, I am forced to create a new proxy (and new token) for every operation.


