using System; using System.Diagnostics; using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; using Constants = Umbraco.Cms.Core.Constants; namespace Umbraco.Cms.Web.BackOffice.Security { /// /// Custom used to associate external logins with umbraco external login options /// public class BackOfficeAuthenticationBuilder : AuthenticationBuilder { private readonly Action _loginProviderOptions; public BackOfficeAuthenticationBuilder( IServiceCollection services, Action loginProviderOptions = null) : base(services) => _loginProviderOptions = loginProviderOptions ?? (x => { }); public string SchemeForBackOffice(string scheme) => Constants.Security.BackOfficeExternalAuthenticationTypePrefix + scheme; /// /// Overridden to track the final authenticationScheme being registered for the external login /// /// /// /// /// /// /// public override AuthenticationBuilder AddRemoteScheme(string authenticationScheme, string displayName, Action configureOptions) { // Validate that the prefix is set if (!authenticationScheme.StartsWith(Constants.Security.BackOfficeExternalAuthenticationTypePrefix)) { throw new InvalidOperationException($"The {nameof(authenticationScheme)} is not prefixed with {Constants.Security.BackOfficeExternalAuthenticationTypePrefix}. The scheme must be created with a call to the method {nameof(SchemeForBackOffice)}"); } // add our login provider to the container along with a custom options configuration Services.Configure(authenticationScheme, _loginProviderOptions); base.Services.AddSingleton(services => { return new BackOfficeExternalLoginProvider( authenticationScheme, services.GetRequiredService>()); }); Services.TryAddEnumerable(ServiceDescriptor.Singleton, EnsureBackOfficeScheme>()); return base.AddRemoteScheme(authenticationScheme, displayName, configureOptions); } // TODO: We could override and throw NotImplementedException for other methods? // Ensures that the sign in scheme is always the Umbraco back office external type private class EnsureBackOfficeScheme : IPostConfigureOptions where TOptions : RemoteAuthenticationOptions { public void PostConfigure(string name, TOptions options) { options.SignInScheme = Constants.Security.BackOfficeExternalAuthenticationType; } } } }