using System.Security.Claims; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Nop.Core.Domain.Customers; using Nop.Services.Customers; namespace Nop.Services.Authentication; /// /// Represents service using cookie middleware for the authentication /// public partial class CookieAuthenticationService : IAuthenticationService { #region Fields protected readonly CustomerSettings _customerSettings; protected readonly ICustomerService _customerService; protected readonly IHttpContextAccessor _httpContextAccessor; protected Customer _cachedCustomer; #endregion #region Ctor public CookieAuthenticationService(CustomerSettings customerSettings, ICustomerService customerService, IHttpContextAccessor httpContextAccessor) { _customerSettings = customerSettings; _customerService = customerService; _httpContextAccessor = httpContextAccessor; } #endregion #region Methods /// /// Sign in /// /// Customer /// Whether the authentication session is persisted across multiple requests /// A task that represents the asynchronous operation public virtual async Task SignInAsync(Customer customer, bool isPersistent) { ArgumentNullException.ThrowIfNull(customer); //create claims for customer's username and email var claims = new List(); if (!string.IsNullOrEmpty(customer.Username)) claims.Add(new Claim(ClaimTypes.Name, customer.Username, ClaimValueTypes.String, NopAuthenticationDefaults.ClaimsIssuer)); if (!string.IsNullOrEmpty(customer.Email)) claims.Add(new Claim(ClaimTypes.Email, customer.Email, ClaimValueTypes.Email, NopAuthenticationDefaults.ClaimsIssuer)); //create principal for the current authentication scheme var userIdentity = new ClaimsIdentity(claims, NopAuthenticationDefaults.AuthenticationScheme); var userPrincipal = new ClaimsPrincipal(userIdentity); //set value indicating whether session is persisted and the time at which the authentication was issued var authenticationProperties = new AuthenticationProperties { IsPersistent = isPersistent, IssuedUtc = DateTime.UtcNow }; //sign in await _httpContextAccessor.HttpContext.SignInAsync(NopAuthenticationDefaults.AuthenticationScheme, userPrincipal, authenticationProperties); //cache authenticated customer _cachedCustomer = customer; } /// /// Sign out /// /// A task that represents the asynchronous operation public virtual async Task SignOutAsync() { //reset cached customer _cachedCustomer = null; //and sign out from the current authentication scheme await _httpContextAccessor.HttpContext.SignOutAsync(NopAuthenticationDefaults.AuthenticationScheme); } /// /// Get authenticated customer /// /// /// A task that represents the asynchronous operation /// The task result contains the customer /// public virtual async Task GetAuthenticatedCustomerAsync() { //whether there is a cached customer if (_cachedCustomer != null) return _cachedCustomer; //try to get authenticated user identity var authenticateResult = await _httpContextAccessor.HttpContext.AuthenticateAsync(NopAuthenticationDefaults.AuthenticationScheme); if (!authenticateResult.Succeeded) return null; Customer customer = null; if (_customerSettings.UsernamesEnabled) { //try to get customer by username var usernameClaim = authenticateResult.Principal.FindFirst(claim => claim.Type == ClaimTypes.Name && claim.Issuer.Equals(NopAuthenticationDefaults.ClaimsIssuer, StringComparison.InvariantCultureIgnoreCase)); if (usernameClaim != null) customer = await _customerService.GetCustomerByUsernameAsync(usernameClaim.Value); } else { //try to get customer by email var emailClaim = authenticateResult.Principal.FindFirst(claim => claim.Type == ClaimTypes.Email && claim.Issuer.Equals(NopAuthenticationDefaults.ClaimsIssuer, StringComparison.InvariantCultureIgnoreCase)); if (emailClaim != null) customer = await _customerService.GetCustomerByEmailAsync(emailClaim.Value); } //whether the found customer is available if (customer == null || !customer.Active || customer.RequireReLogin || customer.Deleted || !await _customerService.IsRegisteredAsync(customer)) return null; static DateTime trimMilliseconds(DateTime dt) => new(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second, 0, dt.Kind); //get the latest password var customerPassword = await _customerService.GetCurrentPasswordAsync(customer.Id); //require a customer to re-login after password changing var isPasswordChange = trimMilliseconds(customerPassword.CreatedOnUtc).CompareTo(trimMilliseconds(authenticateResult.Properties.IssuedUtc?.DateTime ?? DateTime.UtcNow)) > 0; if (_customerSettings.RequiredReLoginAfterPasswordChange && isPasswordChange) return null; //cache authenticated customer _cachedCustomer = customer; return _cachedCustomer; } #endregion }