Introduce IExternalLoginProvider

pull/4979/head
Halil İbrahim Kalkan 5 years ago
parent c232b8d761
commit 01580e3aa7

@ -6,5 +6,12 @@
/// Default: true.
/// </summary>
public bool ConfigureAuthentication { get; set; } = true;
public ExternalLoginProviderDictionary ExternalLoginProviders { get; }
public AbpIdentityAspNetCoreOptions()
{
ExternalLoginProviders = new ExternalLoginProviderDictionary();
}
}
}
}

@ -2,6 +2,7 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@ -9,6 +10,8 @@ namespace Volo.Abp.Identity.AspNetCore
{
public class AbpSignInManager : SignInManager<IdentityUser>
{
protected AbpIdentityAspNetCoreOptions AbpOptions { get; }
public AbpSignInManager(
UserManager<IdentityUser> userManager,
IHttpContextAccessor contextAccessor,
@ -16,7 +19,8 @@ namespace Volo.Abp.Identity.AspNetCore
IOptions<IdentityOptions> optionsAccessor,
ILogger<SignInManager<IdentityUser>> logger,
IAuthenticationSchemeProvider schemes,
IUserConfirmation<IdentityUser> confirmation
IUserConfirmation<IdentityUser> confirmation,
IOptions<AbpIdentityAspNetCoreOptions> options
) : base(
userManager,
contextAccessor,
@ -26,16 +30,42 @@ namespace Volo.Abp.Identity.AspNetCore
schemes,
confirmation)
{
AbpOptions = options.Value;
}
public override Task<SignInResult> PasswordSignInAsync(
public override async Task<SignInResult> PasswordSignInAsync(
string userName,
string password,
bool isPersistent,
bool lockoutOnFailure)
{
return base.PasswordSignInAsync(userName, password, isPersistent, lockoutOnFailure);
foreach (var externalLoginProviderInfo in AbpOptions.ExternalLoginProviders.Values)
{
var externalLoginProvider = (IExternalLoginProvider) Context.RequestServices
.GetRequiredService(externalLoginProviderInfo.Type);
if (await externalLoginProvider.TryAuthenticateAsync(userName, password))
{
var user = await UserManager.FindByNameAsync(userName);
if (user == null)
{
user = await externalLoginProvider.CreateUserAsync(userName);
//TODO: TenantId, LoginProvider, Password, NormalizeNames
//TODO: Set default roles
await UserManager.CreateAsync(user);
}
else
{
await externalLoginProvider.UpdateUserAsync(user);
//TODO: LoginProvider
await UserManager.UpdateAsync(user);
}
return await SignInOrTwoFactorAsync(user, isPersistent);
}
}
return await base.PasswordSignInAsync(userName, password, isPersistent, lockoutOnFailure);
}
}
}

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
namespace Volo.Abp.Identity.AspNetCore
{
public class ExternalLoginProviderDictionary : Dictionary<string, ExternalLoginProviderInfo>
{
/// <summary>
/// Adds or replaces a provider.
/// </summary>
public void Add<TProvider>([NotNull] string name)
where TProvider : IExternalLoginProvider
{
this[name] = new ExternalLoginProviderInfo(name, typeof(TProvider));
}
}
}

@ -0,0 +1,25 @@
using System;
using JetBrains.Annotations;
namespace Volo.Abp.Identity.AspNetCore
{
public class ExternalLoginProviderInfo
{
public string Name { get; }
public Type Type
{
get => _type;
set => _type = Check.NotNull(value, nameof(value));
}
private Type _type;
public ExternalLoginProviderInfo(
[NotNull] string name,
[NotNull] Type type)
{
Name = Check.NotNullOrWhiteSpace(name, nameof(name));
Type = Check.AssignableTo<IExternalLoginProvider>(type, nameof(type));
}
}
}

@ -0,0 +1,30 @@
using System.Threading.Tasks;
namespace Volo.Abp.Identity.AspNetCore
{
public interface IExternalLoginProvider //TODO: A base class to simplift implementing this!
{
/// <summary>
/// Used to try authenticate a user by this source.
/// </summary>
/// <param name="userName">User name or email address</param>
/// <param name="plainPassword">Plain password of the user</param>
/// <returns>True, indicates that this used has authenticated by this source</returns>
Task<bool> TryAuthenticateAsync(string userName, string plainPassword);
/// <summary>
/// This method is called when a user is authenticated by this source but the user does not exists yet.
/// So, the source should create the user and fill the properties.
/// </summary>
/// <param name="userName">User name</param>
/// <returns>Newly created user</returns>
Task<IdentityUser> CreateUserAsync(string userName);
/// <summary>
/// This method is called after an existing user is authenticated by this source.
/// It can be used to update some properties of the user by the source.
/// </summary>
/// <param name="user">The user that can be updated</param>
Task UpdateUserAsync(IdentityUser user);
}
}

@ -1,9 +1,24 @@
using Volo.Abp.AspNetCore.TestBase;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.AspNetCore.TestBase;
namespace Volo.Abp.Identity.AspNetCore
{
public abstract class AbpIdentityAspNetCoreTestBase : AbpAspNetCoreIntegratedTestBase<AbpIdentityAspNetCoreTestStartup>
{
protected virtual async Task<string> GetResponseAsStringAsync(string url, HttpStatusCode expectedStatusCode = HttpStatusCode.OK)
{
var response = await GetResponseAsync(url, expectedStatusCode);
return await response.Content.ReadAsStringAsync();
}
protected virtual async Task<HttpResponseMessage> GetResponseAsync(string url, HttpStatusCode expectedStatusCode = HttpStatusCode.OK)
{
var response = await Client.GetAsync(url);
response.StatusCode.ShouldBe(expectedStatusCode);
return response;
}
}
}

@ -24,6 +24,14 @@ namespace Volo.Abp.Identity.AspNetCore
});
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpIdentityAspNetCoreOptions>(options =>
{
options.ExternalLoginProviders.Add<FakeExternalLoginProvider>(FakeExternalLoginProvider.Name);
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();

@ -35,20 +35,5 @@ namespace Volo.Abp.Identity.AspNetCore
result.ShouldBe("Failed");
}
//TODO: Move to a better common place ----------------------------------------------------
protected virtual async Task<string> GetResponseAsStringAsync(string url, HttpStatusCode expectedStatusCode = HttpStatusCode.OK)
{
var response = await GetResponseAsync(url, expectedStatusCode);
return await response.Content.ReadAsStringAsync();
}
protected virtual async Task<HttpResponseMessage> GetResponseAsync(string url, HttpStatusCode expectedStatusCode = HttpStatusCode.OK)
{
var response = await Client.GetAsync(url);
response.StatusCode.ShouldBe(expectedStatusCode);
return response;
}
}
}

@ -0,0 +1,19 @@
using System.Threading.Tasks;
using Shouldly;
using Xunit;
namespace Volo.Abp.Identity.AspNetCore
{
public class ExternalLoginProvider_Tests : AbpIdentityAspNetCoreTestBase
{
[Fact]
public async Task Should_SignIn_With_ExternalLoginProvider()
{
var result = await GetResponseAsStringAsync(
"api/signin-test/password?userName=ext_user&password=abc"
);
result.ShouldBe("Succeeded");
}
}
}

@ -0,0 +1,34 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Identity.AspNetCore
{
public class FakeExternalLoginProvider : IExternalLoginProvider, ITransientDependency
{
public const string Name = "Fake";
public Task<bool> TryAuthenticateAsync(string userName, string plainPassword)
{
return Task.FromResult(
userName == "ext_user" && plainPassword == "abc"
);
}
public Task<IdentityUser> CreateUserAsync(string userName)
{
return Task.FromResult(
new IdentityUser(
Guid.NewGuid(),
userName,
"test@abp.io"
)
);
}
public Task UpdateUserAsync(IdentityUser user)
{
return Task.CompletedTask;
}
}
}
Loading…
Cancel
Save