Refresh Security Token for Microsoft Dynamics CRM Connection

In my previous blog post I used singleton pattern to keep the connection open to Dynamics CRM organization service. The only caveat of this method is that you need to monitor your token expiry depending on your implementation method and renew it before expiry.

From MSDN best practices:

Monitor your WCF security token (Token) and refresh it before it expires so that you do not lose the token and have to start over with authentication. To check the token, create a custom class that inherits from the OrganizationServiceProxy or DiscoveryServiceProxy class and that implements the business logic to check the token. Or wrap the proxy classes in a new class. Another technique is to explicitly check the token before each call to the web service. Example code that demonstrates these techniques can be found in the ManagedTokenDiscoveryServiceProxy, ManagedTokenOrganizationServiceProxy, and AutoRefreshSecurityToken classes in the Helper code: ServerConnection class topic.

See the code on GitHub

This piece of code demonstrate how to do monitor the token and refresh it before it expires:

using System;
using System.ServiceModel;
using System.ServiceModel.Description;

using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;

namespace CRMConnectionHelper
{
    public sealed class ManagedTokenOrganizationServiceProxy : OrganizationServiceProxy
    {
        private AutoRefreshSecurityToken<OrganizationServiceProxy, IOrganizationService> _proxyManager;

        public ManagedTokenOrganizationServiceProxy(Uri serviceUri, ClientCredentials userCredentials)
            : base(serviceUri, null, userCredentials, null)
        {
            this._proxyManager = new AutoRefreshSecurityToken<OrganizationServiceProxy, IOrganizationService>(this);
        }

        public ManagedTokenOrganizationServiceProxy(IServiceManagement<IOrganizationService> serviceManagement,
            SecurityTokenResponse securityTokenRes)
            : base(serviceManagement, securityTokenRes)
        {
            this._proxyManager = new AutoRefreshSecurityToken<OrganizationServiceProxy, IOrganizationService>(this);
        }

        public ManagedTokenOrganizationServiceProxy(IServiceManagement<IOrganizationService> serviceManagement,
            ClientCredentials userCredentials)
            : base(serviceManagement, userCredentials)
        {
            this._proxyManager = new AutoRefreshSecurityToken<OrganizationServiceProxy, IOrganizationService>(this);
        }

        protected override void AuthenticateCore()
        {
            this._proxyManager.PrepareCredentials();
            base.AuthenticateCore();
        }

        protected override void ValidateAuthentication()
        {
            this._proxyManager.RenewTokenIfRequired();
            base.ValidateAuthentication();
        }
    }

    ///
<summary>
    /// Class that wraps acquiring the security token for a service
    /// </summary>

    public sealed class AutoRefreshSecurityToken<TProxy, TService>
        where TProxy : ServiceProxy<TService>
        where TService : class
    {
        private TProxy _proxy;

        ///
<summary>
        /// Instantiates an instance of the proxy class
        /// </summary>

        /// <param name="proxy">Proxy that will be used to authenticate the user</param>
        public AutoRefreshSecurityToken(TProxy proxy)
        {
            if (null == proxy)
            {
                throw new ArgumentNullException("proxy");
            }

            this._proxy = proxy;
        }

        ///
<summary>
        /// Prepares authentication before authenticated
        /// </summary>

        public void PrepareCredentials()
        {
            if (null == this._proxy.ClientCredentials)
            {
                return;
            }

            switch (this._proxy.ServiceConfiguration.AuthenticationType)
            {
                case AuthenticationProviderType.ActiveDirectory:
                    this._proxy.ClientCredentials.UserName.UserName = null;
                    this._proxy.ClientCredentials.UserName.Password = null;
                    break;
                case AuthenticationProviderType.Federation:
                case AuthenticationProviderType.LiveId:
                    this._proxy.ClientCredentials.Windows.ClientCredential = null;
                    break;
                default:
                    return;
            }
        }

        ///
<summary>
        /// Renews the token (if it is near expiration or has expired)
        /// </summary>

        public void RenewTokenIfRequired()
        {
            if (null != this._proxy.SecurityTokenResponse &&
            DateTime.UtcNow.AddMinutes(15) >= this._proxy.SecurityTokenResponse.Response.Lifetime.Expires)
            {
                try
                {
                    this._proxy.Authenticate();
                }
                catch (CommunicationException)
                {
                    if (null == this._proxy.SecurityTokenResponse ||
                        DateTime.UtcNow >= this._proxy.SecurityTokenResponse.Response.Lifetime.Expires)
                    {
                        throw;
                    }

                    // Ignore the exception 
                }
            }
        }
    }
}

now create an instance of ManagedTokenOrganizationServiceProxy instead of OrganizationServiceProxy :

//get the OrganizationService
//OrganizationServiceProxy _serviceproxy = new OrganizationServiceProxy(new Uri(config.XrmOrgServiceProxy), null, clientcred, null);
 ManagedTokenOrganizationServiceProxy _serviceproxy = new ManagedTokenOrganizationServiceProxy(new Uri(config.XrmOrgServiceProxy), clientcred);