One of the noticeable gaps that comes up immediately when you start planning any significant SharePoint 2013 deployment with requirements such as multi-tenancy and SAML based authentication (ADFS, ACS, etc) are the some of the limitations with the new features of 2013.

One such limitation is the new App Store which only supported Windows Authentication and didn’t support hostheaders at RTM! Fortunately Microsoft fixed the hostheader limitation in the March PU release (KB2768001), however the SAML limitation remains.

Steve Peschka wrote about one solution to part of this problem here: Using SharePoint Apps with SAML and FBA Sites in SharePoint 2013, however that only covers the Provider hosted (or Autohosted) apps, which leaves a big gaping hole where the simplest Apps of all are not supported.

 

The Problem with SharePoint hosted Apps

Basically the problem is in the the App Domain configuration and authentication requirements, take a typical example:

  1. First tenant:
    App Domain: https://tenant1-*******.contosoapps.com
  2. Second tenant:
    App Domain: https://tenant2-*******.contosoapps.com

As you know those App Domains are auto created for each installed app with a unique name per app. (Note that you need March PU to configure the above App Domains)

So with that in mind, when a user is authenticated to tenant1.contoso.com that does not automatically authenticate them to tenant1-******.contosoapps.com forcing re-authentication, that’s where we hit our problem.

 

Azure ACS and ADFS – No Apps??

Unfortunately Azure ACS and ADFS don’t work at all in this scenario. The problem:

Capture1

UseWReplyParameter while supported by both is restricted to sub sites only (e.g. reply to , as a result in our scenario above what you end up with is this annoying authentication loop:

image

 

The reason for this is for security, as allowing any arbitrary WReply parameter could potentially allow an attacker from one Relaying Party (RP) to be redirected with a valid authentication token to a second RP.

So in my scenario above it may be possible for tenant1 users to authenticate not only with tenant1*** apps, but also tenant2 sites!

 

Solutions?

Unless Microsoft changes ACS and ADFS (unlikely) or they modify the SPTrustedIdentityTokenIssuer (maybe?), then the only option right now is to roll your own STS Identity Provider based on the the Windows Identity Framework SDK!

Fortunately you’re not alone, plenty of examples of this exist;

And my favourite:

Lots of good examples to work with but for this blog I’m going to extend the last one on the list by Steve Peschka using SAML with Microsoft Accounts (LiveID). So if you want to implement my changes below, you’ll need to start by reading the last article above and download and get familiar with the source for that one.

 

Changes to WindowsLiveOauthSts Solution

In summary we need to make the following changes to the solution:

  1. Firstly we need to update the Custom STS to accept and use the WReplyParameter.
  2. Secondly we need to capture the original request WReplyParameter in the PassiveSTS.cs and send it to the Custom STS.
  3. Finally we need to ensure that we maintain the security of our solution in with all changes.

That’s it, simple hey?

 

Updates to CustomSecurityTokenService.cs

First add somewhere to store the parameter to the class:

    protected String WReplyParameter { get; set; }

Next extend the constructor to accept the parameter:

    public CustomSecurityTokenService(CustomSecurityTokenServiceConfiguration configuration,
        Dictionary<string, string> ClaimValues, String wReplyParameter = "")
        : base(configuration)
    {
        this.oAuthValues = ClaimValues;
        this.WReplyParameter = wReplyParameter;
    }

And finally use the saved value in the GetScope() method:

        // Set the ReplyTo address for the WS-Federation passive protocol (wreply).

        // Use the provided WReplyParameter if it exists
        if (String.IsNullOrEmpty(WReplyParameter))
            scope.ReplyToAddress = scope.AppliesToAddress;
        else
            scope.ReplyToAddress = WReplyParameter;

        return scope;

Updates to PassiveSTS.cs

First we have to get the Query String parameter from the request:

    string wReplyParameter = HttpUtility.ParseQueryString(HttpUtility.UrlDecode(state))["wreply"];

Then we make sure to pass the parameter to the CustomSTS when instantiated:

    //create an instance of our sts and pass in the dictionary of values we got from Windows Live oAuth
    SecurityTokenService sts =
        new CustomSecurityTokenService(CustomSecurityTokenServiceConfiguration.Current, values,
            wReplyParameter);

 

Security Considerations

The risk arises if we allow any ‘wreply’ parameter to be be used as our returned ReplyToAddress in the Custom STS, in this example implementation a simple validation is included using an array of allowed URL’s (unmodified code quoted here);

    // TODO: Set enableAppliesToValidation to true to enable only the RP Url's specified in the
    // PassiveRedirectBasedClaimsAwareWebApps array to get a token from this STS
    static bool enableAppliesToValidation = false;

Obviously for a production multi-tenanted environment you would need something more sophisticated, but I’ll leave that to you. Also another thing that particular solution does not allow for is our dynamically created App Domains, so that too will require some changes.

 

But hang on..

However it is worth saying here that in our simple example when using only Live ID across all Web Apps we have not exposed anything extra (yet) with this change! Think about it; if an attacker in wants to login to Tenant2 then all they need to do is open and login! Our STS is passing only identity and user related claims so all of the securing of resources is left to the tenant. IE; John from Tenant 1 has no site collection SPUser rights to Tenant 2 sites! So as it stands this scenario is similar to an equivalent Azure ACS implementation using multiple Relaying Parties.

 

 

Final Words

It’s worth mentioning that this likely will become redundant after a future service pack, assuming Microsoft fixes this by changing SharePoint that is.

A good indication is to look at how Office365’s doing it right now, and a quick Fiddler trace shows that the two cookies (rtFa and FedAuth) are passed directly to the AppDomain from the tenant Web App, so clearly MS is handling this in the initial Auth.

That may be a topic for investigation another time.

 

Hope this is useful for someone out there.

Share and Enjoy !

Shares