Login to OWA programmatically using saml token.

Before reading this article, you need to prepare the environment. You have to have configured Exchange 2013, AD FS and so on. Configuration of the environment is out of this article. There are a lot of posts on the internet about it but I advise you to use it:

http://nilsvanwoensel.azurewebsites.net/?p=74

I followed all steps of this article. There were some hardships but in general all was good. The only one note from my side is that I have configured all environment at the one server. I did not use separate server for the AD FS.

If you configured all correctly, after opening "https://.../Owa" page on browser you would see the following page:

It is the standard AD FS login page. In this case the usual mechanism of authorization in OWA has the following steps. They are:

  • 1. Sending the user login and password to the AD FS side.
  • 2. Getting the SAML token and redirecting to OWA.
  • 3. Autologin to OWA using the SAML token.

We will try to change these steps. Let us start with the login page. The main magic feature of the page is that straight access to it does not exist. We cannot open the page at Notepad and find it in our disk space. To customize the page, we must use special features. They are here:

https://technet.microsoft.com/enus/library/dn636121%28v=ws.11%29.aspx?f=255&MSPPError=-2147217396
https://deploywindows.info/2015/05/05/customize-your-adfs-login-page/

These features make it possible for us to embed some javascript code to the page. Using javascript code we can interact with any server. For example, after customization we have such page:

The next step we need to create a simple ASP MVC .NET app which will interact with AD FS to receive the SAML token. You can see an example of the source code at the bottom.

private string Case1(string login, string password)
{
var binding = new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential)
{
ClientCredentialType = HttpClientCredentialType.None
};
var trustChannelFactory = new WSTrustChannelFactory(binding, new EndpointAddress("https://datacenterr2/adfs/services/trust/13/usernamemixed"))
{
TrustVersion = TrustVersion.WSTrust13
};
var channelCredentials = trustChannelFactory.Credentials;
channelCredentials.UserName.UserName = login;
channelCredentials.UserName.Password = password;
channelCredentials.SupportInteractive = false;
var rst = new RequestSecurityToken()
{
AppliesTo = new EndpointReference("https://datacenterr2/owa"),
KeyType = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer",
RequestType = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue",
TokenType = "urn:oasis:names:tc:SAML:1.0:assertion"
};
var token = trustChannelFactory.CreateChannel().Issue(rst) as GenericXmlSecurityToken;
var outherToken = token.TokenXml.OuterXml;
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine("");
stringBuilder.AppendLine("");
stringBuilder.AppendLine(outherToken);
stringBuilder.AppendLine("
");
stringBuilder.AppendLine("urn:oasis:names:tc:SAML:1.0:assertion");
stringBuilder.AppendLine("http://schemas.xmlsoap.org/ws/2005/02/trust/Issue");
stringBuilder.AppendLine("http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey");
stringBuilder.AppendLine("
");
var html = WebUtility.HtmlEncode(stringBuilder.ToString());
return html;
}

The SAML token must have the following appearance:



Summing up. Now we are going to create consequence of our steps. Our solution will have the following steps:

  • 1. A user opens "https://.../Owa". After this he will be redirected to our custom page. The screen of this page you can see above.

  • 2. A user fills out the login and the password fields. After this we will send these values to our .Net app using javascript http request.

    $("#login-complete").on("click", function() {
    var url = "https://datacenterr2/UsialTestWeb/home/LoginUser?" + "&login=" + $("#input-login").val() + "&password=" + $("#input-password").val()
    $.ajax({url: url, success: function(result){
    if(result !== "Error"){
    var inner = "<form method=\"POST\" name=\"hiddenform\" action=\"https://datacenterr2:443/owa\"><input type=\"hidden\"
    name=\"wa\" value=\"wsignin1.0\" /><input type=\"hidden\" name=\"wresult\" value=\"";
    var inner2 = result;
    var inner3 = "\"/><input type=\"hidden\" name=\"wctx\" value=\"rm=0&id=passive&ru=%2fowa%2f\" /><noscript><p>Script is disabled. Click Submit to continue.
    </p><input type=\"submit\" value=\"Submit\" /></noscript>";
    inner = inner + inner2 + inner3;
    workArea.innerHTML = inner;
    window.setTimeout('document.forms[0].submit()', 0);
    }
    else
    {
    alert('Error;');
    }
    }, error : function(error){
    alert('Error;');
    }});
    });

    $("#login-complete").on("click", function() { - It is the click event on the "login" button.
    $("#input-login").val() and $("#input-password").val() - They are values of inputs tags for the password and the login.
    https://datacenterr2/UsialTestWeb/home/LoginUser - It is the url of the action our .Net app which is hosted at IIS.
    result - This is the SAML token which we get from the server side (our .Net web app)
    inner - It is HTML form we build dynamically so as to send SAML token to the OWA.
    action=\"https://datacenterr2:443/owa\" - This is the OWA url.

  • 3. After getting the password and the login at the server side we will call the AD FS " https://.../adfs/services/trust/13/usernamemixed " endpoint in order that receive the SAML token.

    public ActionResult LoginUser(string login, string password)
    {
    var html = Case1(login, password);
    return Content(html);
    }

    public ActionResult LoginUser(string login, string password) - It is the action of the "Home" controller. Our server .Net app is ASP MVC web app.
    Case1(login, password) - This is the method of getting the SAML token. You can find the source code of it above.

  • 4. After receiving the SAML token from the AD FS we will transfer it back to our custom page.

  • 5. The last step is to send the SAML token to the OWA and redirect a user to the OWA as well. We will make it in the success event at javascript side. You can find in at the step 2.

  • February 20,2017
  • Eugen Goroluov