Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save auroris/dae6482ae4c214b6e2d79e5d887c95fe to your computer and use it in GitHub Desktop.
Save auroris/dae6482ae4c214b6e2d79e5d887c95fe to your computer and use it in GitHub Desktop.
Using Umbraco 11 with auroris/OpenIddict-WindowsAuth
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;
}
}
}
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); }
}
}
}
}
{
"$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
}
}
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