using System; using System.Diagnostics; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; using Umbraco.Cms.Web.Common.Security; using Umbraco.Extensions; using Constants = Umbraco.Cms.Core.Constants; namespace Umbraco.Cms.Web.Website.Security { /// /// Custom used to associate external logins with umbraco external login options /// public class MemberAuthenticationBuilder : AuthenticationBuilder { private readonly Action _loginProviderOptions; public MemberAuthenticationBuilder( IServiceCollection services, Action loginProviderOptions = null) : base(services) => _loginProviderOptions = loginProviderOptions ?? (x => { }); public string SchemeForMembers(string scheme) => scheme?.EnsureStartsWith(Constants.Security.MemberExternalAuthenticationTypePrefix); /// /// 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.MemberExternalAuthenticationTypePrefix)) { throw new InvalidOperationException($"The {nameof(authenticationScheme)} is not prefixed with {Constants.Security.MemberExternalAuthenticationTypePrefix}. The scheme must be created with a call to the method {nameof(SchemeForMembers)}"); } // add our login provider to the container along with a custom options configuration Services.Configure(authenticationScheme, _loginProviderOptions); base.Services.AddSingleton(services => { return new MemberExternalLoginProvider( authenticationScheme, services.GetRequiredService>()); }); Services.TryAddEnumerable(ServiceDescriptor.Singleton, EnsureMemberScheme>()); return base.AddRemoteScheme(authenticationScheme, displayName, configureOptions); } // Ensures that the sign in scheme is always the Umbraco member external type private class EnsureMemberScheme : IPostConfigureOptions where TOptions : RemoteAuthenticationOptions { public void PostConfigure(string name, TOptions options) { if (!name.StartsWith(Constants.Security.MemberExternalAuthenticationTypePrefix)) { return; } options.SignInScheme = IdentityConstants.ExternalScheme; } } } }