<1> Create Entra ID in the Azure portal
Following these steps to create Entra ID in the Azure portal
Step 1: Create a new Entra
Note: By default, when using your company/team’s Azure subscription, you’re not allowed to access Entra ID (by opening the left sidebar > Select “Azure Active Directory) So, you must create a new Entra ID by the step below:
- Go to Azure portal homepage (https://portal.azure.com/#home)
- Click on “Create a resource” and search “Azure Active Directory” & select “Azure Acitve Directory” in the search results, then click “Create”
- In “Create a tenant” page
- [Basic tab] Select a tenant type “Microsoft Entra ID”
- [Configuration tab]
- Orginazation name: EX: OptimizelyAS
- Initial domain name: EX: OptimizelyAS
- Country/Region: (optional) keep it as default: United States
- [Review + create] Re-check all information
- Then, click “Create” to create new AAD
Step 2: Create a new user
- Open left sidebar > select “Microsoft Entra ID” > click “Users” menu item under “All User” section in left sidebar > click “New user” > then, fill in user info > click “Create” Note: Password can auto-generated as you have to change it after the first login.
Step 3: Register a web application
Open AAD from the left sidebar > Select “App registrations” > New registration > Fill in the app’s info as follows:
- Name: OptimizelyASapp
- Supported account types: “Accounts in this organizational directory only (OptimizelyAS only – Single tenant)
- Redirect URI (optional): leave it for now (can be changed later)
- Then, click “Register”
Step 4: In “App registrations” Select your App then “Authentication” from the left sidebar
Click “Add a platform” > Select “Web” > Add the following info:
Redirect URIs:
- https://localhost:5000/
- https://localhost:5000/EPiServer/
- https://localhost:5000/signin-oidc
- https://localhost:5000/EPiServer/EPiServer.CMS.UI.Admin/default#/
Note: If you run a CMS site on non-local env, just replace localhost:8000 by your server’s name, for example: https://10.120.18.249:8080/
Front-channel logout URL: https://localhost:5000/util/Logout
Select “ID tokens”
(As I know, the access token no longer has the roles inside it so please choose this only)
Then, click “Save”
Step 5: Select “Certificates & secrets” from the left sidebar.
Go to Client secrets tab > Add “New client secret” > Enter a “Description” > Click “Add”
Note: make sure to copy the Secret ID because it will be used in the config step
Step 6: Select “Token configuration” from the left sidebar.
Add a “groups claim” as below:
Step 7: Select “API permission” from left sidebar
Add permission for AAD. After that, make sure the green tick showing
Step 8: Select “Expose an API” from the left sidebar
- [Application ID URI] with the format: https://{domain}/{registered_app_name}
For example: https://OptimizelyAS.onmicrosoft.com/OptimizelyAS
Tip: hover mouse over the username in top-right corner of the Azure portal, you can see the domain - [Add a scope]
Step 9: Select “Manifest” from left sidebar
In the editor window, replace “appRoles”:[] by following the settings including some default CMS roles (WebAdmins, WebEditors, Administrators)
Note: DO NOT need to change “id” values provided because they are unique in each registered app
"appRoles": [
{
"allowedMemberTypes": [ "User" ],
"description": "Editor can edit the site.",
"displayName": "WebEditors",
"id": "7dacc4ee-fbcb-4ef4-a29e-6272d48ab0be",
"isEnabled": true,
"value": "WebEditors"
},
{
"allowedMemberTypes": [ "User" ],
"description": "Admins can manage roles and perform all task actions.",
"displayName": "WebAdmins",
"id": "c54e99d2-9467-48a9-a9c1-b6fcba74b33a",
"isEnabled": true,
"value": "WebAdmins"
},
{
"allowedMemberTypes": [ "User" ],
"description": "Admin the site.",
"displayName": "Administrators",
"id": "a7b2f640-91c7-4c58-ad29-0cae734d76f5",
"isEnabled": true,
"value": "Administrators"
}
]
Step 10: Select the “Managed application in local directory” link from the Overview in the left panel.
Then, select “Users and groups” from left sidebar > Add user/group
In the Add Assignment page, select
- User: select the user created from step #2 or any available user
- Role: select a role that is defined in the Manifest setting at step #9 (this role should match with their own role as it will be used to authenticate and authorize)
<2> Integrated AAD into CMS site
I set up integrated AAD into a CMS site on my local machine. Follow the link below to install an Alloy Mvc Sample site:
https://docs.developers.optimizely.com/content-management-system/docs/installing-optimizely-net-5
If you deploy the site to another env (non-local) you need to update all URLs correctly.
For example, when deploying site Linux, instead of using https://localhost:5000/, you should use https://10.120.18.247:8080/
Step 1: Update config in appsettings.json file, you need this AAD info (go to App registrations > Select your registered app)
- Application (client) ID
- Directory (tenant) ID
Then, update appsettings.json file like the sample below:
Step 2: Install the OpenIdConnect package
Note: You can add the package directly to “.csproj” file to install it like this
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="6.0.23" />
Step 3: Update Startup.cs file
Update in the ‘ConfigureServices’ method:
//Comment out AddCmsAspNetIdentity adn AddAdminUserRegistration to Authenticate using AAD
services
//.AddCmsAspNetIdentity<ApplicationUser>()
.AddCms()
.AddAlloy()
//.AddAdminUserRegistration()
.AddEmbeddedLocalization<Startup>();
//add below code to define login link
services.ConfigureApplicationCookie(c =>
{
c.LoginPath = "/Login";
c.LogoutPath = "/Logout";
});
Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
// AAD Auth info
var clientId = _configuration["Authentication:AzureClientID"];
var clientSecret = "YOUR CLIENT SECRET";
var callbackPath = "/signin-oidc";
var azureAuthority = _configuration["Authentication:azureAuthority"];
var cookieSchema = "azure-cookie";
var challengeSchema = "azure";
// Authentication Config
services
.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = cookieSchema;
options.DefaultChallengeScheme = challengeSchema;
})
.AddCookie(cookieSchema, options =>
{
options.Events.OnSignedIn = async ctx =>
{
if (ctx.Principal?.Identity is ClaimsIdentity claimsIdentity)
{
// Syncs user and roles so they are available to the CMS
var synchronizingUserService = ctx
.HttpContext
.RequestServices
.GetRequiredService<ISynchronizingUserService>();
await synchronizingUserService.SynchronizeAsync(claimsIdentity);
}
};
})
.AddOpenIdConnect(challengeSchema, options =>
{
options.SignInScheme = cookieSchema;
//options.SignOutScheme = "azure-cookie";
options.ResponseType = OpenIdConnectResponseType.Code;
options.UsePkce = true;
options.ClientId = clientId;
options.Authority = azureAuthority;
options.CallbackPath = callbackPath;
options.ClientSecret = clientSecret;
options.Scope.Clear();
options.Scope.Add(OpenIdConnectScope.OpenIdProfile);
options.Scope.Add(OpenIdConnectScope.OfflineAccess);
options.Scope.Add(OpenIdConnectScope.Email);
options.MapInboundClaims = false;
options.TokenValidationParameters = new TokenValidationParameters
{
// get the role from azure
RoleClaimType = "roles",
NameClaimType = "preferred_username",
ValidateIssuer = false
};
options.Events.OnRedirectToIdentityProvider = ctx =>
{
// Prevent redirect loop
if (ctx.Response.StatusCode == 401)
{
ctx.HandleResponse();
}
return Task.CompletedTask;
};
options.Events.OnAuthenticationFailed = context =>
{
context.HandleResponse();
context.Response.BodyWriter.WriteAsync(Encoding.ASCII.GetBytes(context.Exception.Message));
return Task.CompletedTask;
};
options.Events.OnTokenValidated = (ctx) =>
{
var redirectUri = new Uri(ctx.Properties.RedirectUri, UriKind.RelativeOrAbsolute);
if (redirectUri.IsAbsoluteUri)
{
ctx.Properties.RedirectUri = redirectUri.PathAndQuery;
}
//Sync user and the roles to EPiServer in the background
ServiceLocator.Current.GetInstance<ISynchronizingUserService>().SynchronizeAsync(ctx.Principal.Identity as ClaimsIdentity);
return Task.FromResult(0);
};
});
Update in the ‘Configure’ method
app.UseEndpoints(endpoints =>
{
endpoints.MapContent();
endpoints.MapControllerRoute("AspNetCore", "/Login", new { controller = "DefaultPage", action = "Login" });
endpoints.MapControllerRoute("AspNetCore", "/Logout", new { controller = "DefaultPage", action = "Logout" });
});
Step 4: Update “Logout” and “Login” methods in PageControllerBase.cs inside the Controllers folder:
public async Task<IActionResult> Logout()
{
//comment out old logout code
//await UISignInManager.Service.SignOutAsync();
//return Redirect(HttpContext.RequestServices.GetService<UrlResolver>().GetUrl(PageContext.ContentLink, PageContext.LanguageID));
await ControllerContext.HttpContext.SignOutAsync("azure-cookie");
HttpContext.Response.Cookies.Delete($".AspNetCore.{"azure-cookie"}");
return Redirect(HttpContext.RequestServices.GetService<UrlResolver>().GetUrl(PageContext.ContentLink, PageContext.LanguageID) ?? "/");
}
public IActionResult Login()
{
return Challenge(new AuthenticationProperties { RedirectUri = "/" }, "azure");
}
Step 5: Login CMS site with an AAD account
Launch the site with URLs: https://localhost:5000, then click the “Login” link in the footer of the home page. It will be redirected to the login page with AAD
Log in as a created user created before on the Azure Portal
The user will be synced to CMS as below:
Please sign in to leave a comment.