TokenProviderEndpointBehavior

The next step is to create the TokenProviderEndpointBehavior. This topic lists the code to create the TokenProviderEndpointBehavior.

TokenProviderEndpointBehavior

In order to exchange messages with a Service Bus for Windows Server farm using WCF, an application must use the NetMessagingBinding. Client and service WCF endpoints need to use the TransportClientEndpointBehavior with the WindowsTokenProvider or OAuthTokenProvider to authenticate against the STS used by the Service Bus farm. Therefore, it is sufficient to configure WCF-Custom receive locations and send ports to use the TransportClientEndpointBehavior. The problem is that the WindowsTokenProvider and OAuthTokenProvider require you to specify a list of STS URIs. This URI of the STS endpoint is easily determined from the connection string of the Service Bus namespace; which is obtained using the Get-SBClientConfiguration Windows PowerShell cmdlet, as shown in the following script:

# Import Azure PowerShell Module
Import-Module  "C:\Program Files\Service Bus\1.0\ServiceBus\ServiceBus.psd1" -ErrorAction Stop

# Retrieve the connection string for the namespace
Get-SBClientConfiguration -Namespaces ServiceBusDefaultNamespace

If you select the TransportClientEndpointBehavior in the configuration of a WCF-Custom receive location or send port, as shown in the following image, you can't add an address to the StsUris array:

Transport Client Endpoint Behavior

For this reason, create a custom endpoint behavior called TokenProviderEndpointBehavior. This endpoint allows you to define the following information in the configuration of a WCF-Custom receive location or send port:

  • The URI of the STS endpoint.

  • A token provider between the WindowsTokenProvider or OAuthTokenProvider. When using the WindowsTokenProvider, you can use the service account of the host process running the receive location to authenticate against the local STS or specify alternative credentials. When using the OAuthTokenProvider, always specify credentials in the form of username, password, or domain. LocalMachine\LocalUser or Domain\DomainUser is used to access the Service Bus namespace. The user account used by the receive location must be authorized to access the Service Bus namespace hosting the queue or subscription. To authorize the user account to access the namespace, you can use the New-SBNamespace Windows PowerShell cmdlet when you create the namespace or the Set-SBNamespace cmdlet to add the user account to the group of the namespace managers.

The following image shows how to use this component in a WCF-Custom receive location or send port:

BizTalk Security

For convenience, the following code is for this component:

public enum TokenProviderType
{
    WindowsTokenProvider,
    OAuthTokenProvider
}

public class TokenProviderBehaviorExtensionElement : BehaviorExtensionElement
{
    #region Private Constants
    //***************************
    // Constants
    //***************************

    private const string UserNamePropertyName = "userName";
    private const string PasswordPropertyName = "password";
    private const string DomainPropertyName = "domain";
    private const string StsEndpointPropertyName = "stsEndpoint";
    private const string TokenProviderTypePropertyName = "tokenProviderType";
    private const string IsTrackingEnabledPropertyName = "isTrackingEnabled";
    private const string UserNamePropertyDescription = "Gets or sets the user name associated with the credentials.";
    private const string PasswordPropertyDescription = "Gets or sets the password for the user name associated with the credentials.";
    private const string DomainPropertyDescription = "Gets or sets the domain associated with these credentials.";
    private const string StsEndpointPropertyDescription = "Gets or sets the uri of the STS endpoint.";
    private const string TokenProviderTypePropertyDescription = "Gets or sets the token provider type.";
    private const string IsTrackingEnabledPropertyDescription = "Gets or sets a value indicating whether tracking is enabled.";
    #endregion

    #region BehaviorExtensionElement Members
    //***************************
    // Protected Methods
    //***************************

    /// <summary>
    /// Creates a behavior extension based on the current configuration settings.
    /// </summary>
    /// <returns>The behavior extension.</returns>
    protected override object CreateBehavior()
    {
        return new TokenProviderEndpointBehavior(UserName, Password, Domain, StsEndpoint, TokenProviderType, IsTrackingEnabled);
    }

    /// <summary>
    /// Gets the type of behavior.
    /// </summary>
    public override Type BehaviorType
    {
        get
        {
            return typeof(TokenProviderEndpointBehavior);
        }
    }      
    #endregion

    #region Public Properties
    /// <summary>
    /// Gets or sets the user name associated with the credentials.
    /// </summary>
    [ConfigurationProperty(UserNamePropertyName, IsRequired = false)]
    [SettingsDescription(UserNamePropertyDescription)]
    public string UserName
    {
        get
        {
            return (string)base[UserNamePropertyName];
        }
        set
        {
            base[UserNamePropertyName] = value;
        }
    }

    /// <summary>
    /// Gets or sets the password for the user name associated with the credentials.
    /// </summary>
    [ConfigurationProperty(PasswordPropertyName, IsRequired = false)]
    [SettingsDescription(PasswordPropertyDescription)]
    public string Password
    {
        get
        {
            return (string)base[PasswordPropertyName];
        }
        set
        {
            base[PasswordPropertyName] = value;
        }
    }

    /// <summary>
    /// Gets or sets the domain associated with these credentials.
    /// </summary>
    [ConfigurationProperty(DomainPropertyName, IsRequired = false)]
    [SettingsDescription(DomainPropertyDescription)]
    public string Domain
    {
        get
        {
            return (string)base[DomainPropertyName];
        }
        set
        {
            base[DomainPropertyName] = value;
        }
    }

    /// <summary>
    /// Gets or sets the uri of the STS endpoint.
    /// </summary>
    [ConfigurationProperty(StsEndpointPropertyName, IsRequired = true)]
    [SettingsDescription(StsEndpointPropertyDescription)]
    public string StsEndpoint
    {
        get
        {
            return (string)base[StsEndpointPropertyName];
        }
        set
        {
            base[StsEndpointPropertyName] = value;
        }
    }

    /// <summary>
    /// Gets or sets the STS port.
    /// </summary>
    [ConfigurationProperty(TokenProviderTypePropertyName, DefaultValue = TokenProviderType.WindowsTokenProvider, IsRequired = true)]
    [SettingsDescription(TokenProviderTypePropertyDescription)]
    public TokenProviderType TokenProviderType
    {
        get
        {
            return (TokenProviderType)base[TokenProviderTypePropertyName];
        }
        set
        {
            base[TokenProviderTypePropertyName] = value;
        }
    }

    /// <summary>
    /// Gets or sets a value indicating whether tracking is enabled.
    /// </summary>
    [ConfigurationProperty(IsTrackingEnabledPropertyName, DefaultValue = true, IsRequired = false)]
    [SettingsDescription(IsTrackingEnabledPropertyDescription)]
    public bool IsTrackingEnabled
    {
        get
        {
            return (bool)base[IsTrackingEnabledPropertyName];
        }
        set
        {
            base[IsTrackingEnabledPropertyName] = value;
        }
    }
    #endregion
}

public class TokenProviderEndpointBehavior : IEndpointBehavior
{
    #region Private Constants
    //***************************
    // Constants
    //***************************

    private const string MessageFormat = "[TokenProviderEndpointBehavior] TokenProvider = {0}.";
    #endregion

    #region Private Fields
    private readonly string userName;
    private readonly string password;
    private readonly string domain;
    private readonly string stsEndpoint;
    private readonly TokenProviderType tokenProviderType;
    private readonly bool isTrackingEnabled;
    #endregion

    #region Public Constructors
    /// <summary>
    /// Initializes a new instance of the TokenProviderEndpointBehavior class.
    /// </summary>
    /// <param name="userName">The user name associated with the credentials.</param>
    /// <param name="password">The password for the user name associated with the credentials.</param>
    /// <param name="domain">The domain associated with these credentials.</param>
    /// <param name="stsEndpoint">The uri of the sts endpoint.</param>
    /// <param name="tokenProviderType">The type of the token provider.</param>
    /// <param name="isTrackingEnabled">A boolean value indicating whether tracking is enabled.</param>
    public TokenProviderEndpointBehavior(string userName, 
                                            string password,
                                            string domain,
                                            string stsEndpoint,
                                            TokenProviderType tokenProviderType,
                                            bool isTrackingEnabled)
    {
        this.userName = userName;
        this.password = password;
        this.domain = domain;
        this.stsEndpoint = stsEndpoint;
        this.tokenProviderType = tokenProviderType;
        this.isTrackingEnabled = isTrackingEnabled;
    }
    #endregion

    #region IEndpointBehavior Members
    /// <summary>
    /// Implement to pass data at runtime to bindings to support custom behavior.
    /// </summary>
    /// <param name="endpoint">The endpoint to modify.</param>
    /// <param name="bindingParameters">The objects that binding elements require to support the behavior.</param>
    void IEndpointBehavior.AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
        var stsUri = new Uri(stsEndpoint);
        var stsUris = new List<Uri> { stsUri };
        var useCurrentUser = string.IsNullOrEmpty(userName) ||
                                string.IsNullOrEmpty(password) ||
                                string.IsNullOrEmpty(domain);
        bindingParameters.Add(new TransportClientEndpointBehavior
            {
                TokenProvider = tokenProviderType == TokenProviderType.WindowsTokenProvider ?
                                useCurrentUser ? TokenProvider.CreateWindowsTokenProvider(stsUris) : TokenProvider.CreateWindowsTokenProvider(stsUris, new NetworkCredential(userName, password, domain)) :
                                TokenProvider.CreateOAuthTokenProvider(stsUris, new NetworkCredential(userName, password, domain))
            });
        Trace.WriteLineIf(isTrackingEnabled, string.Format(MessageFormat, tokenProviderType));
    }

    /// <summary>
    /// Implements a modification or extension of the client across an endpoint.
    /// </summary>
    /// <param name="endpoint">The endpoint that is to be customized.</param>
    /// <param name="clientRuntime">The client runtime to be customized.</param>
    void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
    }

    /// <summary>
    /// Implements a modification or extension of the service across an endpoint.
    /// </summary>
    /// <param name="endpoint">The endpoint that exposes the contract.</param>
    /// <param name="endpointDispatcher">The endpoint dispatcher to be modified or extended.</param>
    void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
    }

    /// <summary>
    /// Implement to confirm that the endpoint meets some intended criteria.
    /// </summary>
    /// <param name="endpoint">The endpoint to validate.</param>
    void IEndpointBehavior.Validate(ServiceEndpoint endpoint)
    {
    }
    #endregion
}

Next Step

Install and Configure Components

See Also

Concepts

ListenUriEndpointBehavior
SessionChannelEndpointBehavior
ServiceBusMessageInspector
TokenProviderEndpointBehavior
Create the Endpoint Behaviors