Trying to authenticate backoffice user via SAML2 authentication method, below is my code in which user successfully authenticated via “options.Notifications.AcsCommandResultCreated in ConfigureSaml2Options.cs” but it redirect to “/umbraco#/login/false?returnPath=%252Fumbraco%2523%252Fcontent” without creating user in backoffice and no auto login ,
code are distributed in 3 place
- SamlComposer2.cs
- ConfigureSaml2Options.cs
- BackOfficeSamlOptions.cs
any help or guidance will be helpful
1)SamlComposer2.cs
{
public void Compose(IUmbracoBuilder builder)
{
builder.Services.ConfigureOptions<BackOfficeSamlOptions>();
builder.Services.ConfigureOptions<ConfigureSaml2Options>();
builder.Services.AddHttpContextAccessor();
builder.AddBackOfficeExternalLogins(logins =>
{
logins.AddBackOfficeLogin(backOfficeAuthenticationBuilder =>
{
Console.WriteLine("Registering SAML login provider..." + BackOfficeSamlOptions.SchemeName1);
backOfficeAuthenticationBuilder.AddSaml2(
backOfficeAuthenticationBuilder.SchemeForBackOffice("Umbraco.Saml2"),
"company Employee Id", // Button label
options => { }
);
Console.WriteLine("Registering SAML login provider... completed");
});
});
}
}```
2) ConfigureSaml2Options.cs
public class ConfigureSaml2Options : IConfigureNamedOptions<Saml2Options>
{
private readonly IHttpContextAccessor _httpContextAccessor;
public ConfigureSaml2Options(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void Configure(string name, Saml2Options options)
{
var expectedSchemeName = Constants.Security.BackOfficeExternalAuthenticationTypePrefix + BackOfficeSamlOptions.SchemeName1;
options.SignInScheme = Constants.Security.BackOfficeExternalAuthenticationType;
if (!string.Equals(name, expectedSchemeName, StringComparison.Ordinal))
return;
options.SPOptions = new SPOptions
{
EntityId = new EntityId("https://localhost:44338/Saml2"),
ReturnUrl = new Uri("https://localhost:44338/Saml2/Acs", UriKind.Absolute)
};
var idp = new IdentityProvider(
new EntityId("https://sts.windows.net/{XXXX-tenant-id}/"),
options.SPOptions)
{
LoadMetadata = true,
MetadataLocation = "https://login.microsoftonline.com/{XXXX-tenant-id}/federationmetadata/2007-06/federationmetadata.xml?appid=XXXXXX",
AllowUnsolicitedAuthnResponse = true
};
options.IdentityProviders.Add(idp);
options.Notifications.AcsCommandResultCreated = async (commandResult, saml2Response) =>
{
var httpContext = _httpContextAccessor.HttpContext;
var claimsIdentity = (ClaimsIdentity)commandResult.Principal.Identity;
var emailClaim = claimsIdentity.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress")?.Value;
var nameClaim = claimsIdentity.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name")?.Value;
var nameIdClaim = claimsIdentity.FindFirst(ClaimTypes.NameIdentifier)?.Value;
if (!string.IsNullOrWhiteSpace(emailClaim))
{
claimsIdentity.AddClaim(new Claim(ClaimTypes.Email, emailClaim));
}
if (!string.IsNullOrWhiteSpace(nameClaim))
{
claimsIdentity.AddClaim(new Claim(ClaimTypes.Name, nameClaim));
claimsIdentity.AddClaim(new Claim(ClaimTypes.GivenName, nameClaim));
claimsIdentity.AddClaim(new Claim(ClaimTypes.NameIdentifier, nameClaim));
}
else if (!string.IsNullOrWhiteSpace(nameIdClaim))
{
claimsIdentity.AddClaim(new Claim(ClaimTypes.Name, nameIdClaim));
}
var returnPath = httpContext.Request.Query["returnPath"].ToString();
Console.WriteLine($"Return path: {returnPath}");
// Fallback logic if returnPath is empty
if (string.IsNullOrEmpty(returnPath))
{
returnPath = "/umbraco#/content?mculture=en-US"; // Default to Umbraco dashboard
}
// Sign the user in
await httpContext.SignInAsync(Constants.Security.BackOfficeAuthenticationType, commandResult.Principal);
// Redirect to the returnPath after login
httpContext.Response.Redirect(returnPath);
};
}
public void Configure(Saml2Options options)
=> Configure(Constants.Security.BackOfficeExternalAuthenticationTypePrefix + BackOfficeSamlOptions.SchemeName1, options);
}
3) BackOfficeSamlOptions.cs
public class BackOfficeSamlOptions : IConfigureNamedOptions<BackOfficeExternalLoginProviderOptions>
{
public const string SchemeName1 = "Saml2"; // Your SAML scheme name
public void Configure(string? name, BackOfficeExternalLoginProviderOptions options)
{
var expectedSchemeName = Constants.Security.BackOfficeExternalAuthenticationTypePrefix + SchemeName1;
if (!string.Equals(name, expectedSchemeName, StringComparison.Ordinal))
return;
Configure(options);
}
public void Configure(BackOfficeExternalLoginProviderOptions options)
{
Console.WriteLine("Configuring SAML External Login Provider...");
options.AutoLinkOptions = new ExternalSignInAutoLinkOptions(
autoLinkExternalAccount: true,
defaultUserGroups: new[] { Constants.Security.EditorGroupAlias },
// Your user group alias
defaultCulture: null,
allowManualLinking: false)
{
OnAutoLinking = (autoLinkUser, loginInfo) =>
{
var emailClaim = loginInfo.Principal.FindFirst(ClaimTypes.Email);
if (emailClaim != null)
{
autoLinkUser.Email = emailClaim.Value;
}
var nameClaim = loginInfo.Principal.FindFirst(ClaimTypes.Name);
if (nameClaim != null)
{
autoLinkUser.Name = nameClaim.Value;
autoLinkUser.UserName = nameClaim.Value; // Optional fallback
}
var nameIdClaim = loginInfo.Principal.FindFirst(ClaimTypes.NameIdentifier);
if (!string.IsNullOrWhiteSpace(autoLinkUser.UserName) == false && nameIdClaim != null)
{
autoLinkUser.UserName = nameIdClaim.Value;
}
autoLinkUser.IsApproved = true;
},
OnExternalLogin = (user, loginInfo) =>
{
var nameClaim = loginInfo.Principal.FindFirst(ClaimTypes.Name);
if (nameClaim != null)
{
user.Name = nameClaim.Value;
}
return true; // Allow login
}
};
}
}