May 17, 2013 at 4:05 PM

 

BizTalk2013 logo

BizTalk 2013 is out, but this is obviously old news for most of you. With a lot of new features and support for on premises, or in the cloud, this new version seems exciting and maybe challenging…

To help you overcome any potential issues and to make sure you are ready for the new version, Codit (partner of the global Microsoft Integration Alliance "IMPACK") organizes the European edition of the global BizTalk 2013 launch in Belgium - preceded by Breeze in Australia and in parallel with Matricis in North-America.

What will I learn?

  • What is new in Microsoft BizTalk Server 2013? New features!
  • The new cloud capabilities of BizTalk 2013
  • BizTalk 2013 & its new adapters
  • Demonstration of the Nevatech SOA governance tool called Sentinet - a flexible and scalable solution for the SOA governance of your BizTalk (or Azure) integration environment
  • Exclusively discover Windows Azure BizTalk Services (WABS) - Hear from the experts: Codit is selected Microsoft launch partner of WABS and is one of the first partners worldwide to incorporate these innovative services as a core component in its solution.

Practical details

  • Date: Wednesday May 29th, from 8:30 AM to 16:30 PM (including lunch)
  • Location: Communicatieloft in Ghent, easy accessible via R4 (large parking) & walking distance from railway station Gent Sint-Pieters
  • Participation is free, but open for pre-registrants only
  • Language: English
  • Bring your business cards!

Speakers & Sessions

  • Keynote: Microsoft Cloud OS as the Foundation for your Hybrid Cloud
    • Rudy Van Hoe, Business Manager, Server and Cloud Platform at Microsoft
  • Windows Azure BizTalk Services; notes from the field
    • Sam Vanhoutte, CTO Codit
  • SOA Governance and Runtime Capabilities Enablement for BizTalk Server 2013
    • Andrew Slivker, CTO Nevatech
  • BizTalk Server 2013 in Windows Azure IaaS
    • Peter Borremans
  • Maintaining chain integrity after a system or disaster recovery
    • Jasper Defesche, Axon Olympus
  • New adapters in BizTalk 2013
    • Toon Vanhoutte

 

For more information and registration, visit this page on our website.

Hope to see you there!

Posted in: BizTalk | Events

Tags: ,


April 30, 2013 at 4:30 PM

On the latest version of BizTalk (2013): a new adapter was introduced for natively working with REST endpoints, using WCF technology: the WCF-WebHttp Adapter. 

After the beta version was released we could find some very good articles about this adapter, but since it is a new adapter there's a lot of ground to cover.
At the moment of my writing, the  MSDN website has very few information about it, so time to give something to the community... 

 


On this small article I will focus on using HTTP headers with the new adapter.

In this scenario I wanted to set HTTP content type at the header level. This could be done in two ways: using the adapter properties or writing in the message context. 

 

Adapter properties 

Using the adapter properties is a fairly simple task, we just need to go to our port properties and then adapter properties. On the last tab "Messages" we have a text area destined to Outbound HTTP Headers. Every HTTP header that we place here will be included on the message that will be sent (I'm using a send port for this example).

 

 

By default, WCF-WebHttp sends your message with as "Content-Type: application/xml; charset=utf-8", if you want to invoke a REST service with a GET request, for instance, you will have to insert the following code in the HTTP Outbound Headers:

Content-Type: application/http

And use a PassThruTransmit pipeline component, or another component that doesn't validate XML.


Message Context

If we need to set the HTTP headers at runtime we could do this mainly on orchestrations or pipeline components by writing the property on context.

 

We have two properties available to set in the headers: 

Property Name

Property Schema

Adapter

UserHttpHeaders

http://schemas.microsoft.com/BizTalk/2003/http-properties

HTTP

OutboundCustomHeaders

http://schemas.microsoft.com/BizTalk/2006/01/Adapters/WCF-properties

WCF-*


Both options will not raise any error if you try to write/promote them, but it won't work!
UserHttpHeaders is for HTTP adapter only, if you use it you will send the message as application/xml since the property isn't read by the adapter and since we are assuming that there is no HTTP header configured on the adapter properties.
The same goes for the OutboundCustomHeaders, it's for SOAP messages only (and using an XML structure), if you try to use it you will have the following error:
 

System.InvalidOperationException: Envelope Version 'EnvelopeNone (http://schemas.microsoft.com/ws/2005/05/envelope/none)' does not support adding Message Headers. 

If you happen to have an XML error it's probably because OutboundCustomHeaders is expecting an XML structure and you are not passing one.

 

So after dwelling on this issue for some time I inspected the new Biztalk 2013 property schema for WCF, and we can clearly  see some new properties related to the WCF-WebHttp adapter: 
 

Name

Type

Description

InboundHttpHeaders

xs:string

The HTTP headers present in the inbound message received over a HTTP transport

InboundHttpStatusCode

xs:string

The HTTP status code of the response message

InboundHttpStatusDescription

xs:string

The HTTP status description of the response message

InboundHttpMethod

xs:string

The HTTP Verb of the request message

OutboundHttpStatusCode

xs:string

The HTTP status code of the response message

OutboundHttpStatusDescription

xs:string

The HTTP status description of the response message

SuppressMessageBodyForHttpVerbs

xs:string

Removes the Message Body from the outbound request message, for the specified HTTP Verbs

HttpHeaders

xs:string

Sets HTTP headers for the outbound message

VariablePropertyMapping

xs:string

Provides the URL Template Variables and Message Context Properties mapping configuration

HttpMethodAndUrl

xs:string

Provides the HTTP Method and URL mapping configuration

 

In my scenario I wanted to use a send port, so the property that suits my purpose was HttpHeaders
All I had to do was write the content type into the property, without the need of promoting it. 

Here's part of my Execute() method of the custom pipeline component, where I set the message content type to application/atom+xml.


public Microsoft.BizTalk.Message.Interop.IBaseMessage Execute(IPipelineContext pContext, Microsoft.BizTalk.Message.Interop.IBaseMessage pInMsg)
{
    // Some non important logic here

    pInMsg.Context.Write("HttpHeaders", "http://schemas.microsoft.com/BizTalk/2006/01/Adapters/WCF-properties","content-type: application/atom+xml");

    return pInMsg;
}


Another important note that I should add, is about the adapter HTTP header limitations:
If you are planning to change the HttpHeader per message it won't work with a standard port, since by "design" the adapter uses the properties by port and not by message. If you need to change the header at runtime (per message) you will need to use a dynamic port instead.


And that’s it Smile

Ricardo Marques

 

 

 

Posted in: BizTalk | REST | WCF

Tags: , ,


April 26, 2013 at 3:34 PM

Hello,

One of our Managed Services customers had a problem where a message got suspended with the following error:

They already looked into the issue, tested the username and password for the share, etc ...
The platform is a multi-server platform with 2 BizTalk servers within the same BizTalk group.

 

Troubleshooting

I was digging into the issue and saw that it was very likely that only one of the servers had the issue (the server where the message got suspended was always the same). 

So during the retry within BizTalk, some messages got processed by the other server as BizTalk chooses the processing server depending on load...
Sometimes though, a message got only processed through the first server, leading to a suspended message eventually.

 

I was digging into the eventlog and stumbled into this (system eventlog):

 

I checked and there was indeed a stored username/password on the first server for connecting to the server hosting the file-share:

(This were credentials of a BizTalk operator and his password had changed recently).

When I removed the stored credentials the issue was resolved. (Also when you define the credentials in the send port configuration).

So stored credentials were used instead of the credentials defined in the BizTalk host to connect to the fileshare!

 

For more information on stored passwords, please see MSDN: http://technet.microsoft.com/en-us/library/cc733968(v=ws.10).aspx

 

Hope this helps,

Brecht


April 15, 2013 at 1:26 PM

The moment we’ve all been waiting for is finally there.  Windows Azure Infrastructure Services reached the General Availability milestone today.  This contains the following capabilities:

  • Windows Azure Virtual Machines: you are able to have persistent virtual machines deployed in your Windows Azure subscription.  For this, you can bring your own (Linux or Microsoft) images or select them from the existing gallery.
  • Windows Azure Virtual Networking: you are able to provision and manage virtual private networks (VPNs) in Windows Azure and securely connect them with your local IT infrastructure.

More information can be found on the blog of ScottGu: http://weblogs.asp.net/scottgu/archive/2013/04/16/windows-azure-general-availability-of-infrastructure-as-a-service-iaas.aspx

 

What is new with this release ?

There are some interesting new items available with the new release:

  • High memory instances.  These are machines with much more memory assigned to it that can serve some specific workloads.  (these are not the high compute machines.  The names for these machines will be A6 and A7, instead of XXXL Smile)
  • Monthly SLAs.  There will from now on be SLA’s provided, that will measure on a monthly basis.  SLAs for Virtual Machines are 99.95% and require 2 instances.  For Virtual Networking, the monthly SLA will be 99.9%.
  • More validated & supported Microsoft workloads.  There will be much more workloads and images added to the platform.  Also BizTalk Server 2013 will be available soon.
  • The actual prices will start as from June 1, but are seriously decreased, also for PaaS images!

What does it mean for you ?

  • From now on, you are able to have a real better together story that fits your preferences: cloud, on premises or hybrid.
  • You can manage all these machines, using the System Center suite
  • And you can share the same identity management through Active Directory and Federation Services
  • There is no lock in.  This is a common concern for enterprises, but with these infrastructure services, you are now able to move virtual machines from the cloud and run them again in your own data center, because of the VM portability.
  • You can now have both the software and the infrastructure layer provided by the same vendor: Microsoft.  This should help you in getting better quality and support through the wide network of Microsoft partners and local Microsoft teams.

What about BizTalk Server ?

As described in the MSDN article “Configuring BizTalk Server 2013 on a Windows Azure VM”, you can install BizTalk Server in Windows Azure infrastructure services.  Until now, these virtual machines were running in preview mode, meaning they were not officially supported or backed up by SLAs.  But from now on, you can have BizTalk Server officially installed and supported on Windows Azure.

Sam Vanhoutte

Posted in: Azure | BizTalk

Tags: , ,


April 10, 2013 at 4:08 PM

In an XSLT mapping I wanted to convert the node with its child nodes to a node with attributes. 
I noticed that the source schema contained more than 60 child nodes and most of them could be optional. The transform of this mapping would result in a big piece of code for some logic that perhaps could be made in a better way.

This process could be optimized by creating the attributes in a dynamic way. 

For illustration, I am using this small piece of XML:

<Contact>
	<Name>Reception</Name>
	<Phone>+32 9 247 32 65</Phone>
	<Mobile>+32 475 36 45 78</Mobile>
	<Fax>+32 9 247 32 66</Fax>
	<Email>reception@company.be</Email>
</Contact>

This is the desired output:

<Contact Name='Reception' Phone='+32 9 247 32 65' Mobile='+32 475 36 45 78' Fax='+32 9 247 32 66' Email='reception@company.be' />

The Problem

I am looping through each child node with the query Contact/* (there is no namespace for simplicity reasons). The function name() is able to give the name of the node. I store this name in the variable $attributeName, for later usage.  When we know the name of the current node, this should be easy to create an attribute dynamically:

<Contact>
    <xsl:for-each select="Contact/*">
            <xsl:variable name="attributeName" select="name(.)" />
            <xsl:attribute name="$attributeName">
              <xsl:value-of select="'value'"/>
            </xsl:attribute>
         </xsl:for-each>
     </xsl:for-each>
</Contact>


Although, it is impossible to define special characters in the name parameter of the attribute definition.
Visual studio gave me the error “the ‘$’ character, hexadecimal value 0x24, cannot be included in a name.”  

 

The Solution

The name argument is not able to perform a query or do some piece of logic. This problem can be solved by working with attribute value templates. Attribute value templates will evaluate the expression and convert the resulting object to a string.

I replaced $attributeName with {$attributeName}

This is the final result:

<Contact>
    <xsl:for-each select="Contact/*">
            <xsl:variable name="attributeName" select="name(.)" />
            <xsl:attribute name="{$attributeName}">
              <xsl:value-of select="'value'"/>
            </xsl:attribute>
         </xsl:for-each>
     </xsl:for-each>
</Contact>

 More information about attribute value templates can be found on the website of W3C.

Additional Notes

The same trick can be used to create elements in a dynamic way in XSLT or for using variables in arguments.
Note that when the source schema will be changed and some child nodes are added, this mapping will also add those attributes, even if this is unknown by the destination schema. This can be positive or negative.

Posted in: Schemas | XML | XSLT

Tags: , ,


April 3, 2013 at 4:05 PM

Some months ago, I was asked for an intervention regarding a SSL client certificate issue. There was a problem related to the setup of transport security (SSL) of a WCF service hosted in IIS 7.0, using client certificates that are mapped to a local account.  Let’s have a look.

 

The Setup

Server Setup

  • Wild card server certificate is installed in IIS (server level). Wild card is required, because we’re using multiple host names are configured.

          image

        image

 

  • The Certificate Authority is added to the Trusted Root Certification Authorities store (Local Machine)

        image

 

  • Website binding is configured to use https with the server certificate for SSL

        image

 

  • Website is configured to require SSL and to require client certificates

       image

 

  • Client certificate mapping is configured in order to map an individual client certificate to a specific Windows account. Configurable via this extension.

         image

       image

 

       This extension actually changes this configuration:

      image

 

  • An Allow authorization rule is configured for this Windows account

        image

 

  • Anonymous Authentication is enabled (Client Certificate is anonymous authentication)

        image

 

  • WCF service is configured for transport security

          image

 

Client Setup

  • The Certificate Authority is added to the Trusted Root Certification Authorities store (Local Machine)

           image

 

  • Client Certificate (containing private key) is added to the Personal certificate store (Local Machine).  This is required for the WCF client

          image

 

  • Client Certificate (containing private key) is added to the Personal certificate store (Current User).  This is required for browsing to the service via IE

         image

 

  • WCF Client is configured for transport security, providing Client Certificates:

          image

 

Problem Solving

The issue

When browsing to the service, for a particular web server, we’re not prompted to select a client certificate. Instead, we get this exception:

The page you are attempting to access requires your browser to have a Secure Sockets Layer (SSL) client certificate that the Web Server recognizes.

image

 

Troubleshooting

We configured another web server with exactly the same setup, which worked fine.  But still it didn’t work on that particular web server. Handy tools / commands during troubleshooting:

  • netsh http show sslcert
  • SSL Diagnostics

 

At the end, a warning in the System Event Log gave the solution. This entry is only written to the Event Log for the first call after the IIS service is restarted. That’s why we didn’t discover this Event Log warning earlier.

When asking for client authentication, this server sends a list of trusted certificate authorities to the client.  The client uses this list to choose a client certificate that is trusted by the server.  Currently, this server trusts so many certificate authorities that the list has grown too long.  This list has thus been truncated.

image

 

Root cause

During the handshake protocol for client certificate authorization, the server sends a list of Trusted Root Certification Authorities to the client.  The client will in this case only provide Client Certificates, issued by one of these Trusted Root Certification Authorities.  The problem was that the Trusted Root Certification Authorities list was too long on that particular server, so it was truncated before sent to the client.  Unluckily, our Root Certificate Authority was truncated from the list, so the handshake failed.

 

Solutions

There are two solutions to solve this issue:

  • The first solution is to clean up the Trusted Root Certification Authorities store (Local Machine) and remove all unnecessary certificates. Be aware that you don’t remove certificates that are required by Windows.

 

  • A second solution is to configure Schannel to no longer send the list of trusted root certification authorities during the TLS/SSL handshake process.  This can be done by adding this registry entry on the web server:

        HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL

        Value name: SendTrustedIssuerList
        Value type: REG_DWORD
        Value data: 0 (False)

 

Result

When browsing to the service, we’re now promoted for our Client Certificate. Now we can access the service:

         image

         image

Posted in: IIS | Security | SSL

Tags: , , ,


March 29, 2013 at 3:26 PM

Today I was deploying a new release for one of my BizTalk applications. I updated logic on several places in two assemblies containing orchestrations.

I decided to deploy to the testing server manually: the first assembly was no problem to deploy, however the second assembly gave me the following error:

 

Failed to update binding information. (mscorlib)

Party 'PartyName' enlisted under role 'Provider(Codit.MyRoleLinkType)' has not bound all the operations of role link port types. (Microsoft.BizTalk.ExplorerOM)

 

You do not find a lot of information on the above error online, so I tried to check where the problem lied myself to no avail.

I found one similar blog post from Steve Harclerode which claims that there is no other way than to delete the entire BizTalk application and then recreate it.

However, I found that if I delete this particular resource and then add it again, I have no issues.
Off course, by removing the resource and thus also removing the artifacts and bindings, you also lose your role link setup. In my case however, this is easily put back by importing the bindings I exported before removing the assembly.

Hope this helps someone, please feel free to comment if you have any cause or solution.


March 27, 2013 at 4:10 PM

BizTalk provides out-of-the-box functionality to include your custom BizTalk database in the standard backup procedure. 
You can find all details on MSDN.  Here’s a small synopsis:

 

  • Execute these scripts against your custom database
    • %BTSINTALLDIR%\Schema\Backup_Setup_All_Procs.dsl
    • %BTSINTALLDIR%\Schema\Backup_Setup_All_Tables.dsl

 

  • Add the SQL Agent account to the BTS_BACKUP_USERS role of your custom database

 

  • Modify the adm_OtherBackupDatabases table of the BizTalkMgmtDb, add a record for your custom database

 

        image

 

However, when I executed the BizTalk Backup Job after this change, I got the following exception:

 

  • [SQLSTATE 01000] (Message 4035)  BACKUP LOG is terminating abnormally. [SQLSTATE 42000] (Error 3013)  BACKUP LOG cannot be performed because there is no current database backup.

 

The cause was the fact that it is not possible to take a transactional backup of a database, if you didn’t take a full backup first.  This issue was fixed by executing the stored procedure sp_ForceFullBackup of the BizTalkMgmtDb.  Now the custom database had a initial full backup and the BizTalk Backup Job executed successfully.  This result was an extra database backup:

 

image

Posted in: BizTalk | Database | Infrastructure | SQL Server

Tags:


March 22, 2013 at 10:20 AM

Codit is very happy to let you know that the new version of Microsoft BizTalk Server: BizTalk 2013 has been Released to Manufacturing (RTM).  This version of BizTalk Server is the flagship integration server that is built to build hybrid integration scenarios in an easier and consistent way.

This is the eighth release of BizTalk Server, and it is the eighth version where Codit has been actively working with.  As a TAP customer, we have provided feedback and did extensive testing with the product.  The cloud offerings, which will be released soon, include Infrastructure as a Service (IaaS) capabilities as well as Platform as a Service (PaaS) capabilities. Please stay tuned for exciting updates on this topic in the near future as we are working very close with the product team on these releases.

We are also proud to say that we have just released a new version of the following products, supported and tested on BizTalk Server 2013:

  • Codit Integration Dashboard: handle exceptions and resubmit messages in a user friendly web based portal.
  • Codit Integration Framework: our framework is processing millions of messages every day in projects around the world and will be doing so for the BizTalk 2013 version.  The new version of our framework contains a lot of new features.  Don't hesitate to contact us for more information.

For the on-premises BizTalk Server 2013 release, the following themes are important:

  • Cloud Connectivity
  • Ability to run existing BizTalk applications in the cloud (IaaS)
  • Improved Performance
  • Simplified Development and Management Experience
  • Support for the latest platform and standards

In terms of features, this translates to

  • Integration with Cloud Services- BizTalk Server 2013 includes new out-of-the box adapters to send and receive messages from Windows Azure Service Bus, making it easy to build hybrid solutions. It also provides capabilities to host BizTalk endpoints in Azure through the Service Bus Relay providing a simple and secure way to connect external partners and application to BizTalk Server on premises.
  • RESTful services- BizTalk Server 2013 provides adapters to invoke REST endpoints as well as expose BizTalk Server artifacts as a RESTful service.
  • Enhanced SharePoint adapter- Integrating with SharePoint using BizTalk Server 2013 is now as simple as integrating with a file share.
  • SFTP adapter-Enables sending and receiving messages from an SFTP server.
  • Other enhancements: The ESB capabilities previously introduced in the ESB Toolkit are now fully integrated with BizTalk Server, Dependency tracking,Improvements in dynamic send ports, XslCompiledTransform, more support for protocol updates (X12, EDIFACT, HL7)

There is also a change in the licensing approach, where BizTalk is now moving to a per-core licensing model.  If you need more information on this, don't hesitate to let us know.

Posted in: BizTalk | Service Bus | Azure

Tags:


March 21, 2013 at 3:16 PM

This is the second part in Using ACS and WAAD with JWT Tokens for Web and Store Applications.

If you missed Part 1 make sure you catch up first!

We will first create a Windows Store Application. Then we will make use of the AuthenticationBroker in order to authenticate with our ACS namespace.
To support this type of authentication we will modify our existing MVC application Web Api Controller (created in part 1).

1. Creating the Windows Store Application


If you don't have any experience building Windows Store Applications, there are some things you need to keep in mind:
    - To run / simulate a Windows Store Application, you will need a non-admin user in order to view your application.
    - There is an extra "Simulator" in visual studio that simulates the application instead of deploying it to your Metro interface.


windows_store_vs_simulator

 

1.1. Creating the new project


create_windows_store_project


Add a new javascript file to the solution named "authentication.js". Add the javascript reference to the default.html page.

<script src="/js/authentication.js"></script>

Add a new button and text field in the default.html page by replacing “<p>Content goes here</p>” by:

<p style="margin: 20px;"> 
    <button id="bttnAuthenticate">Login using WAAD</button> 
    <button id="bttnCall">Hit MVC without a token</button> 
    <button id="bttnTokenCall">Hit MVC with Jason Web token</button> 
    <br /> 
    <textarea cols="70" rows="10" id="log"></textarea> 
</p>

Let's add an event handler for our button:

Replace args.setPromise(WinJS.UI.processAll()); in the default.js by 
args.setPromise(WinJS.UI.processAll().done(function () { 
    var bttnAuthenticate = document.getElementById("bttnAuthenticate"); 
    var bttnCall = document.getElementById("bttnCall"); 
    var bttnTokenCall = document.getElementById("bttnTokenCall"); 
    bttnAuthenticate.addEventListener("click", bttnAuthenticateClick, false); 
    bttnCall.addEventListener("click", bttnCallClick, false); 
    bttnTokenCall.addEventListener("click", bttnTokenCallClick, false); 
}));

 

1.2. Modifying our existing Auth Api Controller


In the GetToken method we currently have the following:

[AllowAnonymous] 
[HttpPost] 
public HttpResponseMessage GetToken() 
{ 
    var response = Request.CreateResponse(HttpStatusCode.Redirect); 
    response.Headers.Add("Location", "/"); 
    return response; 
}

 

In order to pass the token to a Windows Store Apps we need to adjust the GetToken method to the following;

[HttpPost] 
[AllowAnonymous] 
public HttpResponseMessage GetToken() 
{ 
    var response = Request.CreateResponse(HttpStatusCode.Redirect); 
    var bootstrapToken = ExtractBootstrapToken(); 
    const string mvcCookieName = "tokenCookie"; 
    // We check for cookies here. If there is no "login" cookie we do not issue a token redirect (for use in Windows Store Apps) 
    if (HttpContext.Current.Request.Cookies.AllKeys.Contains(mvcCookieName)) 
    { 
        response.Headers.Add("Location", string.Format("/")); 
    } 
    else 
    { 
        response.Headers.Add("Location", string.Format("/auth/end?t={0}", bootstrapToken)); 
    } 
    return response; 
}

protected virtual string ExtractBootstrapToken() 
{ 
    var bc = ClaimsPrincipal.Current.Identities.First().BootstrapContext as BootstrapContext; 
    return  ((JWTSecurityToken) bc.SecurityToken).RawData; 
}

 

Let's explain what's changed:
Whenever a request comes in that triggers an action method on any of our Web Api controllers, we will need to provide a token. Whether this request is coming from our current MVC Application or our Windows Store Application.
When we login to MVC, we need to redirect the user to the homepage.
When we login to Windows Store Application, we need to redirect to a Url that is configured in our store application as return Url for the Authentication Broker. (see later)
The redirection Url will also contain the token as a querystring.

The only way (that I know of) we can do this is by setting a cookie on the client that is included in the request when we login from MVC. The store application will not provide any cookies so we will get our custom return Url.
Setting the cookie is easy by adding the following to our Authentication Controller Login Action:

ControllerContext.HttpContext.Response.Cookies.Add(new HttpCookie("tokenCookie") { Value = "" });

 

1.3. Configuring the authentication broker

 

Great, our superfantastic GUI is all setup and our controller will redirect us to a custom URI. Time to add some javascript to start our login process. To achieve that, we will make use of the Authentication Broker

Add the following code to the authentication.js file:

function bttnAuthenticateClick() { 
    var log = document.getElementById("log");

    var requestUri = "https://[yourAcsNamespace].accesscontrol.windows.net:443/v2/wsfederation?wa=wsignin1.0&wtrealm=[yourRealm]"; 
    // Add the url here that you redirect to in the Auth Api Controller 
    var callBackUri = "[yourRedirectUrl]";

    if (authzInProgress) { 
        log.value = "Authorization already in Progress ..."; 
        return; 
    }

    var startUri = new Windows.Foundation.Uri(requestUri); 
    var endUri = new Windows.Foundation.Uri(callBackUri);

    log.value = "Navigating to: " + requestUri + "\r\n";

    authzInProgress = true;

    Windows.Security.Authentication.Web.WebAuthenticationBroker.authenticateAsync( 
        Windows.Security.Authentication.Web.WebAuthenticationOptions.none, startUri, endUri) 
        .done(function (result) { 
            log.value += "Response Data | Status: " + result.responseData + "|" + result.responseStatus + "\r\n";

            if (result.responseStatus === Windows.Security.Authentication.Web.WebAuthenticationStatus.errorHttp) { 
                log.value += "Error returned: " + result.responseErrorDetail + "\r\n"; 
            }

            if (result.responseData.split('?')) { 
                token = result.responseData.split('?')[1].split('t=')[1]; 
                log.value += "JWT: " + token + "\r\n"; 
            } else {

            } log.value += "JWT token is missing... something went wrong \r\n";

            authzInProgress = false; 
        }, function (err) { 
            log.value += "Error returned by WebAuth broker: " + err; 
            log.value += "Error Message: " + err.message + "\r\n"; 
            authzInProgress = false; 
        }); 
}



Replace the following values

 

1.3.1. Debugging the authentication broker

We all know that things mostly won't work the first try. When the authentication broker doesn't work, you won't get a proper error message.
That's why we first want to check if our reply Url gets hit by adding a breakpoint. If that breakpoint doesn't get hit, there is a big chance you have a misconfiguration in ACS.

The return Url configured in ACS will trigger our MVC application. The MVC application will get us the token and we'll forward the call to a new Url, the Url defined in our Windows Store Application as "callbackUri".
This Url doesn't need to exist, it will only trigger the callback of the authentication broker.

 

1.4. Running the applications

Fire up another instance of Visual Studio (run as administrator) and open our MVC solution we created in part 1. 
Add a breakpoint to the GetToken method of our Auth Api Controller. Debug the MVC Application.
Run the Simulator to start our windows store application. Click the "Login using WAAD" button.
You should be prompted with the following screen


simulator_waad_authentication

After login, we can debug our GetToken() method:

extract_bootstraptoken

When everything went well, our Windows Store App will be able to retrieve the token

reply_from_mvc

 

2. Secure our Mvc Web Api

2.1. Modifying Global.asax for authentication


Whenever a request comes to trigger an action on one of our Web Api Controllers, we need to parse the JWT Token in order to authenticate the user.
Authentication happens in the Global.asax, authorization will happen in the Controller Action by decorating the class or method with an Authorization Attribute.

To intercept a Web Api request we can make use of the Web Api Message Handlers

Add the following to the Global.asax.cs:

public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            Configure(GlobalConfiguration.Configuration);
            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
            AuthConfig.RegisterAuth();
        }

        static void Configure(HttpConfiguration config)
        {
            config.MessageHandlers.Add(new JwtTokenValidationHandler());
        }

        internal class JwtTokenValidationHandler : DelegatingHandler
        {
            protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
            {
                // We support Forms Based authentication
                var authCookie = request.Headers.GetCookies(FormsAuthentication.FormsCookieName).FirstOrDefault();
                if (authCookie != null)
                {
                    var authC = authCookie[FormsAuthentication.FormsCookieName];
                    ParseCookie(authC.Value);

                    return base.SendAsync(request, cancellationToken);
                }

                // We support JWT Token authentication
                HttpStatusCode statusCode;
                string token;
                if (!TryRetrieveToken(request, out token))
                {
                    // if the token could not get parsed we still allow access to our controller action methods.
                    return base.SendAsync(request, cancellationToken);
                }

                try
                {
                    var tokenHandler = new JWTSecurityTokenHandler();

                    List<SecurityKey> skeys;

                    var identityConfig = new IdentityConfiguration();
                    string keyName = identityConfig.AudienceRestriction.AllowedAudienceUris[0].ToString();

                    if (((NamedKeyIssuerTokenResolver)(identityConfig).IssuerTokenResolver).SecurityKeys.TryGetValue(keyName, out skeys))
                    {
                        var tok = new NamedKeySecurityToken(keyName, skeys);
                        var validationParameters = new TokenValidationParameters
                        {
                            AllowedAudience = keyName,
                            ValidIssuer = ((ConfigurationBasedIssuerNameRegistry)identityConfig.IssuerNameRegistry).ConfiguredTrustedIssuers.ToList()[0].Value,
                            SigningToken = tok
                        };

                        var currentPrincipal = tokenHandler.ValidateToken(token, validationParameters);

                        Thread.CurrentPrincipal = currentPrincipal;
                    }

                    return base.SendAsync(request, cancellationToken);
                }
                catch (SecurityTokenValidationException)
                {
                    return base.SendAsync(request, cancellationToken);
                }

                catch (Exception ex)
                {
                    statusCode = HttpStatusCode.InternalServerError;
                    // TODO log exception
                }
                return Task<HttpResponseMessage>.Factory.StartNew(() =>
                                                                  new HttpResponseMessage(statusCode));
            }

            private static void ParseCookie(string cookieValue)
            {
                // Parse authentication Cookie here
                FormsAuthenticationTicket enabledTicket = FormsAuthentication.Decrypt(cookieValue);

                if (enabledTicket == null) return;

                var id = new FormsIdentity(enabledTicket);
                var principal = new GenericPrincipal(id, null);
                HttpContext.Current.User = principal;
            }

            private static bool TryRetrieveToken(HttpRequestMessage request, out string token)
            {
                token = null;

                // Substract the token from the bearer authorization header
                IEnumerable<string> authzHeaders;
                if (!request.Headers.TryGetValues("Authorization", out authzHeaders) || authzHeaders.Count() > 1)
                {
                    return false;
                }
                var bearerToken = authzHeaders.ElementAt(0);
                token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
                return true;
            }
        }
    }

 

You don't need to change anything, it will retrieve everything all security settings from the Web.Config.
What we try to do here is set a CurrentPrincipal when we can. When we find an authentication cookie, we parse that cookie (we support forms authentication on MVC). When we find an authentication header, we retrieve the token from the header and try to validate it. If it's valid, we know the current user.
Of course it can happen that the cookie or the authentication header is invalid. However, as we are only authenticating here, we still let the call go through to the controller action. Our controller will check if the user is authenticated or not. Why do we do this? Well, we still need some anonymous actions to be accessible for unauthenticated users.

 

2.2. Adding a Web Api AuthorizationAttrubute

We already added a MultipleAuthorization Attribute for our normal Controllers. Now it's time to add an ApiAuthorizationAttribute for our Api Controllers.

Let's create a new class called "ApiAuthorization" and add the following code

using System.Security.Principal; 
using System.Threading; 
using System.Web.Http.Controllers;

namespace Codit.Blog.Waad.Mvc.Infrastructure.Auth 
{ 
    public class ApiAuthorizeAttribute : System.Web.Http.AuthorizeAttribute 
    { 
        protected override bool IsAuthorized(HttpActionContext context) 
        { 
            IPrincipal client = Thread.CurrentPrincipal; 
            return client.Identity.IsAuthenticated; 
        } 
    } 
}

 

We also add a new Api Controller called "QuoteController" with the following code:

using System.Web.Http; 
using Codit.Blog.Waad.Mvc.Infrastructure.Auth;

namespace Codit.Blog.Waad.Mvc.Controllers.api 
{ 
    public class QuoteController : ApiController 
    { 
        [ApiAuthorize] 
        public string GetSecretQuote() 
        { 
            return "Housework can't kill you, but why take a chance?"; 
        } 
    } 
}

 

We implement our other buttons onclick event handlers:

function bttnCallClick() { 
    WinJS.xhr({ 
        url: 'http://localhost:50238/api/quote/GetSecretQuote', 
        type: 'GET' 
    }).then( 
        function (response) { 
            var log = document.getElementById("log"); 
            log.value = "bttnCall response \r\n"; 
            var json = JSON.parse(response.responseText); 
            log.value += json; 
        }, 
        function(error) { 
            var log = document.getElementById("log"); 
            log.value = "Error in call " + error; 
        }, 
        function (progress) { 
        } 
); 
}

Basically we're requesting the quote but as we're not logged in, our ApiAuthorization class will revoke authorization.

hit_mvc_without_jwt

 

function bttnTokenCallClick() { 
    WinJS.xhr({ 
        url: 'http://localhost:50238/api/quote/GetSecretQuote', 
        type: 'GET', 
        headers: { 
            "Authorization": "Bearer " + token 
        } 
    }).then( 
        function(response) { 
            var log = document.getElementById("log"); 
            log.value = "bttnCall response \r\n";

            var json = JSON.parse(response.responseText); 
            log.value += json; 
        }, 
        function(error) { 
            var log = document.getElementById("log"); 
            log.value = "Error in token call " + error; 
        }, 
        function(progress) { 
        } 
    ); 
};

 

Here we add the token to our Authorization header so it will get parsed in our Global.asax file.
hit_mvc_with_jwt

 

We could extend this example to add more in depth user role authentication but we simply need to call the GraphApi for this.

I hope you enjoyed this series. As you have already seen, there is a lot of configuration to do in order to configure ACS and WAAD. But, with a lot of trial and error you will get a better understanding on how things work.

Download part 2 of this series, the zip contains the Windows Store Application and an updated MVC Application.

Posted in: Azure | WIF

Tags: , ,