Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
using Grand.Data;
using Grand.Domain.Customers;
using Grand.Domain.Stores;
using Grand.Infrastructure;
using Grand.Infrastructure.Extensions;
using Grand.SharedKernel.Extensions;
using Grand.Mediator;
Expand All @@ -31,7 +30,6 @@ public ExternalAuthenticationService(
IGroupService groupService,
IMediator mediator,
IRepository<ExternalAuthentication> externalAuthenticationRecordRepository,
IContextAccessor contextAccessor,
IEnumerable<IExternalAuthenticationProvider> externalAuthenticationProviders,
CustomerSettings customerSettings,
ExternalAuthenticationSettings externalAuthenticationSettings)
Expand All @@ -44,7 +42,6 @@ public ExternalAuthenticationService(
_groupService = groupService;
_mediator = mediator;
_externalAuthenticationRecordRepository = externalAuthenticationRecordRepository;
_contextAccessor = contextAccessor;
_externalAuthenticationProviders = externalAuthenticationProviders;
}

Expand All @@ -58,7 +55,6 @@ public ExternalAuthenticationService(
private readonly IGroupService _groupService;
private readonly IMediator _mediator;
private readonly IRepository<ExternalAuthentication> _externalAuthenticationRecordRepository;
private readonly IContextAccessor _contextAccessor;
private readonly IEnumerable<IExternalAuthenticationProvider> _externalAuthenticationProviders;
private readonly CustomerSettings _customerSettings;
private readonly ExternalAuthenticationSettings _externalAuthenticationSettings;
Expand All @@ -72,14 +68,16 @@ public ExternalAuthenticationService(
/// </summary>
/// <param name="associatedUser">Associated with passed external authentication parameters user</param>
/// <param name="currentLoggedInUser">Current logged-in user</param>
/// <param name="customer">Customer of the request (a guest when nobody is logged in)</param>
/// <param name="storeId">Store identifier</param>
/// <param name="returnUrl">URL to which the user will return after authentication</param>
/// <returns>Result of an authentication</returns>
protected virtual async Task<IActionResult> AuthenticateExistingUser(Customer associatedUser,
Customer currentLoggedInUser, string returnUrl)
Customer currentLoggedInUser, Customer customer, string storeId, string returnUrl)
{
//log in guest user
if (currentLoggedInUser == null)
return await LoginUser(associatedUser, returnUrl);
return await LoginUser(associatedUser, customer, storeId, returnUrl);

//account is already assigned to another user
if (currentLoggedInUser.Id != associatedUser.Id)
Expand All @@ -95,10 +93,12 @@ protected virtual async Task<IActionResult> AuthenticateExistingUser(Customer as
/// </summary>
/// <param name="currentLoggedInUser">Current logged-in user</param>
/// <param name="parameters">Authentication parameters received from external authentication method</param>
/// <param name="customer">Customer of the request (a guest when nobody is logged in)</param>
/// <param name="storeId">Store identifier</param>
/// <param name="returnUrl">URL to which the user will return after authentication</param>
/// <returns>Result of an authentication</returns>
protected virtual async Task<IActionResult> AuthenticateNewUser(Customer currentLoggedInUser,
ExternalAuthParam parameters, string returnUrl)
ExternalAuthParam parameters, Customer customer, string storeId, string returnUrl)
{
//associate external account with logged-in user
if (currentLoggedInUser != null)
Expand All @@ -111,7 +111,7 @@ protected virtual async Task<IActionResult> AuthenticateNewUser(Customer current

//or try to register new user
if (_customerSettings.UserRegistrationType != UserRegistrationType.Disabled)
return await RegisterNewUser(parameters, returnUrl);
return await RegisterNewUser(parameters, customer, storeId, returnUrl);

//registration is disabled
return Error(["Registration is disabled"]);
Expand All @@ -121,41 +121,44 @@ protected virtual async Task<IActionResult> AuthenticateNewUser(Customer current
/// Register new user
/// </summary>
/// <param name="parameters">Authentication parameters received from external authentication method</param>
/// <param name="customer">Guest customer of the request; registration turns it into the new user</param>
/// <param name="storeId">Store identifier</param>
/// <param name="returnUrl">URL to which the user will return after authentication</param>
/// <returns>Result of an authentication</returns>
protected virtual async Task<IActionResult> RegisterNewUser(ExternalAuthParam parameters, string returnUrl)
protected virtual async Task<IActionResult> RegisterNewUser(ExternalAuthParam parameters, Customer customer,
string storeId, string returnUrl)
{
var approved =
_customerSettings.UserRegistrationType is UserRegistrationType.Standard
or UserRegistrationType.EmailValidation;

//create registration request
var registrationRequest = new RegistrationRequest(_contextAccessor.WorkContext.CurrentCustomer,
var registrationRequest = new RegistrationRequest(customer,
parameters.Email, parameters.Email,
CommonHelper.GenerateRandomDigitCode(20),
PasswordFormat.Hashed,
_contextAccessor.StoreContext.CurrentStore.Id,
storeId,
approved);

//whether registration request has been completed successfully
await _customerManagerService.RegisterCustomer(registrationRequest);

//allow to save other customer values by consuming this event
await _mediator.Publish(new RegisteredByExternalMethod(_contextAccessor.WorkContext.CurrentCustomer, parameters));
await _mediator.Publish(new RegisteredByExternalMethod(customer, parameters));

//raise customer registered event
await _mediator.Publish(new CustomerRegisteredEvent(_contextAccessor.WorkContext.CurrentCustomer));
await _mediator.Publish(new CustomerRegisteredEvent(customer));

//associate external account with registered user
await AssociateCustomer(_contextAccessor.WorkContext.CurrentCustomer, parameters);
await AssociateCustomer(customer, parameters);

//authenticate
if (!approved)
return _customerSettings.UserRegistrationType == UserRegistrationType.AdminApproval
? new RedirectToRouteResult("RegisterResult",
new { resultId = (int)UserRegistrationType.AdminApproval })
: Error(["Error on registration"]);
await _authenticationService.SignIn(_contextAccessor.WorkContext.CurrentCustomer, false);
await _authenticationService.SignIn(customer, false);

return new RedirectToRouteResult("RegisterResult", new { resultId = (int)UserRegistrationType.Standard });
}
Expand All @@ -164,13 +167,15 @@ _customerSettings.UserRegistrationType is UserRegistrationType.Standard
/// Login passed user
/// </summary>
/// <param name="user">User to login</param>
/// <param name="previousCustomer">Customer of the request before the login (the guest whose cart is merged)</param>
/// <param name="storeId">Store identifier</param>
/// <param name="returnUrl">URL to which the user will return after authentication</param>
/// <returns>Result of an authentication</returns>
protected virtual async Task<IActionResult> LoginUser(Customer user, string returnUrl)
protected virtual async Task<IActionResult> LoginUser(Customer user, Customer previousCustomer, string storeId,
string returnUrl)
{
//raise event
await _mediator.Publish(new CustomerLoggedInEvent(user,
_contextAccessor.WorkContext.CurrentCustomer, _contextAccessor.StoreContext.CurrentStore.Id));
//raise event
await _mediator.Publish(new CustomerLoggedInEvent(user, previousCustomer, storeId));

//authenticate
await _authenticationService.SignIn(user, false);
Expand Down Expand Up @@ -201,14 +206,17 @@ protected virtual IActionResult Error(IEnumerable<string> errors)
/// Load active external authentication providers
/// </summary>
/// <returns>External Authentication Providers</returns>
public virtual IList<IExternalAuthenticationProvider> LoadActiveAuthenticationProviders(Customer customer = null,
Store store = null)
public virtual IList<IExternalAuthenticationProvider> LoadActiveAuthenticationProviders(Customer customer,
Store store)
{
ArgumentNullException.ThrowIfNull(customer);
ArgumentNullException.ThrowIfNull(store);

return LoadAllAuthenticationProviders()
.Where(provider =>
provider.IsMethodActive(_externalAuthenticationSettings) &&
provider.IsAuthenticateGroup(_contextAccessor.WorkContext.CurrentCustomer) &&
provider.IsAuthenticateStore(_contextAccessor.StoreContext.CurrentStore)
provider.IsAuthenticateGroup(customer) &&
provider.IsAuthenticateStore(store)
).ToList();
}

Expand Down Expand Up @@ -236,15 +244,20 @@ public virtual IList<IExternalAuthenticationProvider> LoadAllAuthenticationProvi
/// Check whether authentication by the passed external authentication provider is available
/// </summary>
/// <param name="systemName">System name of the external authentication provider</param>
/// <param name="customer">Customer whose groups the provider may be limited to</param>
/// <param name="store">Store the provider may be limited to</param>
/// <returns>True if is available; otherwise false</returns>
public virtual bool AuthenticationProviderIsAvailable(string systemName)
public virtual bool AuthenticationProviderIsAvailable(string systemName, Customer customer, Store store)
{
ArgumentNullException.ThrowIfNull(customer);
ArgumentNullException.ThrowIfNull(store);

var authenticationMethod = LoadAuthenticationProviderBySystemName(systemName);

return authenticationMethod != null &&
authenticationMethod.IsMethodActive(_externalAuthenticationSettings) &&
authenticationMethod.IsAuthenticateGroup(_contextAccessor.WorkContext.CurrentCustomer) &&
authenticationMethod.IsAuthenticateStore(_contextAccessor.StoreContext.CurrentStore);
authenticationMethod.IsAuthenticateGroup(customer) &&
authenticationMethod.IsAuthenticateStore(store);
}

#endregion
Expand All @@ -255,27 +268,30 @@ public virtual bool AuthenticationProviderIsAvailable(string systemName)
/// Authenticate user by passed parameters
/// </summary>
/// <param name="parameters">External authentication parameters</param>
/// <param name="customer">Customer of the request (a guest when nobody is logged in)</param>
/// <param name="store">Store of the request</param>
/// <param name="returnUrl">URL to which the user will return after authentication</param>
/// <returns>Result of an authentication</returns>
public virtual async Task<IActionResult> Authenticate(ExternalAuthParam parameters, string returnUrl = null)
public virtual async Task<IActionResult> Authenticate(ExternalAuthParam parameters, Customer customer, Store store,
string returnUrl = null)
{
ArgumentNullException.ThrowIfNull(parameters);
ArgumentNullException.ThrowIfNull(customer);
ArgumentNullException.ThrowIfNull(store);

if (!AuthenticationProviderIsAvailable(parameters.ProviderSystemName))
if (!AuthenticationProviderIsAvailable(parameters.ProviderSystemName, customer, store))
return Error(["External authentication method cannot be loaded"]);

//get current logged-in user
var currentLoggedInUser = await _groupService.IsRegistered(_contextAccessor.WorkContext.CurrentCustomer)
? _contextAccessor.WorkContext.CurrentCustomer
: null;
var currentLoggedInUser = await _groupService.IsRegistered(customer) ? customer : null;

//authenticate associated user if already exists
var associatedUser = await GetCustomer(parameters);
if (associatedUser != null)
return await AuthenticateExistingUser(associatedUser, currentLoggedInUser, returnUrl);
return await AuthenticateExistingUser(associatedUser, currentLoggedInUser, customer, store.Id, returnUrl);

//or associate and authenticate new user
return await AuthenticateNewUser(currentLoggedInUser, parameters, returnUrl);
return await AuthenticateNewUser(currentLoggedInUser, parameters, customer, store.Id, returnUrl);
}

#endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@ public interface IExternalAuthenticationService
/// Use passed parameters to authenticate customer
/// </summary>
/// <param name="parameters">External authentication parameters</param>
/// <param name="customer">Customer of the request (a guest when nobody is logged in)</param>
/// <param name="store">Store of the request</param>
/// <param name="returnUrl">Defines the URL of user return, when successfully authenticated</param>
/// <returns>Result of an authentication</returns>
Task<IActionResult> Authenticate(ExternalAuthParam parameters, string returnUrl = null);
Task<IActionResult> Authenticate(ExternalAuthParam parameters, Customer customer, Store store,
string returnUrl = null);

#endregion

Expand Down Expand Up @@ -50,11 +53,10 @@ public interface IExternalAuthenticationService
/// <summary>
/// Load all active authentication providers
/// </summary>
/// <param name="customer">Customer</param>
/// <param name="store">Store</param>
/// <param name="customer">Customer whose groups the providers may be limited to</param>
/// <param name="store">Store the providers may be limited to</param>
/// <returns>External authentication providers</returns>
IList<IExternalAuthenticationProvider> LoadActiveAuthenticationProviders(Customer customer = null,
Store store = null);
IList<IExternalAuthenticationProvider> LoadActiveAuthenticationProviders(Customer customer, Store store);

/// <summary>
/// Load specified external authentication provider by system name
Expand All @@ -73,8 +75,10 @@ IList<IExternalAuthenticationProvider> LoadActiveAuthenticationProviders(Custome
/// Check whether authentication by the passed external authentication provider is available
/// </summary>
/// <param name="systemName">System name of the external authentication provider</param>
/// <param name="customer">Customer whose groups the provider may be limited to</param>
/// <param name="store">Store the provider may be limited to</param>
/// <returns>True if is available; otherwise false</returns>
bool AuthenticationProviderIsAvailable(string systemName);
bool AuthenticationProviderIsAvailable(string systemName, Customer customer, Store store);

#endregion
}
4 changes: 2 additions & 2 deletions src/Core/Grand.Domain/Customers/CustomerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -232,12 +232,12 @@ public static bool IsPasswordRecoveryLinkExpired(this Customer customer, Custome
/// Get customer group identifiers
/// </summary>
/// <param name="customer">Customer</param>
/// <returns>Customer group identifiers</returns>
/// <returns>Customer group identifiers, sorted so that cache keys built from them do not depend on the stored order</returns>
public static string[] GetCustomerGroupIds(this Customer customer)
{
ArgumentNullException.ThrowIfNull(customer);

return customer.Groups.ToArray();
return customer.Groups.Order(StringComparer.Ordinal).ToArray();
}

#endregion
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Authentication.Facebook.Models;
using Grand.Business.Core.Interfaces.Authentication;
using Grand.Business.Core.Utilities.Authentication;
using Grand.Infrastructure;
using Grand.SharedKernel;
using Grand.Web.Common.Controllers;
using Microsoft.AspNetCore.Authentication;
Expand All @@ -17,10 +18,12 @@ public class FacebookAuthenticationController : BasePluginController

public FacebookAuthenticationController(
IExternalAuthenticationService externalAuthenticationService,
IConfiguration configuration)
IConfiguration configuration,
IContextAccessor contextAccessor)
{
_externalAuthenticationService = externalAuthenticationService;
_configuration = configuration;
_contextAccessor = contextAccessor;
}

#endregion
Expand All @@ -29,15 +32,17 @@ public FacebookAuthenticationController(

private readonly IExternalAuthenticationService _externalAuthenticationService;
private readonly IConfiguration _configuration;
private readonly IContextAccessor _contextAccessor;

#endregion

#region Methods

public IActionResult FacebookLogin(string returnUrl)
{
if (!_externalAuthenticationService.AuthenticationProviderIsAvailable(FacebookAuthenticationDefaults
.ProviderSystemName))
if (!_externalAuthenticationService.AuthenticationProviderIsAvailable(
FacebookAuthenticationDefaults.ProviderSystemName, _contextAccessor.WorkContext.CurrentCustomer,
_contextAccessor.StoreContext.CurrentStore))
throw new GrandException("Facebook authentication module cannot be loaded");

if (string.IsNullOrEmpty(_configuration["FacebookSettings:AppId"]) ||
Expand Down Expand Up @@ -71,7 +76,8 @@ public async Task<IActionResult> FacebookLoginCallback(string returnUrl)
};

//authenticate Grand user
return await _externalAuthenticationService.Authenticate(authenticationParameters, returnUrl);
return await _externalAuthenticationService.Authenticate(authenticationParameters,
_contextAccessor.WorkContext.CurrentCustomer, _contextAccessor.StoreContext.CurrentStore, returnUrl);
}

public IActionResult FacebookSignInFailed(string error_code, string error_message, string state)
Expand Down
Loading
Loading