Last active
July 5, 2023 22:51
-
-
Save auroris/dae6482ae4c214b6e2d79e5d887c95fe to your computer and use it in GitHub Desktop.
Using Umbraco 11 with auroris/OpenIddict-WindowsAuth
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.IdentityModel.Logging; | |
using Umbraco.Cms.Core; | |
namespace Umbraco4Wing.Plugins | |
{ | |
public static class IdentityServerAuthenticationExtension | |
{ | |
public static IUmbracoBuilder AddIdentityServerBackofficeAuthentication(this IUmbracoBuilder builder, IConfiguration _config) | |
{ | |
string SchemeLabel = _config.GetValue<string>("IdentityServer:Name") ?? "Forces"; | |
string SchemeName = Constants.Security.BackOfficeExternalAuthenticationTypePrefix + SchemeLabel; | |
IdentityModelEventSource.ShowPII = true; // Show debugging messages during OpenID | |
builder.Services.ConfigureOptions<IdentityServerProviderOptions>(); | |
builder.AddBackOfficeExternalLogins(logins => | |
{ | |
logins.AddBackOfficeLogin( | |
backOfficeAuthenticationBuilder => | |
{ | |
backOfficeAuthenticationBuilder.AddOpenIdConnect( | |
SchemeName, | |
SchemeLabel, | |
options => | |
{ | |
options.Authority = _config.GetValue<string>("IdentityServer:Authority"); | |
options.ClientId = _config.GetValue<string>("IdentityServer:ClientId"); | |
options.ClientSecret = _config.GetValue<string>("IdentityServer:ClientSecret"); | |
options.CallbackPath = "/signin-oidc"; | |
// What to ask IdentityServer for | |
options.ResponseType = "token id_token"; | |
options.Scope.Add("openid"); | |
options.Scope.Add("profile"); | |
options.Scope.Add("email"); | |
options.Scope.Add("roles"); | |
// I configured IdentityServer to return all claims on the id_token so no need to go | |
// get more | |
options.GetClaimsFromUserInfoEndpoint = false; | |
// Accept any SSL or even no SSL. My intranet server isn't configured to use SSL and | |
// even if it was has no way of verifying certs. | |
options.RequireHttpsMetadata = false; | |
options.BackchannelHttpHandler = new HttpClientHandler | |
{ | |
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true | |
}; | |
}); | |
}); | |
}); | |
return builder; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.AspNetCore.Identity; | |
using Microsoft.Extensions.Options; | |
using System.Security.Claims; | |
using Umbraco.Cms.Core.Models.Membership; | |
using Umbraco.Cms.Core.Security; | |
using Umbraco.Cms.Core.Services; | |
using Umbraco.Cms.Web.BackOffice.Security; | |
namespace Umbraco4Wing.Plugins | |
{ | |
public class IdentityServerProviderOptions: IConfigureNamedOptions<BackOfficeExternalLoginProviderOptions> | |
{ | |
private readonly IUserService _userService; | |
private readonly IConfiguration _config; | |
/// <summary> | |
/// Inject stuff we need | |
/// </summary> | |
/// <param name="userService"></param> | |
public IdentityServerProviderOptions(IUserService userService, IConfiguration config) | |
{ | |
_userService = userService; | |
_config = config; | |
} | |
public void Configure(BackOfficeExternalLoginProviderOptions providerOptions) | |
{ | |
providerOptions.ButtonStyle = "btn-microsoft"; | |
providerOptions.Icon = "fa fa-windows"; | |
providerOptions.DenyLocalLogin = false; | |
providerOptions.AutoRedirectLoginToExternalProvider = _config.GetValue<bool>("IdentityServer:AutoRedirectLoginToExternalProvider"); | |
providerOptions.AutoLinkOptions = new ExternalSignInAutoLinkOptions( | |
autoLinkExternalAccount: true, | |
defaultUserGroups: Array.Empty<string>(), | |
defaultCulture: null, | |
allowManualLinking: false | |
) | |
{ | |
OnAutoLinking = OnAutoLinking, | |
OnExternalLogin = OnExternalLogin | |
}; | |
} | |
public void Configure(string? name, BackOfficeExternalLoginProviderOptions options) | |
{ | |
Configure(options); | |
} | |
/// <summary> | |
/// When automatically linking a back office account to an external account | |
/// </summary> | |
/// <param name="autoLinkUser">The back office user the external account is being linked to</param> | |
/// <param name="loginInfo">Information about the external account</param> | |
void OnAutoLinking(BackOfficeIdentityUser autoLinkUser, ExternalLoginInfo loginInfo) | |
{ | |
SetupUser(autoLinkUser, loginInfo); | |
} | |
/// <summary> | |
/// When a user logins via the external login provider | |
/// </summary> | |
/// <param name="user">The back office user logging in</param> | |
/// <param name="loginInfo">Information about the external account</param> | |
/// <returns></returns> | |
bool OnExternalLogin(BackOfficeIdentityUser user, ExternalLoginInfo loginInfo) | |
{ | |
SetupUser(user, loginInfo); | |
// If the user has roles, return true, else return false | |
if (user.Roles.Count > 0) { return true; } | |
return false; | |
} | |
/// <summary> | |
/// Set up a user; specify their username, name, phone number, and roles | |
/// </summary> | |
/// <param name="user">The back office user logging in</param> | |
/// <param name="loginInfo">Information about the external account</param> | |
private void SetupUser(BackOfficeIdentityUser user, ExternalLoginInfo loginInfo) | |
{ | |
user.EnableChangeTracking(); | |
user.UserName = (loginInfo.Principal.FindFirstValue(ClaimTypes.WindowsAccountName) ?? "\\").Split('\\')[1]; | |
user.Name = loginInfo.Principal.FindFirstValue(ClaimTypes.Name); | |
user.PhoneNumber = loginInfo.Principal.FindFirstValue(ClaimTypes.HomePhone); | |
// If user is member of the administrators group, don't alter their roles | |
if (user.Roles.FirstOrDefault(r => r.RoleId.Equals("admin")) != null) | |
{ | |
return; | |
} | |
// Clear and re-add the user to any roles | |
IEnumerable<IUserGroup> groups = _userService.GetAllUserGroups(); | |
user.Roles.Clear(); | |
foreach (Claim role in loginInfo.Principal.Claims.Where(c => c.Type == ClaimTypes.Role)) | |
{ | |
IUserGroup? group = groups.FirstOrDefault(g => g.Name.Equals(role.Value, StringComparison.OrdinalIgnoreCase)); | |
if (group != null) { user.AddRole(group.Alias); } | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"$schema": "appsettings-schema.json", | |
"ConnectionStrings": { | |
"umbracoDbDSN": "Data Source=|DataDirectory|/Umbraco.sqlite.db;Cache=Shared;Foreign Keys=True;Pooling=True", | |
"umbracoDbDSN_ProviderName": "Microsoft.Data.Sqlite" | |
}, | |
"Umbraco": { }, | |
"IdentityServer": { | |
"Authority": "http://myintranetserver/IdentityServer", | |
"Name": "CompanyName", | |
"ClientId": "umbraco-backoffice", | |
"ClientSecret": "secret", | |
"AutoRedirectLoginToExternalProvider": false | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Umbraco.Cms.Core.Notifications; | |
using Umbraco.Cms.Core; | |
using Umbraco.Cms.Web.BackOffice.Security; | |
using Umbraco.Cms.Core.Models.Membership; | |
using Microsoft.IdentityModel.Logging; | |
using Umbraco.Cms.Core.Services; | |
using Microsoft.AspNetCore.Identity; | |
using Umbraco4Wing.Plugins; | |
using Umbraco.Cms.Core.Security; | |
using Umbraco4Wing.App_Code; | |
namespace Umbraco4Wing | |
{ | |
public class Startup | |
{ | |
private readonly IWebHostEnvironment _env; | |
private readonly IConfiguration _config; | |
private static Startup? _this; | |
/// <summary> | |
/// Initializes a new instance of the <see cref="Startup" /> class. | |
/// </summary> | |
/// <param name="webHostEnvironment">The web hosting environment.</param> | |
/// <param name="config">The configuration.</param> | |
/// <remarks> | |
/// Only a few services are possible to be injected here https://github.com/dotnet/aspnetcore/issues/9337. | |
/// </remarks> | |
public Startup(IWebHostEnvironment webHostEnvironment, IConfiguration config) | |
{ | |
_env = webHostEnvironment ?? throw new ArgumentNullException(nameof(webHostEnvironment)); | |
_config = config ?? throw new ArgumentNullException(nameof(config)); | |
_this = this; | |
} | |
public static IWebHostEnvironment GetWebHostEnvironment() | |
{ | |
if (_this is not null) | |
{ | |
return _this._env; | |
} | |
throw new NullReferenceException(); | |
} | |
/// <summary> | |
/// Configures the services. | |
/// </summary> | |
/// <param name="services">The services.</param> | |
/// <remarks> | |
/// This method gets called by the runtime. Use this method to add services to the container. | |
/// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940. | |
/// </remarks> | |
public void ConfigureServices(IServiceCollection services) | |
{ | |
services.Configure<CookiePolicyOptions>(options => | |
{ | |
options.MinimumSameSitePolicy = SameSiteMode.Unspecified; | |
options.OnAppendCookie = cookieContext => cookieContext.CookieOptions.SameSite = SameSiteMode.Strict; | |
options.OnDeleteCookie = cookieContext => cookieContext.CookieOptions.SameSite = SameSiteMode.Strict; | |
}); | |
services.AddUmbraco(_env, _config) | |
.AddBackOffice() | |
.AddIdentityServerBackofficeAuthentication(_config) | |
.AddWebsite() | |
.AddComposers() | |
.Build(); | |
} | |
/// <summary> | |
/// Configures the application. | |
/// </summary> | |
/// <param name="app">The application builder.</param> | |
/// <param name="env">The web hosting environment.</param> | |
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) | |
{ | |
if (env.IsDevelopment()) | |
{ | |
app.UseDeveloperExceptionPage(); | |
} | |
else | |
{ | |
app.UseCookiePolicy(); | |
} | |
app.UseUmbraco() | |
.WithMiddleware(u => | |
{ | |
u.UseBackOffice(); | |
u.UseWebsite(); | |
}) | |
.WithEndpoints(u => | |
{ | |
u.UseInstallerEndpoints(); | |
u.UseBackOfficeEndpoints(); | |
u.UseWebsiteEndpoints(); | |
}); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment