Overview
The beef of the behavior extension is that it adds or creates an authorization header with a value "Bearer + [token returned from CRM]". The authorization header is then used when the integration tries to connect to the backend REST API address.
Instructions
Install ADAL to Visual Studio (https://azure.microsoft.com/en-us/documentation/articles/active-directory-authentication-libraries/).
Go to Tools -> NuGet Package Manager -> Package Manager Console
Run "Install-Package Microsoft.IdentityModel.Clients.ActiveDirectory" command.
Add references needed by the project.
Add the code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Diagnostics;
using System.ServiceModel;
using System.Web;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.Configuration;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
namespace AppIDAuthentication
{
class AppIDAuthenticationBehavior : IEndpointBehavior
{
public string Username { get; set; }
public string Password { get; set; }
public string CrmUri { get; set; }
public string ClientID { get; set; }
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
AppIDAuthenticationMessageInspector inspector = new AppIDAuthenticationMessageInspector(this.Username, this.Password, this.CrmUri, this.ClientID);
clientRuntime.MessageInspectors.Add(inspector);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
public class AppIDAuthenticationMessageInspector : IClientMessageInspector
{
string _username = string.Empty;
string _password = string.Empty;
string _crmUri = string.Empty;
string _clientID = string.Empty;
private string m_Bearer;
private const string AUTHORIZATION_HTTP_HEADER = "Authorization";
public AppIDAuthenticationMessageInspector(string username, string password, string crmUri, string clientID)
{
this._username = username;
this._password = password;
this._crmUri = crmUri;
this._clientID = clientID;
}
//Enables inspection or modification of a message after a reply message is received but prior to passing it back to the client application.
public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
//Do nothing
}
//Enables inspection or modification of a message before a request message is sent to a service.
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request,
System.ServiceModel.IClientChannel channel)
{
Task<Task> CRMLogin = new Task<Task>(async () =>
{
AuthenticationContext authContext = new AuthenticationContext("https://login.windows.net/common", false);
AuthenticationResult result = await authContext.AcquireTokenAsync(_crmUri, _clientID, new UserPasswordCredential(_username, _password));
m_Bearer = "Bearer " + result.AccessToken;
});
CRMLogin.Start();
CRMLogin.Wait();
CRMLogin.Result.Wait();
HttpRequestMessageProperty httpRequestMessage;
object httpRequestMessageObject;
if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out httpRequestMessageObject))
{
httpRequestMessage = httpRequestMessageObject as HttpRequestMessageProperty;
if (string.IsNullOrEmpty(httpRequestMessage.Headers[AUTHORIZATION_HTTP_HEADER]))
{
httpRequestMessage.Headers[AUTHORIZATION_HTTP_HEADER] = this.m_Bearer;
}
}
else
{
httpRequestMessage = new HttpRequestMessageProperty();
httpRequestMessage.Headers.Add(AUTHORIZATION_HTTP_HEADER, this.m_Bearer);
request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage);
}
return null;
}
}
public class AppIDAuthenticationBehaviorExtensionElement : BehaviorExtensionElement
{
[ConfigurationProperty("username", DefaultValue = "", IsRequired = true)]
public string Username
{
get { return (string)base["username"]; }
set { base["username"] = value; }
}
[ConfigurationProperty("password", DefaultValue = "", IsRequired = true)]
public string Password
{
get { return (string)base["password"]; }
set { base["password"] = value; }
}
[ConfigurationProperty("crmuri", DefaultValue = "", IsRequired = true)]
public string CrmUri
{
get { return (string)base["crmuri"]; }
set { base["crmuri"] = value; }
}
[ConfigurationProperty("clientid", DefaultValue = "", IsRequired = true)]
public string ClientID
{
get { return (string)base["clientid"]; }
set { base["clientid"] = value; }
}
protected override void Init()
{
base.Init();
}
protected override void InitializeDefault()
{
base.InitializeDefault();
}
public override Type BehaviorType
{
get { return typeof(AppIDAuthenticationBehavior); }
}
protected override object CreateBehavior()
{
AppIDAuthenticationBehavior behavior = new AppIDAuthenticationBehavior();
behavior.Username = this.Username;
behavior.Password = this.Password;
behavior.CrmUri = this.CrmUri;
behavior.ClientID = this.ClientID;
return behavior;
}
}
}
Add the dlls (all three in the picture, your dll might have a different name) to the GAC using "gacutil -i" command.
You can use SvcConfigEditor ("C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools\SvcConfigEditor.exe") to add behavior element extension to the machine.config file ("C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config\machine.config").
From "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config\machine.config" you can then manually copy the new line to the "C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\machine.config" if needed.
You could select the assembly from GAC also.
In the BizTalk Administrator Console -> integration Send Port -> WCF-WebHttp Transport Properties define the security mode as 'Transport' and the client credential type as 'None' to enable HTTPS in the backend address.
Suppress body for verbs: GET
You need to increase maximum received message size.
Add the extension you created
Enter the values
Enjoy!