I have a WCF Service that requires a security token issued by a separate WCF STS Service. This all works just dandy. In my application, I use the service like so:
MyServiceClient myService = new MyServiceClient();
myService.Open();
myService.DoStuff();
The STS Service is called to get a token and the token is used to call the service method DoStuff.
Once the initial handshake is over, though, the myService
object has the token cached and re-uses it until it expires. This is fine behavior and all, but how would I force it to refresh the token?
myService.ClientCredentials.Invalidate(); // something like this?
Such that if I called DoStuff() again it would know it needs to go to the STS again much as it did the first time.
Am I stuck just making a new proxy class object, i.e. myService = new MyServiceClient();
? This works but it seems like the nuclear bomb solution.
Alternatively, is there a way to just manually get a new token and replace the current one, i.e. myService.ClientCredentials.Renew();
?
If I have to make a custom ClientCredentials class to do this, how would I implement the above example methods?
In my codebase, we actually cache the token so we ensure that we don't make repeated calls to the STS. Using the same method, you could definitely alter it manually request another token whenever you wish. Here's how to hook into ClientCredentials:
public class CustomClientCredentials : ClientCredentials
{
public CustomClientCredentials()
{
}
protected CustomClientCredentials(ClientCredentials other)
: base(other)
{
}
protected override ClientCredentials CloneCore()
{
return new CustomClientCredentials(this);
}
/// <summary>
/// Returns a custom security token manager
/// </summary>
/// <returns></returns>
public override SecurityTokenManager CreateSecurityTokenManager()
{
return new CustomClientCredentialsSecurityTokenManager(this);
}
}
public class CustomClientCredentialsSecurityTokenManager : ClientCredentialsSecurityTokenManager
{
public CustomClientCredentialsSecurityTokenManager(ClientCredentials credentials)
: base(credentials)
{
}
/// <summary>
/// Returns a custom token provider when a issued token is required
/// </summary>
public override SecurityTokenProvider CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement)
{
if (IsIssuedSecurityTokenRequirement(tokenRequirement))
{
// Adds the endpoint behaviors for calling the issuer
IssuedSecurityTokenProvider baseProvider = (IssuedSecurityTokenProvider)base.CreateSecurityTokenProvider(tokenRequirement);
CustomIssuedSecurityTokenProvider provider = new CustomIssuedSecurityTokenProvider(baseProvider);
return provider;
}
return base.CreateSecurityTokenProvider(tokenRequirement);
}
}
public class CustomIssuedSecurityTokenProvider : IssuedSecurityTokenProvider
{
private readonly IssuedSecurityTokenProvider _innerProvider;
public CustomIssuedSecurityTokenProvider(IssuedSecurityTokenProvider innerProvider)
{
_innerProvider = innerProvider;
CacheIssuedTokens = innerProvider.CacheIssuedTokens;
IdentityVerifier = innerProvider.IdentityVerifier;
IssuedTokenRenewalThresholdPercentage = innerProvider.IssuedTokenRenewalThresholdPercentage;
IssuerAddress = innerProvider.IssuerAddress;
IssuerBinding = innerProvider.IssuerBinding;
innerProvider.IssuerChannelBehaviors.ForEach(IssuerChannelBehaviors.Add);
KeyEntropyMode = innerProvider.KeyEntropyMode;
MaxIssuedTokenCachingTime = innerProvider.MaxIssuedTokenCachingTime;
MessageSecurityVersion = innerProvider.MessageSecurityVersion;
SecurityAlgorithmSuite = innerProvider.SecurityAlgorithmSuite;
SecurityTokenSerializer = innerProvider.SecurityTokenSerializer;
TargetAddress = innerProvider.TargetAddress;
innerProvider.TokenRequestParameters.ForEach(TokenRequestParameters.Add);
_innerProvider.Open();
}
~CustomIssuedSecurityTokenProvider()
{
_innerProvider.Close();
}
protected override SecurityToken GetTokenCore(TimeSpan timeout)
{
// We're ignoring the CacheIssuedTokens property in order to force an STS call
var securityToken = _innerProvider.GetToken(timeout);
return securityToken;
}
}
The GetTokenCore() method is where the STS gets called. When you call GetToken(), the STS will be asked to issue another token.
From your question, I assume you know how to hook into your ClientCredentials from the app.config.
There might be a way of setting the CacheIssuedTokens property in the App.config file, I'm just not sure of a way off the top of my head.
Could you not use the IssuedToken.MaxIssuedTokenCachingTime
property and set it to 0?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With