SharePoint hosted Apps with SAML authentication

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.

ADFS Login Failure on one SharePoint site collection

After migrating a Project Server (or any other SharePoint web app) to a new SharePoint 2010 farm using ADFS for federated authentication and multi-tenancy you receive the below error message when trying to access some of the migrated site collections. In my case the root site worked but /PWA failed.

Also if you reprovision a new site collection (include a PWA) in the same web application it also works fine.

 

Error message from ADFS

clip_image001

There was a problem accessing the site. Try to browse to the site again.

If the problem persists, contact the administrator of this site and provide the reference number to identify the problem.

Reference number: [GUID]

 

Event log on ADFS server

Log Name: AD FS 2.0/Admin
Source: AD FS 2.0
Date: 5/11/2012 10:00:43 PM
Event ID: 364
Task Category: None
Level: Error
Keywords: AD FS

Description:

Encountered error during federation passive request.
Additional Data

Exception details:
Microsoft.IdentityServer.Web.InvalidRequestException: MSIS7042: The same client browser session has made ‘6’ requests in the last ‘5’ seconds. Contact your administrator for details.

at Microsoft.IdentityServer.Web.FederationPassiveAuthentication. UpdateLoopDetectionCookie()

at Microsoft.IdentityServer.Web.FederationPassiveAuthentication. SendSignInResponse(MSISSignInResponse response)

 

Cause

What’s happening is that while authentication works as expected on some sites, when you open a particular site collection the authentication goes into a loop, eventually failing when the ADFS server detects this redirect loop.

After a fair amount of digging the problem turned out to be in the multi-tenancy configuration of this particular farm. Specifically the site collections for some reason did not all have a subscription.

Using the PowerShell command ‘Get-SPSite https://site/sitecollection1’ against two site collections to compare the settings turned up the key difference:

image

 

This makes sense as to why the authentication is failing; the different site subscription overrides the FedAuth authentication cookie assigned by ADFS as soon as it is assigned, causing the page to redirect back to re-authenticate.

 

Resolution

Fortunately once the above was found the fix is simple, run the following command in PowerShell:

Get-SPSite | Set-SPSite 
–SiteSubscription [Guide of site subscription]

 

Now after a quick IISRESET all should be working!

Excel Services in a Multi-tenanted Environment

Following on from my previous post about Project Server and PerformancePoint in a multi-tenanted environment, realised that I had not told the whole story!

I neglected to include this when I wrote the above, Excel Services is another critical part of Project Server (more so than PerformancePoint really), and although it does have some support for Multi-tenancy it is far from perfect.

In short, when you configure Excel Services (no partitioning options are available) it does appear to honour subscriptions in the configuration, ie you can use a tenanted Secure Store to create Excel Services Application IDs, however the catch is that it will only talk to the Secure Store in the Default proxy group!

This presents a problem, as you read above PerformancePoint also only talks to the Default Secure Store, however unlike Excel Services PerformancePoint can not talk to a tenanted Secure Store app in the default group so you are left with a catch 22. Either both Excel Services and PerformancePoint can work together using a NON-tenanted default secure store, or one or the other cannot be installed!

*sigh*

If this stuff were simple we wouldn’t be here would we?

Multi-tenancy and Project Server and PerformancePoint 2010

Update 21/01/2011: Update below regarding Excel Services and Multi-tenancy!

One of the exciting new features of SharePoint 2010 is multi-tenancy, if you’re working in hosted or shared environments then it is no less than a must-have feature.

Unfortunately in the real world of software nothing is perfect at RTM, and this would have to be one of those cases!

In short neither Project Server or PerformancePoint appear to support multi-tenancy in SharePoint 2010, it would seem that the feature has yet to be fully implemented for either service application, however the situation is not as bad as you might fear.

 

Project Server in a multi-tenanted environment

Firstly although Project Server does not accept any subscription related parameters when provisioning a new instance using PowerShell or the Central Admin, it does appear to work in a tenanted environment. Basically the fact that any provisioned PWA site is a site collection of its own means that once you have provisioned your PWA instance you can use the following PowerShell command to associate your PWA site collection with your tenant subscription:

Set-SpSite $PWASiteUrl -SiteSubscription $subscription

More good news is that once provisioned a PWA instance is able to communicate with other service applications belonging to the same subscription. Most importantly: Secure Store Service. Without that Excel Services wouldn’t work!

All is not good though, especially if you like to use the full feature set of the 2010 product, read on..

 

PerformancePoint in a multi-tenanted environment

This is where the news gets bad, it would appear that PerformancePoint in 2010 does not support multi-tenancy at all, it actually doesn’t appear to respect tenant subscriptions and so as a result you might end up with errors like the following when attempting to run Dashboard Designer or configuring the service application unattended account;

w3wp.exe (0x04E4)        0x0ED8        Secure Store Service        Secure Store        7557        Critical        The Secure Store Service application Secure Store Service Proxy is not accessible. The full exception text is: Access is denied.        adfdd2a3-b6e5-4d92-8c5e-5a44fd821969

w3wp.exe (0x04E4)        0x0ED8        Secure Store Service        Secure Store        d9ld        Unexpected        Unexpected exception from endpoint address

w3wp.exe (0x04E4)        0x0ED8        Secure Store Service        Secure Store        d9le        Unexpected        Logging unknown/unexpected client side exception: SecurityAccessDeniedException. This will cause this application server to be removed from the load balancer queue. Exception: System.ServiceModel.Security.SecurityAccessDeniedException: Access is denied

Some searching will lead you to a number of sources talking about the lack of a Master Key being created in the Secure Store which is most certainly the cause of the issue, unfortunately it appears that in the case where your Secure Store is created with tenancy enabled PerformancePoint is unable to see the Secure Store service application (and thus unable to retrieve the master key)!

Some deep reading led me to some TechNet articles detailing the requirements for PerformancePoint (sorry can’t find the link ATM), basically PPS requires that a Secure Store Service Application exists in the default proxy group, and as I’ve found that app also cannot be tenanted.

Fortunately this can also be worked around, maintaining a non-tenanted default Secure Store app dedicated to PPS does not as I see it introduce any security implications, in particular as you will have to provision a separate PPS service for each tenant in your farm (thus losing much of the benefits of multi-tenancy).

 

All I can say on this one is that I can’t wait for SP1 for PPS, maybe then we’ll even be able to name our databases and lose that BETA PowerShell command line syntax? :)

 

Update 21/01/2011:

See the following blog for information on how Excel Services impacts the above configurations!