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;
}
}
}
}