Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VCST-1431: Add external sign in for front end #2

Merged
merged 10 commits into from
Aug 9, 2024
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ csharp_preserve_single_line_statements = false
csharp_preserve_single_line_blocks = true
csharp_using_directive_placement = outside_namespace:silent
csharp_prefer_simple_using_statement = true:suggestion
csharp_style_namespace_declarations = block_scoped:silent
csharp_style_namespace_declarations = file_scoped:silent
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_top_level_statements = true:silent
csharp_style_expression_bodied_lambdas = true:silent
Expand Down
3 changes: 3 additions & 0 deletions src/VirtoCommerce.Xapi.Core/Models/StoreSettings.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Identity;

namespace VirtoCommerce.Xapi.Core.Models
Expand Down Expand Up @@ -28,6 +29,8 @@ public class StoreSettings

public PasswordOptions PasswordRequirements { get; set; }

public IList<string> AuthenticationTypes { get; set; }

public ModuleSettings[] Modules { get; set; }
}
}
1 change: 1 addition & 0 deletions src/VirtoCommerce.Xapi.Core/Schemas/StoreSettingsType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public StoreSettingsType()
Field(x => x.DefaultSelectedForCheckout).Description("Default \"Selected for checkout\" state for new line items and gifts");
Field(x => x.EnvironmentName).Description("Environment name");
Field<PasswordOptionsType>("passwordRequirements", "Password requirements");
Field<NonNullGraphType<ListGraphType<StringGraphType>>>("authenticationTypes");
Field<NonNullGraphType<ListGraphType<NonNullGraphType<ModuleSettingsType>>>>("modules");
}
}
5 changes: 5 additions & 0 deletions src/VirtoCommerce.Xapi.Core/Services/IUserManagerCore.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Threading.Tasks;
using GraphQL;
using VirtoCommerce.Platform.Core.Security;

namespace VirtoCommerce.Xapi.Core.Services
Expand All @@ -7,6 +9,9 @@ public interface IUserManagerCore
{
Task<bool> IsLockedOutAsync(ApplicationUser user);

[Obsolete("Use CheckCurrentUserState()", DiagnosticId = "VC0009", UrlFormat = "https://docs.virtocommerce.org/products/products-virto3-versions/")]
Task CheckUserState(string userId, bool allowAnonymous);

Task CheckCurrentUserState(IResolveFieldContext context, bool allowAnonymous);
}
}
2 changes: 1 addition & 1 deletion src/VirtoCommerce.Xapi.Core/VirtoCommerce.Xapi.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
<PackageReference Include="VirtoCommerce.CoreModule.Core" Version="3.807.0" />
<PackageReference Include="VirtoCommerce.CustomerModule.Core" Version="3.811.0" />
<PackageReference Include="VirtoCommerce.SearchModule.Core" Version="3.800.0" />
<PackageReference Include="VirtoCommerce.StoreModule.Core" Version="3.803.0" />
<PackageReference Include="VirtoCommerce.StoreModule.Core" Version="3.806.0" />
<PackageReference Include="VirtoCommerce.TaxModule.Core" Version="3.800.0" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using VirtoCommerce.Platform.Core.Security.ExternalSignIn;
using VirtoCommerce.Platform.Security.OpenIddict;
using VirtoCommerce.Xapi.Core;
using VirtoCommerce.Xapi.Core.Extensions;
Expand Down Expand Up @@ -52,6 +53,8 @@ public static IServiceCollection AddXCore(this IServiceCollection services, IGra
services.AddTransient<IDynamicPropertyUpdaterService, DynamicPropertyUpdaterService>();
services.AddTransient<IUserManagerCore, UserManagerCore>();
services.AddTransient<ITokenRequestValidator, ContactSignInValidator>();
services.AddTransient<IExternalSignInValidator, ExternalSignInValidator>();
services.AddTransient<IExternalSignInUserBuilder, ExternalSignInUserBuilder>();

// provider for external fields
services.AddSingleton<IExternalFieldProvider, ExternalFieldProvider>();
Expand Down
10 changes: 9 additions & 1 deletion src/VirtoCommerce.Xapi.Data/Queries/GetStoreQueryHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class GetStoreQueryHandler : IQueryHandler<GetStoreQuery, StoreResponse>
private readonly IdentityOptions _identityOptions;
private readonly GraphQLWebSocketOptions _webSocketOptions;
private readonly StoresOptions _storeOptions;
private readonly IStoreAuthenticationService _storeAuthenticationService;

public GetStoreQueryHandler(
IStoreService storeService,
Expand All @@ -37,13 +38,15 @@ public GetStoreQueryHandler(
ISettingsManager settingsManager,
IOptions<IdentityOptions> identityOptions,
IOptions<GraphQLWebSocketOptions> webSocketOptions,
IOptions<StoresOptions> storeOptions)
IOptions<StoresOptions> storeOptions,
IStoreAuthenticationService storeAuthenticationService)

{
_storeService = storeService;
_storeSearchService = storeSearchService;
_storeCurrencyResolver = storeCurrencyResolver;
_settingsManager = settingsManager;
_storeAuthenticationService = storeAuthenticationService;
_identityOptions = identityOptions.Value;
_webSocketOptions = webSocketOptions.Value;
_storeOptions = storeOptions.Value;
Expand Down Expand Up @@ -116,6 +119,11 @@ public async Task<StoreResponse> Handle(GetStoreQuery request, CancellationToken
EnvironmentName = _settingsManager.GetValue<string>(ModuleConstants.Settings.General.EnvironmentName),
PasswordRequirements = _identityOptions.Password,

AuthenticationTypes = (await _storeAuthenticationService.GetStoreSchemesAsync(store.Id))
.Where(x => x.IsActive)
.Select(x => x.Name)
.ToList(),

Modules = ToModulesSettings(store.Settings)
};
}
Expand Down
38 changes: 38 additions & 0 deletions src/VirtoCommerce.Xapi.Data/Services/ExternalSignInUserBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using VirtoCommerce.CustomerModule.Core.Model;
using VirtoCommerce.CustomerModule.Core.Services;
using VirtoCommerce.Platform.Core.Common;
using VirtoCommerce.Platform.Core.Security;
using VirtoCommerce.Platform.Core.Security.ExternalSignIn;
using VirtoCommerce.Platform.Core.Settings;
using VirtoCommerce.StoreModule.Core.Services;
using CustomerSettings = VirtoCommerce.CustomerModule.Core.ModuleConstants.Settings.General;

namespace VirtoCommerce.Xapi.Data.Services;

public class ExternalSignInUserBuilder(IStoreService storeService, IMemberService memberService) : IExternalSignInUserBuilder
{
public async Task BuildNewUser(ApplicationUser user, ExternalLoginInfo externalLoginInfo)
{
if (user.MemberId is null && user.UserType == UserType.Customer.ToString())
{
var contact = AbstractTypeFactory<Contact>.TryCreateInstance();
contact.Name = externalLoginInfo.Principal.FindFirstValue("name");
contact.FirstName = externalLoginInfo.Principal.FindFirstValue(ClaimTypes.GivenName);
contact.LastName = externalLoginInfo.Principal.FindFirstValue(ClaimTypes.Surname);
contact.Emails = [user.Email];

if (!string.IsNullOrEmpty(user.StoreId))
{
var store = await storeService.GetNoCloneAsync(user.StoreId);
contact.Status = store?.Settings.GetValue<string>(CustomerSettings.ContactDefaultStatus);
}

await memberService.SaveChangesAsync([contact]);

user.MemberId = contact.Id;
}
}
}
36 changes: 36 additions & 0 deletions src/VirtoCommerce.Xapi.Data/Services/ExternalSignInValidator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using VirtoCommerce.Platform.Core.Common;
using VirtoCommerce.Platform.Core.Security.ExternalSignIn;
using VirtoCommerce.StoreModule.Core.Services;

namespace VirtoCommerce.Xapi.Data.Services;

public class ExternalSignInValidator(IStoreService storeService,
IStoreAuthenticationService storeAuthenticationService)
: IExternalSignInValidator
{
public async Task<bool> ValidateAsync(ExternalSignInRequest request)
{
var store = await storeService.GetNoCloneAsync(request.StoreId);

if (store is null ||
string.IsNullOrEmpty(store.Url) ||
!Uri.TryCreate(store.Url, UriKind.Absolute, out var storeUri) ||
!IsValidUrl(storeUri, request.OidcUrl) ||
!IsValidUrl(storeUri, request.CallbackUrl))
{
return false;
}

var schemes = await storeAuthenticationService.GetStoreSchemesAsync(request.StoreId, clone: false);

return schemes.Any(x => x.IsActive && x.Name.EqualsIgnoreCase(request.AuthenticationType));
}

private static bool IsValidUrl(Uri baseUri, string uriString)
{
return Uri.TryCreate(uriString, UriKind.Absolute, out var uri) && baseUri.IsBaseOf(uri);
}
}
23 changes: 21 additions & 2 deletions src/VirtoCommerce.Xapi.Data/Services/UserManagerCore.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
using System;
using System.Threading.Tasks;
using GraphQL;
using Microsoft.AspNetCore.Identity;
using VirtoCommerce.Platform.Core.Security;
using VirtoCommerce.Platform.Security.Extensions;
using VirtoCommerce.Xapi.Core.Extensions;
using VirtoCommerce.Xapi.Core.Security.Authorization;
using VirtoCommerce.Xapi.Core.Services;

Expand All @@ -25,7 +28,23 @@ public virtual async Task<bool> IsLockedOutAsync(ApplicationUser user)
return result;
}

public async Task CheckUserState(string userId, bool allowAnonymous)
[Obsolete("Use CheckCurrentUserState()", DiagnosticId = "VC0009", UrlFormat = "https://docs.virtocommerce.org/products/products-virto3-versions/")]
public Task CheckUserState(string userId, bool allowAnonymous)
{
return CheckUserState(userId, allowAnonymous, isExternalSignIn: false);
}

public Task CheckCurrentUserState(IResolveFieldContext context, bool allowAnonymous)
{
var principal = context.GetCurrentPrincipal();
var userId = principal.GetCurrentUserId();
var isExternalSignIn = principal.IsExternalSignIn();

return CheckUserState(userId, allowAnonymous, isExternalSignIn);
}


private async Task CheckUserState(string userId, bool allowAnonymous, bool isExternalSignIn)
{
var userManager = _userManagerFactory();
var user = await userManager.FindByIdAsync(userId);
Expand All @@ -40,7 +59,7 @@ public async Task CheckUserState(string userId, bool allowAnonymous)
throw AuthorizationError.AnonymousAccessDenied();
}

if (user.PasswordExpired)
if (user.PasswordExpired && !isExternalSignIn)
{
throw AuthorizationError.PasswordExpired();
}
Expand Down
2 changes: 1 addition & 1 deletion src/VirtoCommerce.Xapi.Data/VirtoCommerce.Xapi.Data.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
<PackageReference Include="VirtoCommerce.Platform.Security" Version="3.841.0" />
<PackageReference Include="VirtoCommerce.Platform.Security" Version="3.848.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\VirtoCommerce.Xapi.Core\VirtoCommerce.Xapi.Core.csproj" />
Expand Down
2 changes: 0 additions & 2 deletions src/VirtoCommerce.Xapi.Web/VirtoCommerce.Xapi.Web.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="GraphQL.Server.Ui.Playground" Version="5.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
Expand Down
4 changes: 2 additions & 2 deletions src/VirtoCommerce.Xapi.Web/module.manifest
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
<version>3.802.0</version>
<version-tag></version-tag>

<platformVersion>3.841.0</platformVersion>
<platformVersion>3.848.0</platformVersion>
<dependencies>
<dependency id="VirtoCommerce.Core" version="3.807.0" />
<dependency id="VirtoCommerce.Customer" version="3.811.0" />
<dependency id="VirtoCommerce.Search" version="3.800.0" />
<dependency id="VirtoCommerce.Store" version="3.803.0" />
<dependency id="VirtoCommerce.Store" version="3.806.0" />
<dependency id="VirtoCommerce.Tax" version="3.800.0" />
</dependencies>
<incompatibilities>
Expand Down
Loading