Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I force a refresh of the security token for a WCF service?

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?

like image 615
Sean Hanley Avatar asked Nov 09 '10 20:11

Sean Hanley


2 Answers

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.

like image 154
pdalbe01 Avatar answered Oct 26 '22 11:10

pdalbe01


Could you not use the IssuedToken.MaxIssuedTokenCachingTime property and set it to 0?

like image 28
Robert MacLean Avatar answered Oct 26 '22 11:10

Robert MacLean