You might have noticed that I'm really into the Belgian eID. Needless to say I had to look at how we can use the Belgian eID in Windows Azure. In this series I'll show you how you can integrate it with your existing or future cloud projects.
We'll start by taking a look at the eID Identity Provider (or eID IdP), an open source project hosted on code.google.com:
http://code.google.com/p/eid-idp/. The IdP runs in JBoss and uses a relational database (like MySQL, PostgreSQL, ...). To keep things simple, we won't be looking at how you can install the IdP. Besides running the eID IdP yourself, you can also use the hosted version on the
e-Contract website (managed by Frank Cornelis, one of the contributors of the eID IdP project). The following page shows the supported protocols:
https://www.e-contract.be/eid-idp/main
Since we're going to use Windows Identity Foundation, these metadata endpoints seem to be the most appropriate:
If you look at the claims offered by the WS-Federation Authentication and Identification endpoint, it seems we can get access to some useful information:
schemas.xmlsoap.org/ws/2005/05/identity/claims/name
schemas.microsoft.com/ws/2008/06/identity/claims/role
schemas.xmlsoap.org/ws/2005/05/identity/claims/surname
schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname
schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier
schemas.xmlsoap.org/ws/2005/05/identity/claims/streetaddress
schemas.xmlsoap.org/ws/2005/05/identity/claims/locality
schemas.xmlsoap.org/ws/2005/05/identity/claims/postalcode
schemas.xmlsoap.org/ws/2005/05/identity/claims/gender
schemas.xmlsoap.org/ws/2005/05/identity/claims/dateofbirth
be:fedict:eid:idp:nationality
be:fedict:eid:idp:pob
be:fedict:eid:idp:photo
be:fedict:eid:idp:age
Enough chitchat, show me some code!
Setting up the web application
We're going to build a webrole that will use the eID IdP for authentication and identification. To build this application you'll need the Azure SDK, ASP.NET MVC3, the WIF SDK and the WIF runtime:
Create a new Windows Azure project, add an ASP.NET MVC 3 web role and choose the Internet Application template. Now if you run the application and you'll see that you can view the application without having to login, but this isn't what we want. We would like to have an application that requires you to login using the eID IdP. This way we'll know the user is authenticated without having to do the management of usernames, passwords, security, ... And we're actually externalizing the authentication.
Claims! Claims! Claims!
Before we can run the application we'll write some code to make everything go well and to display the claims we received from the eID IdP. It's important to know that this IdP uses the realm URI as the return URL after a successful login. This means if you used
http://127.0.0.1 as realm, you'll be redirected here. But this isn't what we want...
What we want is that the user gets redirected to an action like /Account/Display after a successful login (to show the user's claims). To achieve this you'll need to add the following code in the Global.asax file:
protected void Application_Start()
{
...
FederatedAuthentication.ServiceConfigurationCreated += OnServiceConfigurationCreated;
}
private void OnServiceConfigurationCreated(object sender, ServiceConfigurationCreatedEventArgs e)
{
FederatedAuthentication.WSFederationAuthenticationModule.SignedIn += new EventHandler(WSFederationAuthenticationModule_SignedIn);
}
private void WSFederationAuthenticationModule_SignedIn(object sender, EventArgs e)
{
HttpContext.Current.Response.Redirect("/Account/Display");
}
Ok this is a good starting point. Now, after a successful login, the user will be redirected to /Account/Display. This means we'll need to write the required code in the AccountController. Let's start with some helper functions that will allow us to get all the claims available for the current identity and an other function that will help us read the value for a specific claim:
private ClaimCollection GetClaims()
{
var claimsPrincipal = Thread.CurrentPrincipal as IClaimsPrincipal;
var claimsIdentity = (IClaimsIdentity)claimsPrincipal.Identity;
return claimsIdentity.Claims;
}
private string GetClaimValue(ClaimCollection claims, string claim)
{
var claimRecord = claims.FirstOrDefault(o => o.ClaimType == claim);
if (claimRecord != null)
return claimRecord.Value;
else
return null;
}
Now with this code it will become very easy to write the required actions: Display and Photo. This Display action maps the claims to the UserInfo model and displays it in the view:
public ActionResult Display()
{
var claims = GetClaims();
var user = new UserInfo();
user.NameIdentifier = GetClaimValue(claims, "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier");
user.Name = GetClaimValue(claims, "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name");
user.StreetAddress = GetClaimValue(claims, "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/streetaddress");
user.Nationality = GetClaimValue(claims, "be:fedict:eid:idp:nationality");
user.POB = GetClaimValue(claims, "be:fedict:eid:idp:pob");
user.Locality = GetClaimValue(claims, "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/locality");
user.Surname = GetClaimValue(claims, "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname");
user.Gender = GetClaimValue(claims, "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/gender") == "1" ? "Male" : "Female";
user.GivenName = GetClaimValue(claims, "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname");
user.DateOfBirth = GetClaimValue(claims, "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/dateofbirth");
user.PostalCode = GetClaimValue(claims, "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/postalcode");
user.Age = GetClaimValue(claims, "be:fedict:eid:idp:age");
user.AuthenticationMethod = GetClaimValue(claims, "http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod");
return View(user);
}
In the Display view we'll simply show all properties of the model, but we'll also show something else...
<img style="float: right;" src="/Account/Photo" alt="" width="140" height="200" />
Yes... that's right. We'll show the user's photo. One of the claims is the be:fedict:eid:idp:photo claim. This claim contains your picture encoded using standard Base64 encoding. And knowing this makes it very easy to build the Photo action:
public ActionResult Photo()
{
var photo = GetClaims().FirstOrDefault(o => o.ClaimType == "be:fedict:eid:idp:photo");
if (photo != null)
{
var stream = new MemoryStream(Convert.FromBase64String(photo.Value));
return new FileStreamResult(stream, "image/jpg");
}
else
{
return new EmptyResult();
}
}
The result
When you start the application, you'll be redirected to the eID IdP:
After entering the PIN code the Java applet will start reading the eID and the eID IdP will redirect to the realm URI (in this case 127.0.0.1). Since we made that small change in the Global.asax file we'll be redirected to /Account/Display after a successful login:
Enjoy!
Finally... I decided to buy an Office 365 account and after playing around with the platform I'm happy I did! In this article I'll show an easy to follow guide on how you can link your Office 365 subscription to a domain name you have at Combell. Why Combell? Because... it's one of the best hosting companies around! Ok maybe they're a bit expensive, but their service is worth the money. Great interface to manage all your products, great support (I talked to their helpdesk on a sunday at 9PM!), ...
I own the domain sandrino.be and it used to be hosted at One.com. Since I wasn't that happy with One.com (and their DNS manager for the domain isn't that great) I decided to move it to Combell. The procedure was quite simple: I ordered the domain on Combell and payed for it. After a few minutes I got an email from dns.be with a transfercode I could use on the Combell order page. And poof... that was it, my domain was now on Combell and I was ready to start!
Validating the domain
Your domain can be used for sending mails, for your website, ... But before you can actually use it Office 365 needs to know you're the owner of the domain. That's what we'll do first. Get an Office 365 account and navigate to https://portal.microsoftonline.com/Domains/DomainManager.aspx. This page allows you to add your own domains in Office 365 and this is where I added my domain.
But after adding the domain you'll see this:

In short, you'll need to add something to your domain so that Microsoft can be sure you really own it. The only thing you'll need to do is add a TXT record to the domain and as you'll see this is very easy to do with Combell. Write down the text you see in the table in the Destination or Points to Address column.
First you'll need to navigate to http://my.combell.com (keep the current Verify domain page open, you'll need to get back to it soon). Since I transferred my domain from an other registrar I need to change the nameservers. You can skip this step if you didn't transfer a domain to Combell.
Once you arrive on the page go to Mijn domeinnamen and one of the domain names you own using the Beheer button.

The Beheer button brings you to the settings of this domain and will also allow you to change the nameservers for the domain. Go to Nameservers in the left menu and choose the option Combell nameservers. This will allow you to modify the settings of the domain.

To modify the settings navigate back to http://my.combell.com but choose DNS & forwarding beheer this time. And yet again, choose the domain you want to modify using the DNS & forwarding button this time.

Choose the option TXT-records from the left menu and add the destination we saw on the Verify domain page in Office 365. Here is how I did it:

That's it! Now go back to the Verify domain page in Office 365 and press the Verify button. If you see the Edit name server records this means the verification succeeded.
Changing the nameservers
There you go, Office 365 is ready to take over the domain. Take a look at the following page in the wizard:

This page explains that you need to change the nameservers for the domain to Microsoft's nameservers. As a result, you'll be able to modify the domain settings in Office 365 but also to link it to the services you use in Office 365. Again, don't close this page.
Take the 2 nameservers and set them in the settings of your domain (under Domeinnamen, the domain, Nameservers):

Go back to the Edit name server records page in Office 365 and press Next. As a result you should see that the domain is now active:

Using the domain
The reason why I wanted to do this is to have my email addresses in Office365 ending with "@sandrino.be". Go to the Users page (https://portal.microsoftonline.com/UserManagement/UserManager.aspx) and open a user for who you'll want to change the email address and go to the Properties page.
On this page you'll be able to change the primary email address:

If you want to edit yourself you'll see that the username textbox and combobox are read-only. To change the primary email address you'll need to create a temp user with administrator rights, login with this user, modify your actual user, delete the temp user.
Enjoy!
Dynamics CRM 2011 allows you to manage all kinds of data (xRM) but when you start working with actual people it might come in handy to work with eID cards. Especially in healthcare, education, ... where the end user has to work with a lot of people during the day. Instead of manually entering the data over and over again you could use the eID card to automate this process. That's what we'll build today!
Before we get started there are some prerequisites:
Note: This post will explain how to do this in a regular deployment. For IFD I'll write a post in some time explaining how to do this with Windows Azure.
Java?
Sigh... Java. I never thought I'de mention this in one of my posts but yes... we'll be using Java. On code.google.com you'll find a project named
eid-javascript-lib. This project allows you to use the eID Java Applet to make the connection between your websites using Javascript and the Belgian eID. And since Dynamics CRM 2011 relies heavily on Javascript this is the perfect match.
This is how the solution will look like:
In CRM 2011 you are able to add some web resources, but the system only allows a few types like HTML, CSS, JS, XML, PNG, ... You might think, why not change the extension to one of these types... but this won't work since the MIME type used to serve the Java applets will not be the right one (like application/x-java-jnlp-file for example, more on this later). That's why we'll need a seperate site to host the Java specific stuff.
Hosting the eid-javascript-lib and the applets
Before we can dive into Dynamics CRM 2011 we need to host the required Javascript and Java files. Remember that this is only suited for regular deployments, the guide to host this in an IFD will follow soon.
First we'll create an IIS website that will contain all the data. If your CRM deployment is https be sure to also make the IIS website an https website. If you don't do this, you'll get some nasty popups saying only the secure parts of the page are displayed etc...
So I've created a website in IIS with HTTPS binding (
https://localhost:100/). In this website I'll be hosting 3 types of files:
*.js,
*.jar and
*.jnlp. If you want these to work correctly you'll need to check if the MIME Types have been configured in your IIS website. Go to your website and open the
MIME Types option:
Be sure the following MIME Types exist:
Now the site is ready to host our files. I assume you installed the Belgian eID SDK (see prerequisites). If you did, you should have a directory like this: 'C:\Documents and Settings\<user>\My Documents\Belgium Identity Card SDK\beidlib\Java\bin'. This directory should contain the following files:
- applet-launcher.jar
- beid.jnlp
- beid35JavaWrapper-linux.jar
- beid35JavaWrapper-mac.jar
- beid35JavaWrapper-win.jar
- beid35libJava.jar
- BEID_Applet.jar
- be_belgium_eid.js
- beid_java_plugin.jnlp
Modify both *.jnlp files, the codebase (2nd. line) should contain the address of the IIS website containing all these files. Here is an example of the beid.jnlp file:
<?xml version="1.0" encoding="utf-8"?>
<jnlp codebase="https://localhost:100/" href="beid.jnlp">
<information>
<title>Java Binding to Belgium eID Middleware 3.5</title>
<vendor>Fedict</vendor>
<offline-allowed/>
</information>
<security>
<all-permissions/>
</security>
<resources os="Windows">
<nativelib href = "beid35JavaWrapper-win.jar" />
</resources>
<resources os="Linux">
<nativelib href = "beid35JavaWrapper-linux.jar" />
</resources>
<resources os="Mac OS X">
<nativelib href = "beid35JavaWrapper-mac.jar" />
</resources>
<component-desc />
</jnlp>
Finally create a new file in your website called web.config containing the following (this will make sure the Java Applets are hosted correctly):
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<staticContent>
<mimeMap fileExtension=".jnlp" mimeType="application/x-java-jnlp-file" />
</staticContent>
</system.webServer>
</configuration>
This should be the end result:
Creating the Dynamics CRM 2011 solution
Now that we've done the heavy lifting it's time to do some work in CRM 2011. I've started by adding a new button in the Ribbon of the contact page. Since this post is focused on the eID I will not explain how to modify the Ribbon. Take a look at
http://msdn.microsoft.com/en-us/library/gg309639.aspx to get started with Ribbon modifications.
This is my button on the contact page:
Now the goal is that the user clicks the button and a dialog opens. In this dialog the user will be able to read the eID card and if required store the information in the current contact. First we'll write the code for the dialog. The dialog is actually very simple. We'll just write some HTML to display the form and some Javascript to interact with it:
<html>
<head>
<title>eID: Read</title>
<script type="text/javascript" src="https://localhost:100/be_belgium_eid.js"></script>
<script language="javascript" type="text/javascript">
// Initialize the reader.
var cardReader = new be.belgium.eid.CardReader();
// Bind error events.
function noCardPresentHandler() { window.alert("No card present!"); }
cardReader.setNoCardPresentHandler(noCardPresentHandler);
function noReaderDetectedHandler() { window.alert("No reader detected!"); }
cardReader.setNoReaderDetectedHandler(noReaderDetectedHandler);
function appletNotFoundHandler() { window.alert("Applet not found!"); }
cardReader.setAppletNotFoundHandler(appletNotFoundHandler);
function appletExceptionHandler(e) { window.alert("Error reading card!\r\nException: " + e + "\r\nPlease try again."); }
cardReader.setAppletExceptionHandler(appletExceptionHandler);
// Read the card.
function readCard() {
document.getElementById("content").value = "Please wait ...";
window.card = cardReader.read();
if (window.card != null) {
document.getElementById("content").value = window.card.toString();
} else {
document.getElementById("content").value = "No card returned.";
}
}
// Save the card to contact.
function saveCard() {
var data = { firstName: card.getFirstName1().toString(),
lastName: card.getSurname().toString(),
street: card.getStreet().toString(),
streetNumber: card.getStreetNumber().toString(),
zipCode: card.getZipCode().toString(),
country: card.getCountry().toString(),
municipality: card.getMunicipality().toString()
};
self.opener.onUpdateContact(data);
self.close();
}
</script>
</head>
<body style='background-color: rgb(233, 237, 241)'>
<table>
<tr>
<td valign='top'>
<applet code="org.jdesktop.applet.util.JNLPAppletLauncher" codebase="https://localhost:100"
width="140" height="200" name="BEIDAppletLauncher" id="BEIDAppletLauncher" archive="applet-launcher.jar,beid35libJava.jar,BEID_Applet.jar">
<param name="codebase_lookup" value="false">
<param name="subapplet.classname" value="be.belgium.beid.BEID_Applet">
<param name="progressbar" value="true">
<param name="jnlpNumExtensions" value="1">
<param name="jnlpExtension1" value= "https://localhost:100/beid.jnlp">
<param name="debug" value="false"/>
<param name="Reader" value=""/>
<param name="OCSP" value="-1"/>
<param name="CRL" value="-1"/>
<param name="jnlp_href" value="https://localhost:100/beid_java_plugin.jnlp" />
</applet>
</td>
<td valign='top'>
<textarea id='content' wrap='virtual' cols='80' rows='20'> </textarea>
<br>
<input title="Read Card" onclick=javascript:readCard() value="Read Card" type=button>
<input title="Save" onclick=javascript:saveCard() value="Save" type=button>
</td>
</tr>
</table>
</body>
</html>
There isn't actually that much to it:
- Line 4: This references the Javascript file hosted on our IIS Website.
- Line 7-28: Loads the card and the card reader. It actually interacts with the Java Applet to talk to the hardware.
- Line 31-42: Puts all the data in an object and returns it to the window that created this dialog (the contact page).
- Line 49-61: Displays the Java Applet on the page. Mind lines 49, 55 and 60 as they also reference our IIS website.
That's it for the dialog. I've saved the HTML page as a Web Resource and called it contacteid_page (write this down, you'll need this later on).
Now we still need to write some code for the Ribbon button and for updating entity. I've created a new Web Resource holding this code.
// Open the card reader page.
function readCard() {
window.open ("https://crm2011.some.url//WebResources/new_contacteid_page", 'ReadEid', 'width=850, height=450');
}
// Update the contact based on the eID data.
function onUpdateContact(data)
{
Xrm.Page.getAttribute("firstname").setValue(data.firstName);
Xrm.Page.getAttribute("lastname").setValue(data.lastName);
Xrm.Page.getAttribute("address1_line1").setValue(data.street + ' ' + data.streetNumber);
Xrm.Page.getAttribute("address1_city").setValue(data.municipality);
Xrm.Page.getAttribute("address1_postalcode").setValue(data.zipCode);
Xrm.Page.getAttribute("address1_country").setValue(data.country);
Xrm.Page.getAttribute("address1_stateorprovince").setValue('');
};
Again this code is pretty straightforward:
-
The readCard function is called when I click the Ribbon button and will show the dialog. Note that the URL ends with the name of the Web Resource I created for the HTML page.
-
The onUpdateContact function will be called when I press the Save button in my dialog. Finally the data is used to update the entity.
Our Customizations In Action!
First I've opened my contact list:
Now I've opened the contact Cathan Cook and pressed the Connect eID button:
If all goes well you should see a security warning. Press Run to continue:
To read the card I press the Read button and this is the result:
To update the CRM contact press Save and you'll see that the dialog will close and your contact will be updated:
That wasn't so hard was it?
Troubleshooting Javascript
We're working with 2 websites, Javascript, Java Applets, ... and this can sometimes be a pain to troubleshoot. Here are some quick tips!
When saving the dialog to the entity you might bump into a
Permission Denied error caused by the same origin policy (
http://en.wikipedia.org/wiki/Same_origin_policy). To fix this issue, make sure the hostname of the dialog's URL is the same as the hostname of the CRM server. In my example I had the following URLs:
Do you see the difference? One link has 'api' in the hostname and the other one doesn't.
Troubleshooting the Java Applet
The following issue took me a while to troubleshoot... when pressing the Read Card button I suddenly got an Error reading card! error. And a few minutes before everything just worked!
After investigating a bit I stumbled upon the following:
<TD vAlign=top><APPLET id=BEIDAppletLauncher name=BEIDAppletLauncher archive=applet-launcher.jar,beid35libJava.jar,BEID_Applet.jar codeBase="https://localhost:100" code=org.jdesktop.applet.util.JNLPAppletLauncher width=140 height=200><PARAM NAME="_cx" VALUE="3704"><PARAM NAME="_cy" VALUE="5291"></APPLET> </TD>
<TD vAlign=top><TEXTAREA id=content wrap=virtual cols=80 rows=20> </TEXTAREA> <BR><INPUT title="Read Card" onclick=javascript:readCard() value="Read Card" type=button> <INPUT title=Save onclick=javascript:saveCard() value=Save type=button> </TD></TR></TBODY></TABLE></BODY></HTML>
It looked like the code of my HTML page (for the dialog) suddenly changed. Everything was wrapped on two lines and some parts were actually missing. The reason why this happened was because I modified the file using the Text Editor in the Web Resource page. Don't use the Text Editor because it will change your page! Always modify the file locally and upload it using the browse button.
Conclusion
With a bit of magic you can now use the Belgian eID in Dynamics CRM 2011. Think about the possibilities here... this is great for all healthcare, education, ... projects. Projects where you'll want the users to sign in visitors, register people, ...
Enjoy!
While I was playing around with Azure AppFabric ACS I got the following error:
Error message 401.2: Unauthorized: Logon failed due to server configuration. Verify that you have permission to view this directory or page based on the credentials you supplied and the authentication methods enabled on the Web server. Contact the Web server's administrator for additional assistance.

This error only occured when using ACS in a regular Web Forms application in IIS. But for ASP.NET MVC applications everything worked perfectly. To get my Web Forms application working I had to to turn a Windows feature on: IIS Metabase and IIS 6 configuration compatiblity

After updating the IIS installation the Web Forms application worked perfectly using Azure AppFabric ACS authentication. If you're doing this on Windows Server 2008 you can simply modify the Web Role.
Enjoy
A few months ago I had the opportunity to work together with our Enterprise Solutions division on a small CRM project. Part of this assignment was to work on a session for the Belgian Dynamics Community day. Besides that I had to create a Windows Phone 7 application that would connect to Dynamics CRM online and Windows Azure AppFabric to do all kinds of things.
One of those things was to get all contacts in CRM and see which of those were close to me. Based on this nice concept I wrote an article in Microsoft's MSDN Magazine:
Location-Aware Programming: Visualizing Bing Routes on Windows Phone 7
.jpg)
And I'm currently working with a customer to implement this on a commercial multi-platform mobile application.
Enjoy...