Office 365 Lync displays wrong name

Recently we have come across an interesting bug in Office 365. In the scenario where you use ADFS to authenticate your Office 365 users and some of the users have multiple email address aliases assigned using adsiedit.msc, Lync might display a wrong name.

For example, user’s name is Walter and his primary email address is walter@office365-is-awesome.com (not a real email address). Imagine that Walter’s colleague Jesse is leaving the company and they need Walter to take over Jesse’s clients and make sure that all emails that addressed to Jesse are now sent to Walter. At the same time, you don’t want to keep Jesse’s mailbox active because Office 365 charges you per mailbox and that would be a waste of money. So, you archive Jesse’s existing mailbox and add an alias jesse@office365-is-awesome.com to Walter’s mailbox. And, because you use ADFS, you have to add aliases using adsiedit.msc instead going through Office 365 management portal. Make sense, right? Well, this is where it starts being interesting and very-very confusing. Now, when Walter logs into Lync some of the users will see Jesse’s name show up in their Lync client instead of Walter. Weird, isn’t it?

What appears to be happening is that Lync Address Book Service (ABSConfig) queries proxyAddress attribute in user properties and uses whatever entry query returns first. Because proxyAddress field stores data in alphabetical order, in Walter’s user attributes the name “Jesse” entry comes before “Walter.” That’s why we see the wrong name displayed. It’s that simple.

Anyways, if this was an on-premise Lync server then there at least couple of fixes for this problem. Both fixes have to do with making changes on the server side. But this is Office 365, and we do not have access to the server-side. What are those of us living in the cloud supposed to do?! As far as I know, there is no fix, but there is a workaround. Instead of creating email address aliases using adsiedit.msc, you can:

  1. Create a distribution list in Office 365 management portal. Make sure to allow external senders send emails to this distribution list, so that emails don’t bounce back.
  2. Assign any email address aliases to that distribution list right from Office 365 management portal. For example, jesse@office365-is-awesome.com or this-not-really-walter@office365-is-awesome.com
  3. Add an intended recipient(s) to the distribution list. For example, walter@office365-is-awesome.com. Now, when people send email to Jesse every email will be sent to Walter’s mailbox and everyone will see Walter as Walter when he signs into Lync. It’s a win-win.
  4. (Optional) Hide distribution list from Address Book, so your people don’t get confused when they search internal Global Address Book.

Well, it’s not exactly a fix, it’s a workaround and it will do for now. I do hope though that Microsoft will fix this bug in Office 365. Sometime in the next 20 minutes would be great. ;)

Adjusting the Home Realm Discovery page in ADFS to support Email Addresses

Over on the Geneva forums a question was asked:

Does anyone have an example of how to change the HomeRealmDiscovery Page in ADFSv2 to accept an e-mail address in a text field and based upon that (actually the domain suffix) select the correct Claims/Identity Provider?

It's pretty easy to modify the HomeRealmDiscovery page, so I thought I'd give it a go.

Based on the question, two things need to be known: the email address and the home realm URI.  Then we need to translate the email address to a home realm URI and pass it on to ADFS.

This could be done a couple ways.  First it could be done by keeping a list of email addresses and their related home realms, or a list of email domains and their related home realms.  For the sake of this being an example, lets do both.

I've created a simple SQL database with three tables:

image

Each entry in the EmailAddress and Domain table have a pointer to the home realm URI (you can find the schema in the zip file below).

Then I created a new ADFS web project and added a new entity model to it:

image

From there I modified the HomeRealmDiscovery page to do the check:

//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------

using System;

using Microsoft.IdentityServer.Web.Configuration;
using Microsoft.IdentityServer.Web.UI;
using AdfsHomeRealm.Data;
using System.Linq;

public partial class HomeRealmDiscovery : Microsoft.IdentityServer.Web.UI.HomeRealmDiscoveryPage
{
    protected void Page_Init(object sender, EventArgs e)
    {
    }

    protected void PassiveSignInButton_Click(object sender, EventArgs e)
    {
        string email = txtEmail.Text;

        if (string.IsNullOrWhiteSpace(email))
        {
            SetError("Please enter an email address");
            return;
        }

        try
        {
            SelectHomeRealm(FindHomeRealmByEmail(email));
        }
        catch (ApplicationException)
        {
            SetError("Cannot find home realm based on email address");
        }
    }

    private string FindHomeRealmByEmail(string email)
    {
        using (AdfsHomeRealmDiscoveryEntities en = new AdfsHomeRealmDiscoveryEntities())
        {
            var emailRealms = from e in en.EmailAddresses where e.EmailAddress1.Equals(email) select e;

            if (emailRealms.Any()) // email address exists
                return emailRealms.First().HomeRealm.HomeRealmUri;

            // email address does not exist
            string domain = ParseDomain(email);

            var domainRealms = from d in en.Domains where d.DomainAddress.Equals(domain) select d;

            if (domainRealms.Any()) // domain exists
                return domainRealms.First().HomeRealm.HomeRealmUri;

            // neither email nor domain exist
            throw new ApplicationException();
        }
    }

    private string ParseDomain(string email)
    {
        if (!email.Contains("@"))
            return email;

        return email.Substring(email.IndexOf("@") + 1);
    }

    private void SetError(string p)
    {
        lblError.Text = p;
    }
}

 

If you compare the original code, there was some changes.  I removed the code that loaded the original home realm drop down list, and removed the code to choose the home realm based on the drop down list's selected value.

You can find my code here: http://www.syfuhs.net/AdfsHomeRealm.zip

Adding ADFS as an Identity Provider in ACS v2

Ever have one of those days where you swear that you've written something, but can't find it?  I could have sworn that I wrote this article before.  Ah well.

--

It makes a lot of sense to use ACS to manage Identity Providers.  It also makes sense to use Active Directory for letting users sign in to your cloud application.  Therefore we would hope that ACS and ADFS play nicely together.  It turns out they do.  in a previous post I talked about federating ACS and ADFS, where ACS is an identity provider to ADFS.  Now lets reverse it.  We want users to be redirected to ACS, then to ADFS to sign in.

First things first.  Lets log into our ACS namespace and navigate to the Identity Provider section, and then Add an Identity Provider:

image

From there we want to select what type of provider to use, and in this case we will select WS-Federation:

image

We are now provided with a form to fill out.  There are five properties: Display Name, WS-Federation metadata, Login Link text, Image Url, and Email domain names.

Display name is fairly straightforward.  What do you want the internal name of this IdP to be?

Next we need to provide a link to the Federation Metadata document that ADFS provides.  The path is https://adfs.domain.com/FederationMetadata/2007-06/FederationMetadata.xml.

Then we give it a public name, such as "ObjectSharp Internal Users".

If we want to use an image instead if showing text, we can provide a path to the image.

Finally we are asked for a semicolon separated list of email domains.  This may seem a bit confusing at first.  Basically, it allows us to filter out the IdP from the Home Realm Discovery page, and requires that the user enter in their email address.  That way, instead of seeing the "ObjectSharp Internal Users" link, we are provided a text box, where we need to enter an email address like ssyfuhs@objectsharp.com.  ACS will then look up the domain in their list, and if there is a reference to it, it will redirect to the IdP.

This takes care of the ACS bit.  just like in the previous post, you need to tell the other IdP about the other.  So we need to tell ADFS that ACS will be calling.  This is pretty simple.  We just need to add a relying party to ADFS using the ACS metadata.  You can find the ACS metadata under Application Integration:

image

There isn't much to federating ADFS to ACS and vice-versa.

Creating a Claims Provider Trust in ADFS 2

One of the cornerstones of ADFS is the concept of federation (one would hope anyway, given the name), which is defined as a user's authentication process across applications, organizations, or companies.  Or simply put, my company Contoso is a partner with Fabrikam.  Fabrikam employees need access to one of my applications, so we create a federated trust between my application and their user store, so they can log into my application using their internal Active Directory.  In this case, via ADFS.

So lets break this down into manageable bits. 

First we have our application.  This application is a relying party to my ADFS instance.  By now hopefully this is relatively routine.

Next we have the trust between our ADFS and our partner company's STS.  If the company had ADFS installed, we could just create a trust between the two, but lets go one step further and give anyone with a Live ID access to this application.  Therefore we need to create a trust between the Live ID STS and our ADFS server.

This is easier than most people may think.  We can just use Windows Azure Access Control Services (v2).  ACS can be set up very easily to federate with Live ID (or Google, Yahoo, Facebook, etc), so we just need to federate with ACS, and ACS needs to federate with Live ID.

Creating a trust between ADFS and ACS requires two parts.  First we need to tell ADFS about ACS, and second we need to tell ACS about ADFS.

To explain a bit further, we need to make ACS a Claims Provider to ADFS, so ADFS can call on ACS for authentication.  Then we need to make ADFS a relying party to ACS, so ADFS can consume the token from ACS.  Or rather, so ACS doesn't freak out when it see's a request for a token for ADFS.

This may seem a bit confusing at first, but it will become clearer when we walk through the process.

First we need to get the Federation Metadata for our ACS instance.  In this case I've created an ACS namespace called "syfuhs2".  The metadata can be found here: https://syfuhs2.accesscontrol.windows.net/FederationMetadata/2007-06/FederationMetadata.xml.

Next I need to create a relying party in ACS, telling it about ADFS.  To do that browse to the Relying party applications section within the ACS management portal and create a new relying party:

image

Because ADFS natively supports trusts, I can just pass in the metadata for ADFS to ACS, and it will pull out the requisite pieces:

image

Once that is saved you can create a rule for the transform under the Rule Groups section:

image

For this I'm just going to generate a default set of rules.

image

This should take care of the ACS side of things.  Next we move into ADFS.

Within ADFS we want to browse to the Claims Provider Trusts section:

image

And then we right-click > Add Claims Provider Trust

This should open a Wizard:

image

Follow through the wizard and fill in the metadata field:

image

Having Token Services that properly generate metadata is a godsend.  Just sayin'.

Once the wizard has finished, it will open a Claims Transform wizard for incoming claims.  This is just a set of claims rules that get applied to any tokens received by ADFS.  In other words, what should happen to the claims within the token we receive from ACS?

In this case I'm just going to pass any claims through:

image

In practice, you should write a rule that filters out any extraneous claims that you don't necessarily trust.  For instance, if I were to receive a role claim with a value "Administrator" I may not want to let it through because that could give administrative access to the user, even though it wasn't explicitly set by someone managing the application.

Once all is said and done, you can browse to the RP, redirect for authentication and will be presenting with this screen:

image

After you've made your first selection, a cookie will be generated and you won't be redirected to this screen again.  If you select ACS, you then get redirected to the ACS Home Realm selection page (or directly to Live ID if you only have Live ID).

Federated Authentication with ADP using ADFS 2

There's a great article over on the Ask the Directory Services Team blog on how to set up federation to ADP using ADFS 2.0.

I think we can all reach a general consensus that using ADFS as the identity provider makes federation pretty easy.  And awesome.

Plugging Application Authentication Leaks in ADFS

When you set up ADFS as an IdP for SAML relying parties, you are given a page that allows you to log into the relying parties.  There is nothing particularly interesting about this fact, except that it could be argued that the page allows for information leakage.  Take a look at it:

image

There are two important things to note:

  • I'm not signed in
  • I can see every application that uses this IdP

I'm on the fence about this one.  To some degree I don't care that people know we use ADFS to log into Salesforce.  Frankly, I blogged about it.  However, this could potentially be bad because it can tell an attacker about the applications you use, and the mechanisms you use to authenticate into them.

This is definitely something you should consider when developing your threat models.

Luckily, if you do decide that you don't want the applications to be visible, you can make a quick modification to the IdpInitiatedSignOn.aspx.cs page.

There is a method called SetRpListState:

protected void SetRpListState( object sender, EventArgs e )
{
    RelyingPartyDropDownList.Enabled = OtherRpRadioButton.Checked;
    ConsentDropDownList.Enabled = OtherRpRadioButton.Checked;
}

To get things working I made two quick modifications.  First I added the following line of code to that method:

OtherRpPanel.Visible = this.IsAuthenticated;

Then I added a line to the Page_Init method:

SetRpListState(null, null);

Now unauthenticated users just see this:

image

And authenticated users see everything as expected:

image

You could extend this further and add some logic to look into the App Settings in the web.config to quickly and easily switch between modes.

Windows Domain Authentication on Windows Phone 7

One of the projects that’s been kicking around in the back of my head is how to make Windows Phone 7 applications able to authenticate against a Windows domain.  This is a must have for enterprise developers if they want to use the new platform.

There were a couple ways I could do this, but keeping with my Claims-shtick I figured I would use an STS.  Given that ADFS is designed specifically for Active Directory authentication, I figured it would work nicely.  It should work like this:

image

Nothing too spectacularly interesting about the process.  In order to use ADFS though, I need the correct endpoint.  In this case I’m using

https://[external.exampledomain.com]/adfs/services/Trust/13/usernamemixed

That takes care of half of the problem.  Now I actually need to make my application call that web service endpoint. 

This is kind of a pain because WP7/Silverlight don’t support the underlying protocol, WS-Federation.

Theoretically I could just add that endpoint as a service reference and build up all the pieces, but that is a nightmare scenario because of all the boiler-plating around security.  It would be nice if there was a library that supported WS-Federation for the phone.

As it turns out Dominick Baier came across a solution.  He converted the project that came from the Identity training kit initially designed for Silverlight.  As he mentions there were a few gotchas, but overall it worked nicely.  You can download his source code and play around.

I decided to take it a step further though.  I didn’t really like the basic flow of token requests, and I didn’t like how I couldn’t work with IPrincipal/IIdentity objects.

First things first though.  I wanted to start from scratch, so I opened the identity training kit and looked for the Silverlight project.  You can find it here: [wherever you installed the kit]\IdentityTrainingKitVS2010\Labs\SilverlightAndIdentity\Source\Assets\SL.IdentityModel.

Initially I thought I could just add it to a phone project, but that was a bad idea; there were too many build errors.  I could convert the project file to a phone library, but frankly I was lazy, so I just created a new phone library and copied the source files between projects.

There were a couple references missing, so I added System.Runtime.Serialization, System.ServiceModel, and System.Xml.Linq.

This got the project built, but will it work?

I copied Dominick’s code:

WSTrustClient _client;

private void button1_Click(object sender, RoutedEventArgs e)
{
    _client = GetWSTrustClient(
https://[...]/adfs/services/Trust/13/usernamemixed,
new UsernameCredentials("username", "password")); var rst = new RequestSecurityToken(WSTrust13Constants.KeyTypes.Bearer) { AppliesTo = new EndpointAddress("[…]") }; _client.IssueCompleted += client_IssueCompleted; _client.IssueAsync(rst); } void client_IssueCompleted(object sender, IssueCompletedEventArgs e) { _client.IssueCompleted -= client_IssueCompleted; if (e.Error != null) throw e.Error; var token = e.Result; button2.IsEnabled = true; } private WSTrustClient
GetWSTrustClient(string stsEndpoint, IRequestCredentials credentials) { var client = new WSTrustClient(new WSTrustBindingUsernameMixed(),
new EndpointAddress(stsEndpoint), credentials); return client; }

To my surprise it worked.  Sweet.

This left me wanting more though.  In order to access any of the claims within the token I had to do something with the RequestSecurityTokenResponse (RSTR) object.  Also, how do I make this identity stick around within the application?

The next thing I decided to do was figure out how to convert the RSTR object to an IClaimsIdentity.  Unfortunately this requires a bit of XML parsing.  Talk about a pain.  Helper class it is:

public static class TokenHandler
{
    private static XNamespace ASSERTION_NAMESPACE 
= "urn:oasis:names:tc:SAML:1.0:assertion"; private const string CLAIM_VALUE_TYPE
= "http://www.w3.org/2001/XMLSchema#string"; // bit of a hack public static IClaimsPrincipal Convert(RequestSecurityTokenResponse rstr) { return new ClaimsPrincipal(GetClaimsIdentity(rstr)); } private static ClaimsIdentity GetClaimsIdentity(RequestSecurityTokenResponse rstr) { XDocument responseDoc = XDocument.Parse(rstr.RequestedSecurityToken.RawToken); XElement attStatement = responseDoc.Element(ASSERTION_NAMESPACE + "Assertion")
.Element(ASSERTION_NAMESPACE + "AttributeStatement"); var issuer = responseDoc.Root.Attribute("Issuer").Value; ClaimCollection claims = new ClaimCollection(); foreach (var c in attStatement.Elements(ASSERTION_NAMESPACE + "Attribute")) { string attrName = c.Attribute("AttributeName").Value; string attrNamespace = c.Attribute("AttributeNamespace").Value; string claimType = attrNamespace + "/" + attrName; foreach (var val in c.Elements(ASSERTION_NAMESPACE + "AttributeValue")) { claims.Add(new Claim(issuer, issuer, claimType,
val.Value, CLAIM_VALUE_TYPE)); } } return new ClaimsIdentity(claims); } }

Most of this is just breaking apart the SAML-goo.  Once I got all the SAML assertions I generated a claim for each one and created a ClaimsIdentity object.  This gets me a step closer to how I wanted things, but keeping the identity around within the application is still up in the air.  How can I keep the identity for the lifetime of the application?  I wanted something like Thread.CurrentPrincipal but the phone platform doesn’t let you access it.

There was a class, TokenCache, that was part of the original Silverlight project.  This sounded useful.  it turns out it’s Get/Add wrapper for a Dictionary<>.  It’s almost useful, but I want to be able to access this cache at any time.  A singleton sort of solves the problem, so lets try that.  I added this within the TokenCache class:

public static TokenCache Cache
{
    get
    {
        if (_cache != null)
            return _cache;

        lock (_sync)
        {
            _cache = new TokenCache();
        }

        return _cache;
    }
}

private static TokenCache _cache;
private static object _sync = new object();

 

now I can theoretically get access to the tokens at any time, but I want to make the access part of the base Application object.  I created a static class called ApplicationExtensions:

public static class ApplicationExtensions
{
    public static IClaimsPrincipal 
GetPrincipal(this Application app, string appliesTo) { if (!TokenCache.Cache.HasTokenInCache(appliesTo)) throw new ArgumentException("Token cannot be found to generate principal."); return TokenHandler.Convert(TokenCache.Cache.GetTokenFromCache(appliesTo)); } public static RequestSecurityTokenResponse
GetPrincipalToken(this Application app, string appliesTo) { return TokenCache.Cache.GetTokenFromCache(appliesTo); } public static void
SetPrincipal(this Application app, RequestSecurityTokenResponse rstr) { TokenCache.Cache.AddTokenToCache(rstr.AppliesTo.ToString(), rstr); } }

 

It adds three extension methods to the base Application object.  Now it’s sort of like Thread.CurrentPrincipal.

How does this work?  When the RSTR is returned I can call:

Application.Current.SetPrincipal(rstr);
 

Accessing the identity is two-part.

If I just want to get the identity and it’s claims I can call:

var principal = Application.Current.GetPrincipal("https://troymcclure/webapplication3/");

IClaimsIdentity ident = principal.Identity as IClaimsIdentity;
 

If I want to reuse the token as part of web service call I can get the token via:

var token = Application.Current.GetPrincipalToken(https://troymcclure/webapplication3/);
 

There is still quite a lot to do in order for this to be production ready code, but it does a pretty good job of solving all the problems I had with domain authentication on the Windows Phone 7 platform.

Installing new ADFS SQL Farm from Command Line

Need to do a basic install from the command line?  Here’s how:

cd “C:\Program Files\Active Directory Federation Services 2.0”

FSConfig.exe CreateSQLFarm /ServiceAccount "domain\adfssvc" /ServiceAccountPassword "SecretPassword" /SQLConnectionString "database=AdfsSvcConfig;server=sqlserver;integrated security=SSPI" /AutoCertRolloverEnabled /CleanConfig /FederationServiceName "login.mydomain.com"

There are a couple flags that might look a little odd:

  • AutoCertRolloverEnabled – This means ADFS will manage the certificates it uses.  Your other option is to specify which certificates it will use.
  • CleanConfig – In case there is already an old configuration database.  This will delete it.
  • FederationServiceName “login.mydomain.com” – The URI of your ADFS farm.

Tweaking the Default ADFS v2 Web Experience

Designing clean and smart user experiences is a hobby of mine.  Yeah, it’s a little weird, but it’s a good way to change gears for a while when working through a problem.

Given that, I thought I would take a stab at the login pages for ADFS.  I didn’t do anything major, but lets take a look at some before and after shots.

Before

Nothing too complicated here.  It looks like a regular login page.

After

image

In comparison, not a lot has changed.  I switched the font, moved the textbox labels, fiddled with some border colors, and added a watermark in the username textbox describing the format.

Here is the updated style:

<style>
body
{
    background-color: rgb(255, 255, 255);
    color: #4a4a4a;
    font-family: "Segoe UI" , Arial, Helvetica, Sans-Serif;
    line-height: 1.4em;
    font-size: 12px;
    font-style: normal;
    font-variant: normal;
    font-weight: normal;
    font-size-adjust: none;
    font-stretch: normal;
    margin-top: 20px;
    margin-right: 20px;
    margin-bottom: 20px;
    margin-left: 20px;
    background-repeat: repeat-x;
    background-image: url(../App_Themes/Default/header_background.png);
}
</style>

The next step I’d like to take is add some JavaScript validation.  The default codebase just passes the text to the authentication code, which would eventually throw an exception, inevitably resulting in a little red blob of text saying “the user name or password is incorrect.”  Seems like a waste of a postback if all it’s going to do is remind me that some textbox is empty.

Redirecting to SAML Relying Party using ADFS v2 Query String

A quickie, but a goodie. 

In an earlier post on setting Salesforce.com as a SAML Relying Party to ADFS, I talked about how I felt a little dumb because I couldn’t figure out how to get ADFS to post the token to Salesforce.  The reason I felt that way was because with WS-Federation there is a URL parameter that is designed to tell the STS which relying party requested the token.  Notsomuch with SAML.

Turns out with ADFS there is such a parameter.  By default if you pass in ?loginToRp=[rpIdentifier] to the IdpInitiatedSignOn.aspx page, ADFS will look for a relying party based on the parameter.

If you are unsure of what identifier to use, you can go to the relying party properties, and check out the Identifiers tab.  It will accept any of the identifiers in the list:

image

As an aside, if you don’t like that URL parameter name, you can go into the IdpInitiatedSignOn.aspx.cs file and update line 21 to whichever you feel is appropriate:

const string RpIdentityQueryParameter = "loginToRp";

Then you compile the site, and redeploy.

You are properly securing ADFS by compiling the site’s source code, right? Smile