Try our conversational search powered by Generative AI!

Nov 18, 2014
(4 votes)

Using Azure Active Directory as identity provider

EPiServer has now support for federated security and there is a good description of how to get started,

During my presentation at the partner forum, I showed how to use Azure Active Directory to manage authentication for EPiServer, here's the summary.



Open the Azure portal and create a new application in the Active Directory that you want to use for authentication, select the ADD APPLICATION MY ORGANIZATION IS DEVELOPING, enter a name and select WEB APPLICATION AND/OR WEB API.

Add application 1 Add application 2

In the last section, enter the url to your site in the SIGN-ON URL field and a unique url to identify your application in the APP ID URI field,  this is the url that you also should specify in the web.config for your EPiServer site , click the Complete button.

Add application 3



Once the application is created, we enter the configuration tab get the metadata url, click on the icon VIEW ENDPOINTS along the bottom to open dialog, highlight the field FEDERATION DOCUMENT METADATA and copy the value, this should also be pasted into the web.config.

Azure Endpoints

Client ID and Secret Key

Before we close the Azure portal, we need to have another couple of settings from the configuration to get it work.

Copy the value from the field CLIENT ID and paste it to your web.config, we must also create a secret key that is used with the client ID when requesting the Azure's Graph API. Choose if your secret key will be valid for a year or two, and then click save, when the application is saved, you can copy the value of the secret key and paste it to the web.config.

Azure Secret Key

The last thing we need to do before we jump over to EPiServer is to set permissions so that the application can request and read the information via the Graph API, select Windows Azure Active Directory application and at least read access for the Application Permissions as well as Read directory data and Enable sign-on and Read users profiles for the Delegated Permissions, click save.

Azure Permissions


Update web.config

When all settings from Azure has been copied to your web.config, the following appSettings should be included:

   1: <add key="MetadataAddress" value="" />
   2: <add key="Wtrealm" value="" />
   3: <add key="TenantName" value="" />
   4: <add key="ClientId" value="4181422f-7c7d-452a-a84c-91b03e91a062" />
   5: <add key="ClientSecret" value="sZvD2A8ac5jv4ifD2GfTGLt3uoPrbfr1teh1nQ4wQjU=" />
   6: <add key="GraphUrl" value="" />

Create user groups

Select the GROUPS tab for your directory and click on ADD A GROUP, enter a name and click save, I choose to create a group named WebAdmins since this group has access rights to both EPiServers edit and admin interface as default.

Azure Group

Once the group is created, go in and add the users who will be members.



The federated security require OWIN middleware to run, install the following nuget packages.

Install-Package Microsoft.Owin.Security.Cookies
Install-Package Microsoft.Owin.Security.WsFederation
Install-Package Microsoft.Owin.Host.SystemWeb
Install-Package Microsoft.Azure.ActiveDirectory.GraphClient
Install-Package Microsoft.IdentityModel.Clients.ActiveDirectory

Retrieve  groups for the authenticated user

One problem with Azure Active Directory is that the groups that user is a member of is not sent with the Claims, we need the groups to be able to set access rights in EPiServer.

Before EPiServer synchronizes the roles of the authenticated user, they must retrieved through a request to the graph API. I created a AzureGraphService that handles the request to Azure and retrieves roles/grupes for the authenticated user and saves it as Claims.

   1: using System;
   2: using System.Configuration;
   3: using System.Linq;
   4: using System.Security.Claims;
   5: using System.Threading.Tasks;
   6: using EPiServer.ServiceLocation;
   7: using Microsoft.Azure.ActiveDirectory.GraphClient;
   8: using Microsoft.IdentityModel.Clients.ActiveDirectory;
  10: namespace Federated_Security.Business.Security
  11: {
  12:     [ServiceConfiguration(typeof(AzureGraphService))]
  13:     public class AzureGraphService
  14:     {
  15:         public async Task CreateRoleClaimsAsync(ClaimsIdentity identity)
  16:         {
  17:             // Get the Windows Azure Active Directory tenantId
  18:             var tenantId = identity.FindFirst("").Value;
  20:             // Get the userId
  21:             var currentUserObjectId = identity.FindFirst("").Value;
  23:             var servicePointUri = new Uri(ConfigurationManager.AppSettings["GraphUrl"]);
  24:             var serviceRoot = new Uri(servicePointUri, tenantId);
  25:             var activeDirectoryClient = new ActiveDirectoryClient(serviceRoot,
  26:                 async () => await AcquireTokenAsyncForApplication());
  28:             var userResult = await activeDirectoryClient.Users
  29:                 .Where(u => u.ObjectId == currentUserObjectId).ExecuteAsync();
  30:             var currentUser = userResult.CurrentPage.FirstOrDefault() as IUserFetcher;
  32:             var pagedCollection = await currentUser.MemberOf.OfType<Group>().ExecuteAsync();
  33:             do
  34:             {
  35:                 var groups = pagedCollection.CurrentPage.ToList();
  36:                 foreach (Group role in groups)
  37:                 {
  38:                     ((ClaimsIdentity)identity).AddClaim(new Claim(ClaimTypes.Role, role.displayName, ClaimValueTypes.String, "AzureGraphService"));
  40:                 }
  41:                 pagedCollection = pagedCollection.GetNextPageAsync().Result;
  42:             } while (pagedCollection != null && pagedCollection.MorePagesAvailable);
  43:         }
  45:         public async Task<string> AcquireTokenAsyncForApplication()
  46:         {
  47:             var authenticationContext = new AuthenticationContext(string.Format("{0}", 
  48:                 ConfigurationManager.AppSettings["TenantName"]), false);
  50:             // Config for OAuth client credentials 
  51:             var clientCred = new ClientCredential(ConfigurationManager.AppSettings["ClientId"], ConfigurationManager.AppSettings["ClientSecret"]);
  52:             var authenticationResult = authenticationContext.AcquireToken(ConfigurationManager.AppSettings["GraphUrl"], clientCred);
  53:             return authenticationResult.AccessToken;
  54:         }
  55:     }
  56: }

Owin configuration

Add a owin startup class and make sure you call the AzureGraphService before EPiServer is synchronizing the roles.

   1: using System;
   2: using System.Security.Claims;
   3: using System.Threading.Tasks;
   4: using System.Web.Helpers;
   5: using System.Configuration;
   6: using Federated_Security.Business.Security;
   7: using Owin;
   8: using Microsoft.Owin;
   9: using Microsoft.Owin.Extensions;
  10: using Microsoft.Owin.Security;
  11: using Microsoft.Owin.Security.Cookies;
  12: using Microsoft.Owin.Security.WsFederation;
  13: using EPiServer.Security;
  14: using EPiServer.ServiceLocation;
  16: [assembly: OwinStartup(typeof(Federated_Security.Startup))]
  18: namespace Federated_Security
  19: {
  20:     public class Startup
  21:     {
  22:         const string LogoutUrl = "/util/logout.aspx";
  24:         public void Configuration(IAppBuilder app)
  25:         {
  26:             // Enable cookie authentication, used to store the claims between requests
  27:             app.SetDefaultSignInAsAuthenticationType(WsFederationAuthenticationDefaults.AuthenticationType);
  28:             app.UseCookieAuthentication(new CookieAuthenticationOptions
  29:             {
  30:                 AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType
  31:             });
  33:             // Enable federated authentication
  34:             app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions()
  35:             {
  36:                 // Trusted URL to federation server meta data
  37:                 MetadataAddress = ConfigurationManager.AppSettings["MetadataAddress"],
  39:                 // Value of Wtreal must *exactly* match what is configured in the federation server
  40:                 Wtrealm = ConfigurationManager.AppSettings["Wtrealm"],
  42:                 Notifications = new WsFederationAuthenticationNotifications()
  43:                 {
  44:                     RedirectToIdentityProvider = (ctx) =>
  45:                     {
  46:                         //  To avoid a redirect loop to the federation server send 403 when user is authenticated but does not have access
  47:                         if (ctx.OwinContext.Response.StatusCode == 401 && ctx.OwinContext.Authentication.User.Identity.IsAuthenticated)
  48:                         {
  49:                             ctx.OwinContext.Response.StatusCode = 403;
  50:                             ctx.HandleResponse();
  51:                         }
  52:                         return Task.FromResult(0);
  53:                     },
  54:                     SecurityTokenValidated = async (ctx) =>
  55:                     {
  56:                         // Ignore scheme/host name in redirect Uri to make sure a redirect to HTTPS does not redirect back to HTTP
  57:                         var redirectUri = new Uri(ctx.AuthenticationTicket.Properties.RedirectUri, UriKind.RelativeOrAbsolute);
  58:                         if (redirectUri.IsAbsoluteUri)
  59:                         {
  60:                             ctx.AuthenticationTicket.Properties.RedirectUri = redirectUri.PathAndQuery;
  61:                         }
  63:                         #region Azure
  65:                         // Create claims for roles
  66:                         await ServiceLocator.Current.GetInstance<AzureGraphService>().CreateRoleClaimsAsync(ctx.AuthenticationTicket.Identity);
  68:                         #endregion
  70:                         // Sync user and the roles to EPiServer in the background
  71:                         await ServiceLocator.Current.GetInstance<SynchronizingUserService>().SynchronizeAsync(ctx.AuthenticationTicket.Identity);
  72:                     }
  73:                 }
  74:             });
  76:             // Add stage marker to make sure WsFederation runs on Authenticate (before URL Authorization and virtual roles)
  77:             app.UseStageMarker(PipelineStage.Authenticate);
  79:             // Remap logout to a federated logout
  80:             app.Map(LogoutUrl, map =>
  81:             {
  82:                 map.Run(ctx =>
  83:                 {
  84:                     ctx.Authentication.SignOut();
  85:                     return Task.FromResult(0);
  86:                 });
  87:             });
  89:             // Tell antiforgery to use the name claim
  90:             AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Name;
  91:         }
  92:     }
  93: }


Start up your website and try to access the EPiServer editor interface, now you should be redirected to the Azure login page.

Nov 18, 2014


Christer Pettersson
Christer Pettersson Sep 9, 2015 02:05 PM

How did you retrieve the tenantname? I have seemed to missed this part and now recieve a AdalServiceexception No service namespace named '...'

Olav Aukan
Olav Aukan Oct 21, 2016 05:17 PM

So this is a bit old now, and I guess some things have changed. I've set this up on my dev machine and after successfully authenticating in Azure and being sent back to EPiServer I get this error:

IDX10213: SecurityTokens must be signed. SecurityToken: '{0}'.

[SecurityTokenValidationException: IDX10213: SecurityTokens must be signed. SecurityToken: '{0}'.]
   Microsoft.IdentityModel.Tokens.Saml2SecurityTokenHandler.ValidateToken(String securityToken, TokenValidationParameters validationParameters, SecurityToken& validatedToken) +1882
   Microsoft.IdentityModel.Extensions.SecurityTokenHandlerCollectionExtensions.ValidateToken(SecurityTokenHandlerCollection tokenHandlers, String securityToken, TokenValidationParameters validationParameters, SecurityToken& validatedToken) +228
   Microsoft.Owin.Security.WsFederation.d__1f.MoveNext() +3619
   System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() +31
   Microsoft.Owin.Security.WsFederation.d__1f.MoveNext() +5641
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13891908
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
   Microsoft.Owin.Security.Infrastructure.d__0.MoveNext() +822
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13891908
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
   Microsoft.Owin.Security.Infrastructure.d__0.MoveNext() +333
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13891908
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
   Microsoft.Owin.Security.Infrastructure.d__0.MoveNext() +774
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13891908
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
   Microsoft.Owin.Mapping.d__0.MoveNext() +870
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13891908
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
   Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.d__5.MoveNext() +203
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13891908
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
   Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.d__2.MoveNext() +193
   Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.StageAsyncResult.End(IAsyncResult ar) +96
   System.Web.AsyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +509
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +146

Any idea what's going on here?

Robert L
Robert L Jan 16, 2017 03:29 PM


I got the exact same error after signing in with azure AD. After alot of testing and debugging I finally tested downgrading the Microsoft.IdentityModel.Protocol.Extensions package from version to and it just worked after that.

Please login to comment.
Latest blogs
Optimizely Opal... what it does actually do?

At Opticon 2023, Optimizely announced its first AI product Opal. AI is definitely the new tech buzzword in 2024 and with promises that AI will be...

Jon Jones | Feb 25, 2024 | Syndicated blog

How to add more Content Area Context Menu Item in Optimizely CMS 12

Hey folks, today I will share something related to Context Menu customization in the Content Area of Optimizely CMS. As you know, the content area ...

Binh Nguyen | Feb 25, 2024

Developer meetups in Stockholm & Helsinki

It's time for developer meetups! Next month we will be in Stockholm and Helsinki. Join us for getting the latest updates from Optimizely, be inspir...

Magnus Kjellander | Feb 23, 2024

Roll Your Own Security Headers

Proper security headers are a must for your Optimizely driven website. There are a variety of tools out there that will help with this, but when...

Ethan Schofer | Feb 21, 2024