Don't miss out Virtual Happy Hour this Friday (April 26).

Try our conversational search powered by Generative AI!

Loading...
Area: Optimizely CMS
ARCHIVED This content is retired and no longer maintained. See the latest version here.

Recommended reading 

Federated security

Federated security lets you separate the service a client is accessing from the associated authentication and authorization procedures; for example, to enable collaboration across multiple systems, networks, and organizations. This topic describes how to configure federated security where Episerver acts as a claims aware application.

This topic shows how to configure Episerver and does not cover basics in federated security or how to configure ADFS.

Note: Episerver Mail and Episerver Relate (using Common Framework), do not support federated security.

How it works

Federated security  includes features such as Single-Sign-On (SSO) that allows a single user authentication process across multiple IT systems or even organizations. The protocol used is WS-Federation which is a specification supported by a wide range of federation software, such as Active Directory Federation Services (ADFS). Other federation specifications may require third-party software. In documentation the Relying party (RP) refers to the website running Episerver. The identity server/identity provider/federation server/Security Token Service(STS) is a third-party software, such as ADFS.

In short, when authentication is required (Episerver sends HTTP status code 401), a redirect sends the user to an identity provider that after authentication sends back claims about the user. You can use role claims to assign access rights to content or enable access to edit mode. There is no user interface to manage roles and users synchronized from the identity provider; everything that is synchronized is visible for setting access rights by an editor.

To support assigning access rights to role claims or sending notifications to a user that is not logged in, users and roles are synchronized to the database. The synchronization of users and roles is triggered when an identity provider sends back claims about a user to Episerver.

Windows Identity Foundation and OWIN

Federated security was originally implemented in Windows Identity Foundation (WIF), but is fully integrated into the core framework (as of .NET 4.5). Since the release of .NET 4.5, Microsoft has developed a new module called Microsoft.Owin.Security.WSFederation that simplifies configuration but builds on the same proven platform support. This module requires the OWIN framework, which contains abstractions to simplify building .NET middleware that need to run on multiple hosts. See OWIN and Episerver.

Limited multi-site support

The OWIN provider for WS Federation does not support multi-tenancy so each site must run in it's own web application for authentication to work on all URL's (the WtRealm configuration specified in the example below cannot vary per request). The OWIN provider for OpenID connect can work with multiple URL's, see integration with Azure Active Directory.

Configuring Episerver federated security

Prerequisites

  • The site must be able to access the identity server metadata URL.
  • The site must be registered as a Relying Party Trust in the identity server. See the federation provider documentation for details.
  • The identity provider must at least send a Name claim for each user but should also send Role claims to enable setting access righs in Episerver.

1. Disable Role and Membership Providers

Disable the built-in Role and Membership providers  in web.config because they do not support federated security:

<authentication mode="None" />
    <membership>
      <providers>
        <clear/>
      </providers>
    </membership>
    <roleManager enabled="false">
      <providers>
        <clear/>
      </providers>
    </roleManager>

 

Note: roleManager is disabled to prevent administrators from adding users to group membership in the admin view, because these users cannot log in with federated security in place.

2. Configure Episerver to support federation

Enable claims on virtual roles by setting the addClaims property. Also add the provider for security entities, which is used by the set access rights dialog and impersonating users. The SynchronizingRolesSecurityEntityProvider configured in the following example is using the SynchronizingUserService that is used in step 4.

 <episerver.framework>
    <securityEntity>
      <providers>
        <add name="SynchronizingProvider" type ="EPiServer.Security.SynchronizingRolesSecurityEntityProvider, EPiServer"/>
      </providers>
    </securityEntity>
<virtualRoles addClaims="true">
//existing virtual roles
    </virtualRoles>

3. Install NuGet packages

Open Package Manager in Visual Studio and install the following packages:

Install-Package Microsoft.Owin.Security.Cookies
Install-Package Microsoft.Owin.Security.WsFederation
Install-Package Microsoft.Owin.Host.SystemWeb
Update-Package Microsoft.IdentityModel.Protocol.Extensions -Safe

 

Note: Always use Microsoft.IdentityModel.Protocol.Extensions package version 1.0.2 or later, previous versions contain a critical bug that might cause threads to hang.

4. Configure WSFederation

The WSFederation package is configured from code rather from configuration files, which is a practice used for Owin based middleware. See ASP.NET documentation for details about having different configuration in production and development.

Add the following code to the template project. Replace the MetadataAdress with the URL to the federation server and the Wtrealm must match exacly what is registrered in the federation server. See inline comments for more details.

using EPiServer.Framework;
using EPiServer.Security;
using EPiServer.ServiceLocation;
using Microsoft.Owin;
using Microsoft.Owin.Extensions;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.WsFederation;
using Owin;
using System;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web.Helpers;
using WsFederationSite;

[assembly: OwinStartup(typeof(Startup))]

namespace WsFederationSite
{
    public class Startup
    {
        const string LogoutUrl = "/util/logout.aspx";

        public void Configuration(IAppBuilder app)
        {
            //Enable cookie authentication, used to store the claims between requests
            app.SetDefaultSignInAsAuthenticationType(WsFederationAuthenticationDefaults.AuthenticationType);
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType
            });
            //Enable federated authentication
            app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions()
            {
                //Trusted URL to federation server meta data
                MetadataAddress = "https://devdc.dev.test/federationmetadata/2007-06/federationmetadata.xml",
                //Value of Wtreal must *exactly* match what is configured in the federation server
                Wtrealm = "https://localhost:44303/",
                Notifications = new WsFederationAuthenticationNotifications()
                {
                    RedirectToIdentityProvider = (ctx) =>
                        {
                            //To avoid a redirect loop to the federation server send 403 when user is authenticated but does not have access
                            if (ctx.OwinContext.Response.StatusCode == 401 && ctx.OwinContext.Authentication.User.Identity.IsAuthenticated)
                            {
                                ctx.OwinContext.Response.StatusCode = 403;
                                ctx.HandleResponse();
                            }
                            return Task.FromResult(0);
                        },
                    SecurityTokenValidated = (ctx) =>
                        {
                            //Ignore scheme/host name in redirect Uri to make sure a redirect to HTTPS does not redirect back to HTTP
                            var redirectUri = new Uri(ctx.AuthenticationTicket.Properties.RedirectUri, UriKind.RelativeOrAbsolute);
                            if (redirectUri.IsAbsoluteUri)
                            {
                                ctx.AuthenticationTicket.Properties.RedirectUri = redirectUri.PathAndQuery;
                            }
                            //Sync user and the roles to EPiServer in the background
                            ServiceLocator.Current.GetInstance<ISynchronizingUserService>().SynchronizeAsync(ctx.AuthenticationTicket.Identity);
                            return Task.FromResult(0);
                        }
                }
            });
            //Add stage marker to make sure WsFederation runs on Authenticate (before URL Authorization and virtual roles)
            app.UseStageMarker(PipelineStage.Authenticate);

            //Remap logout to a federated logout
            app.Map(LogoutUrl, map =>
            {
                map.Run(ctx =>
                {
                    ctx.Authentication.SignOut();
                    return Task.FromResult(0);
                });
            });

            //Tell antiforgery to use the name claim
            AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Name;
        }
    }
}

Troubleshooting

Generic error when redirected to identity provider

Identity providers, such as ADFS, will not display helpful error information on the login screen because that could compromise security. Check the event log of the identity provider to get details about the cause of an error.

The most common cause is that WtRealm in the code above does not exactly match the value entered as "Relying party identifiers" in the identity provider. Be aware that the value of the WtRealm is just an identifier and does not have to be the URL to the site. The URL that the identity provider posts back the claims to is in most cases another setting defined in the identity provider.

SSL related issues - The remote certificate is invalid according to the validation procedure

Both servers should be using SSL to the protect the communication for dev/test purposes. You can use self-signed certificates on both the web server and identity server. The web server must trust the certificate of the SSL connection to the identity server to access the metadata document defined in the code, when using self-signed certificates you can add the root certificate to the web server.

Value cannot be null. Parameter name: userName

You may not configure the identity provider to send a Name-claim. See the identity provider documentation to resolve this.

Login and logout from the Alloy templates

Alloy templates use Forms Authentication in the Login/Logout link at the bottom the layout, to provide federated authentication change the Login link to send a 401 Access Denied, which triggers the WS Federation middleware. For Logout, use either /Util/Logout.aspx, which is remapped in the previous example, or call the same Signout method in the templates.

401.2 Access Denied when accessing edit mode instead of redirect to identity provider

If the project previously did not have an Owin startup class and optimizeCompilations was enabled, then sometimes the new code is not executed, the result is that you get a 401.2 Access Denied message instead of the redirect to the identity provider. Temporarily set optimizeCompilations in web.config to false to clear the cache.

401.2 Access Denied or 403 Forbidden when accessing edit mode after redirect to identity provider

When the identity provider does not send the required role claims, you will not get access to edit mode. You could configure the identity provider to send the CmsAdmins role claim for a specific group of users.

Enable logging for Owin Security

You can view warning messages reported by Owin Security by adding the following configuration to web.config. These warning messages may contain information about errors in the WS-Federation communication protocol.

 <system.diagnostics>
    <switches>
      <add name="Microsoft.Owin.Security" value="Warning" />
    </switches>
    <trace autoflush="true"></trace>
    <sharedListeners>
      <add name="file" type="System.Diagnostics.TextWriterTraceListener" initializeData="App_Data\OwinSecurity.log" />
    </sharedListeners>
    <sources>
      <source name="Microsoft.Owin.Security">
        <listeners>
          <add name="file" />
        </listeners>
      </source>
    </sources>
  </system.diagnostics>

Alloy: The login link redirects to "/Login.aspx"

The Alloy templates assume you are running forms authentication. You can remove the login link if not required, or implement custom login functionality. For example, create a controller or web forms page that sends a 401 Access Denied message for non-authenticated users.

Do you find this information helpful? Please log in to provide feedback.

Last updated: Sep 21, 2015

Recommended reading