I am working on a project to integrate our internal SSO with UmbracoCMS so that users can authenticate with SSO using their credentials and then be automatically signed into Umbraco BackOffice. I am using .Net 4.7.2 with Umbraco 8. I installed Package UmbracoCms.IdentityExtensions, Microsoft.Owin.Security.OpenIdConnect, IdentityModel.
Currently, I seem to be able to login correctly to the internal SSO however it redirects me back to the Umbraco login page instead of the Umbraco BackOffice.
After logging in with SSO I see these Cookies.
UmbracoCustomOwinStartup.cs
using Microsoft.Owin;
using Microsoft.Owin.Security.OpenIdConnect;
using Owin;
using Microsoft.Owin.Security;
using Umbraco.Core.Security;
using Umbraco.Web;
using Umbraco.Web.Security;
using UmbracoApp;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Owin.Security.Notifications;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using System;
using Thinktecture.IdentityModel.Client;
using System.Linq;
using Umbraco.Core;
using Umbraco.IdentityExtensions;
using Microsoft.AspNet.Identity;
//To use this startup class, change the appSetting value in the web.config called
// "owin:appStartup" to be "UmbracoCustomOwinStartup"
[assembly: OwinStartup("UmbracoCustomOwinStartup", typeof(UmbracoCustomOwinStartup))]
namespace UmbracoApp
{
public class UmbracoCustomOwinStartup : UmbracoDefaultOwinStartup
{
/// <summary>
/// Configures the <see cref="BackOfficeUserManager"/> for Umbraco
/// </summary>
/// <param name="app"></param>
protected override void ConfigureUmbracoUserManager(IAppBuilder app)
{
// There are several overloads of this method that allow you to customize the BackOfficeUserManager or even custom BackOfficeUserStore.
app.ConfigureUserManagerForUmbracoBackOffice(
Services,
Mapper,
UmbracoSettings.Content,
GlobalSettings,
//The Umbraco membership provider needs to be specified in order to maintain backwards compatibility with the
// user password formats. The membership provider is not used for authentication, if you require custom logic
// to validate the username/password against an external data source you can create create a custom UserManager
// and override CheckPasswordAsync
global::Umbraco.Core.Security.MembershipProviderExtensions.GetUsersMembershipProvider().AsUmbracoMembershipProvider());
}
/// <summary>
/// Configures the back office authentication for Umbraco
/// </summary>
/// <param name="app"></param>
protected override void ConfigureUmbracoAuthentication(IAppBuilder app)
{
app
.UseUmbracoBackOfficeCookieAuthentication(UmbracoContextAccessor, RuntimeState, Services.UserService, GlobalSettings, UmbracoSettings.Security, PipelineStage.Authenticate)
.UseUmbracoBackOfficeExternalCookieAuthentication(UmbracoContextAccessor, RuntimeState, GlobalSettings, PipelineStage.Authenticate)
.UseUmbracoPreviewAuthentication(UmbracoContextAccessor, RuntimeState, GlobalSettings, UmbracoSettings.Security, PipelineStage.Authorize);
var identityOptions = new OpenIdConnectAuthenticationOptions
{
ClientId = <clientIdString>,
SignInAsAuthenticationType = Umbraco.Core.Constants.Security.BackOfficeExternalAuthenticationType,
Authority = "https://sso......",
RedirectUri = "http://localhost:49761/umbraco#/login",
ResponseType = OpenIdConnectResponseType.Code,
Scope = "openid",
PostLogoutRedirectUri = "http://localhost:49761",
ClientSecret = <clientSecretString>,
};
// Configure BackOffice Account Link button and style
identityOptions.ForUmbracoBackOffice("btn-microsoft", "fa-windows");
identityOptions.Caption = "OpenId Connect";
// Fix Authentication Type
identityOptions.AuthenticationType = identityOptions.Authority;
// Configure AutoLinking
identityOptions.SetExternalSignInAutoLinkOptions(
new ExternalSignInAutoLinkOptions(autoLinkExternalAccount: true));
identityOptions.Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
},
SecurityTokenValidated = async n =>
{
//Applies "claims transformation", as seen: https://identityserver.github.io/Documentation/docsv2/overview/mvcGettingStarted.html
//So that only the necessary claims are kept for the user's ticket
var id = n.AuthenticationTicket.Identity;
// we want to keep first name, last name, subject and roles
var email = id.FindFirst(JwtClaimTypes.Email);
var givenName = id.FindFirst(JwtClaimTypes.GivenName);
var familyName = id.FindFirst(JwtClaimTypes.FamilyName);
var sub = id.FindFirst(JwtClaimTypes.Subject);
// create new identity and set name and role claim type
var nid = new ClaimsIdentity(
id.AuthenticationType,
JwtClaimTypes.GivenName,
JwtClaimTypes.Role);
nid.AddClaim(email);
nid.AddClaim(givenName);
nid.AddClaim(familyName);
nid.AddClaim(sub);
// keep the id_token for logout
nid.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
nid.AddClaim(new Claim(ClaimTypes.NameIdentifier
, sub.Value, "http://www.w3.org/2001/XMLSchema#string", DefaultAuthenticationTypes.ExternalCookie));
n.AuthenticationTicket = new AuthenticationTicket(
nid,
n.AuthenticationTicket.Properties);
}
// SecurityTokenValidated = ClaimsTransformer.GenerateUserIdentityAsync
};
identityOptions.SignInAsAuthenticationType = Umbraco.Core.Constants.Security.BackOfficeExternalAuthenticationType;
app.UseOpenIdConnectAuthentication(identityOptions);
app.UseSignalR(GlobalSettings);
app.FinalizeMiddlewareConfiguration();
}
}
public class ClaimsTransformer
{
public static async Task GenerateUserIdentityAsync(
SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
var identityUser = new ClaimsIdentity(
notification.AuthenticationTicket.Identity.Claims,
notification.AuthenticationTicket.Identity.AuthenticationType,
ClaimTypes.Name,
ClaimTypes.Role);
var newIdentityUser = new ClaimsIdentity(identityUser.AuthenticationType,
ClaimTypes.GivenName, ClaimTypes.Role);
newIdentityUser.AddClaim(identityUser.FindFirst(ClaimTypes.NameIdentifier));
var emailClaim = identityUser.FindFirst(ClaimTypes.Email) ?? new Claim(ClaimTypes.Email, identityUser.FindFirst("name").Value);
newIdentityUser.AddClaim(emailClaim);
//Optionally add other claims
var userInfoClient = new UserInfoClient(new Uri(notification.Options.Authority + "/protocol/openid-connect/userinfo"), notification.ProtocolMessage.AccessToken);
var userInfo = await userInfoClient.GetAsync();
newIdentityUser.AddClaims(userInfo.Claims.Select(t => new Claim(t.Item1, t.Item2)));
notification.AuthenticationTicket = new AuthenticationTicket(newIdentityUser,
notification.AuthenticationTicket.Properties);
// notification.AuthenticationTicket.Properties.RedirectUri = "http://localhost:49761/umbraco#/content";
}
}
}
I think I need to send the user attributes as claims to the Umbraco BackOffice, but SecurityTokenValidated is not being fired. Any idea where to go from here?
Thank you in advance. Any help is very appreciated!
How to Login to Umbraco BackOffice using SSO OpenIDConnect
I am working on a project to integrate our internal SSO with UmbracoCMS so that users can authenticate with SSO using their credentials and then be automatically signed into Umbraco BackOffice. I am using .Net 4.7.2 with Umbraco 8. I installed Package UmbracoCms.IdentityExtensions, Microsoft.Owin.Security.OpenIdConnect, IdentityModel.
Currently, I seem to be able to login correctly to the internal SSO however it redirects me back to the Umbraco login page instead of the Umbraco BackOffice.
After logging in with SSO I see these Cookies.
UmbracoCustomOwinStartup.cs
Web.config
I think I need to send the user attributes as claims to the Umbraco BackOffice, but SecurityTokenValidated is not being fired. Any idea where to go from here?
Thank you in advance. Any help is very appreciated!
Resource Link: https://yuriburger.net/2017/04/26/login-to-umbraco-backoffice-using-identityserver4/
Did you find any solution? We have a similar problem with AD.
is working on a reply...