From ff5a8fa5718bf81d33246a945ef62f31d7bdfe44 Mon Sep 17 00:00:00 2001 From: Sunil Buddala Date: Sun, 12 Jun 2022 11:31:54 +1200 Subject: [PATCH 1/3] wip for dk --- src/API/Endpoints/Auth/LoginWithGoogle.cs | 35 ++ src/API/Program.cs | 28 +- src/API/Routes/AuthRoutes.cs | 1 + src/API/appsettings.json | 21 +- src/Application/Application.csproj | 1 + .../Commands/Auth/GoogleLoginCommand.cs | 57 ++ .../Commands/Auth/LogOutCommand.cs | 19 +- .../Commands/Auth/RefreshCommand.cs | 20 +- .../Commands/Auth/RegisterUserCommand.cs | 2 +- .../DTOs/Auth/GoogleLoginUserRequest.cs | 12 + .../Common/DTOs/Auth/LoginUserRequest.cs | 2 +- .../Interfaces/IUsersDbReadOnlyContext.cs | 13 + ...onDbContext.cs => IUsersDbWriteContext.cs} | 2 +- .../Common/Settings/JwtSettings.cs | 8 + .../Auth/GoogleLoginUserDtoSchemaFilter.cs | 16 + .../Auth/LoginUserDtoSchemaFilter.cs | 2 +- src/Domain/Entities/User.cs | 9 - src/Infrastructure/Infrastructure.csproj | 4 +- .../20210822072902_initial.Designer.cs | 494 +++++++++--------- .../ApplicationDbContextModelSnapshot.cs | 484 ++++++++--------- .../Persistence/UsersDbReadOnlyContext.cs | 44 ++ ...ionDbContext.cs => UsersDbWriteContext.cs} | 6 +- .../ServiceCollectionExtension.cs | 31 +- .../Services/AuthenticateService.cs | 6 +- 24 files changed, 758 insertions(+), 559 deletions(-) create mode 100644 src/API/Endpoints/Auth/LoginWithGoogle.cs create mode 100644 src/Application/Commands/Auth/GoogleLoginCommand.cs create mode 100644 src/Application/Common/DTOs/Auth/GoogleLoginUserRequest.cs create mode 100644 src/Application/Common/Interfaces/IUsersDbReadOnlyContext.cs rename src/Application/Common/Interfaces/{IApplicationDbContext.cs => IUsersDbWriteContext.cs} (88%) create mode 100644 src/Application/Common/SwaggerSchemaFilters/Auth/GoogleLoginUserDtoSchemaFilter.cs create mode 100644 src/Infrastructure/Persistence/UsersDbReadOnlyContext.cs rename src/Infrastructure/Persistence/{ApplicationDbContext.cs => UsersDbWriteContext.cs} (86%) diff --git a/src/API/Endpoints/Auth/LoginWithGoogle.cs b/src/API/Endpoints/Auth/LoginWithGoogle.cs new file mode 100644 index 0000000..a43904a --- /dev/null +++ b/src/API/Endpoints/Auth/LoginWithGoogle.cs @@ -0,0 +1,35 @@ +using System.Threading; +using System.Threading.Tasks; +using API.Routes; +using Application.Commands.Auth; +using Application.Common.DTOs.Auth; +using Application.Common.Models; +using Ardalis.ApiEndpoints; +using MediatR; +using Microsoft.AspNetCore.Mvc; +using Swashbuckle.AspNetCore.Annotations; + +namespace API.Endpoints.Auth; + +[Route(AuthRoutes.LoginWithGoogle)] +public class LoginWithGoogle : BaseAsyncEndpoint + .WithRequest + .WithResponse> +{ + private readonly IMediator _mediator; + + public LoginWithGoogle(IMediator mediator) => _mediator = mediator; + + [HttpPost, + SwaggerOperation(Description = "Signs in or signs up provided google user and return token", + Summary = "Sign In or Sing Up wiht Google Account", + OperationId = "Auth.LoginWithGoogle", + Tags = new[] { "Auth" }), + SwaggerResponse(200, "User logged in successfully", typeof(IResponse)), + Produces("application/json"), Consumes("application/json")] + public override async Task>> HandleAsync( + [SwaggerRequestBody("User google login payload", Required = true)] + GoogleLoginUserRequest loginUserRequest, + CancellationToken cancellationToken = new()) => + Ok(await _mediator.Send(new GoogleLoginCommand(loginUserRequest), cancellationToken)); +} \ No newline at end of file diff --git a/src/API/Program.cs b/src/API/Program.cs index 307938d..68e8800 100644 --- a/src/API/Program.cs +++ b/src/API/Program.cs @@ -1,8 +1,6 @@ using System; using System.Threading.Tasks; -using Infrastructure.Persistence; using Microsoft.AspNetCore.Hosting; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -33,19 +31,19 @@ public static async Task Main(string[] args) Log.Information("Application Starting..."); var host = CreateHostBuilder(args).Build(); using var scope = host.Services.CreateScope(); - var serviceProvider = scope.ServiceProvider; - try - { - var context = serviceProvider.GetService(); - if (context!.Database.IsSqlServer()) - await context.Database.MigrateAsync(); - // Seed database here - } - catch (Exception e) - { - Log.Fatal(e,"An error occurred while migrating the database."); - throw; - } + //var serviceProvider = scope.ServiceProvider; + //try + //{ + // var context = serviceProvider.GetService(); + // if (context!.Database.IsSqlServer()) + // await context.Database.MigrateAsync(); + // // Seed database here + //} + //catch (Exception e) + //{ + // Log.Fatal(e,"An error occurred while migrating the database."); + // throw; + //} await host.RunAsync(); } diff --git a/src/API/Routes/AuthRoutes.cs b/src/API/Routes/AuthRoutes.cs index 527b8db..c596b73 100644 --- a/src/API/Routes/AuthRoutes.cs +++ b/src/API/Routes/AuthRoutes.cs @@ -4,6 +4,7 @@ public static class AuthRoutes { public const string Register = "api/Auth/Register"; public const string Login = "api/Auth/Login"; + public const string LoginWithGoogle = "api/Auth/LoginWithGoogle"; public const string Refresh = "api/Auth/Refresh"; public const string LogOut = "api/Auth/LogOut"; } \ No newline at end of file diff --git a/src/API/appsettings.json b/src/API/appsettings.json index 07ef731..af54598 100644 --- a/src/API/appsettings.json +++ b/src/API/appsettings.json @@ -9,16 +9,21 @@ "AllowedHosts": "*", "UseInMemoryDatabase": true, "ConnectionStrings": { - "DefaultConnection": "Server=localhost;Database=UserDb;Trusted_Connection=True;MultipleActiveResultSets=true;" + "UsersDbReadOnlyConnection": "Server=localhost;Database=DevasevaUsers;uid=root;password=Test$123;Trusted_Connection=True;MultipleActiveResultSets=true;SSLMode=Required", + "UsersDbWriteConnection": "Server=localhost;Database=DevasevaUsers;uid=root;password=Test$123;Trusted_Connection=True;MultipleActiveResultSets=true;SSLMode=Required" }, "Cors":[], "LongRunningRequestTime":500, - "JwtSettings":{ - "AccessTokenSecret":"my_too_strong_access_secret_key", - "RefreshTokenSecret":"my_too_strong_refresh_secret_key", - "AccessTokenExpirationMinutes":0.3, - "RefreshTokenExpirationMinutes":60, - "Issuer":"https://localhost:5001", - "Audience":"https://localhost:5001" + "JwtSettings": { + "AccessTokenSecret": "NcRfUjXnZr4u7x!A%D*G-Kv8y/B?E(H+MbQeThVmw9z$C&F)J@agYq3t6UkXp2s5", + "RefreshTokenSecret": "dRgUkXp2s5v8y/B?D(G+KbPeShVmYq3t", + "AccessTokenExpirationMinutes": 10080, + "RefreshTokenExpirationMinutes": 100800, + "Issuer": "https://localhost:5001", + "Audience": "https://localhost:5001", + "Google": { + "ClientId": "916270787251-0fgln86oih6ef3u6ftdpgl6telrg3m16.apps.googleusercontent.com", + "ClientSecret": "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" + } } } diff --git a/src/Application/Application.csproj b/src/Application/Application.csproj index 0b56909..10f4b7a 100644 --- a/src/Application/Application.csproj +++ b/src/Application/Application.csproj @@ -11,6 +11,7 @@ + diff --git a/src/Application/Commands/Auth/GoogleLoginCommand.cs b/src/Application/Commands/Auth/GoogleLoginCommand.cs new file mode 100644 index 0000000..696e2e7 --- /dev/null +++ b/src/Application/Commands/Auth/GoogleLoginCommand.cs @@ -0,0 +1,57 @@ +using System.Threading; +using System.Threading.Tasks; +using Application.Common.DTOs.Auth; +using Application.Common.Interfaces; +using Application.Common.Models; +using Application.Common.Settings; +using Application.Common.Wrappers; +using Domain.Entities; +using Forbids; +using Google.Apis.Auth; +using Microsoft.AspNetCore.Identity; + +namespace Application.Commands.Auth; + +public record GoogleLoginCommand(GoogleLoginUserRequest LoginUserRequest) : IRequestWrapper; + +public class GoogleLoginCommandHandler : IHandlerWrapper +{ + private readonly IAuthenticateService _authenticateService; + private readonly IForbid _forbid; + private readonly JwtSettings jwtSettings; + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + + public GoogleLoginCommandHandler(UserManager userManager, SignInManager signInManager, + IAuthenticateService authenticateService, IForbid forbid, JwtSettings jwtSettings) + { + _userManager = userManager; + _signInManager = signInManager; + _authenticateService = authenticateService; + _forbid = forbid; + this.jwtSettings = jwtSettings; + } + + public async Task> Handle(GoogleLoginCommand request, CancellationToken cancellationToken) + { + var payload = await GoogleJsonWebSignature.ValidateAsync(request.LoginUserRequest.ExternalAccessToken, + new GoogleJsonWebSignature.ValidationSettings() + { + Audience = new[] { jwtSettings.Google.ClientId } + }); + + var user = await _userManager.FindByLoginAsync("Google", payload.Subject); + if (user == null) + { + user = new User() + { + Email = payload.Email + }; + + await _userManager.CreateAsync(user); + } + + await _signInManager.SignInAsync(user, false); + return Response.Success(await _authenticateService.Authenticate(user, cancellationToken)); + } +} \ No newline at end of file diff --git a/src/Application/Commands/Auth/LogOutCommand.cs b/src/Application/Commands/Auth/LogOutCommand.cs index ba90ca3..7e9a6aa 100644 --- a/src/Application/Commands/Auth/LogOutCommand.cs +++ b/src/Application/Commands/Auth/LogOutCommand.cs @@ -21,15 +21,20 @@ public class LogOutCommandHandler : IHandlerWrapper { private readonly IHttpContextAccessor _httpContextAccessor; private readonly SignInManager _signInManager; - private readonly IApplicationDbContext _context; + private readonly IUsersDbReadOnlyContext _readOnlyContext; + private readonly IUsersDbWriteContext writeContext; private readonly IForbid _forbid; - public LogOutCommandHandler(IHttpContextAccessor httpContextAccessor,SignInManager signInManager, - IApplicationDbContext context,IForbid forbid) + public LogOutCommandHandler(IHttpContextAccessor httpContextAccessor, + SignInManager signInManager, + IUsersDbReadOnlyContext readOnlyContext, + IUsersDbWriteContext writeContext, + IForbid forbid) { _httpContextAccessor = httpContextAccessor; _signInManager = signInManager; - _context = context; + _readOnlyContext = readOnlyContext; + this.writeContext = writeContext; _forbid = forbid; } @@ -38,11 +43,11 @@ public async Task> Handle(LogOutCommand request, CancellationTok var userId = _httpContextAccessor.HttpContext?.User.FindFirstValue("id"); _forbid.NullOrEmpty(userId, UserNotFoundException.Instance); await _signInManager.SignOutAsync(); - var refreshTokens = await _context.RefreshTokens + var refreshTokens = await _readOnlyContext.RefreshTokens .Where(x => x.UserId == userId) .ToListAsync(cancellationToken); - _context.RefreshTokens.RemoveRange(refreshTokens); - await _context.SaveChangesAsync(cancellationToken); + writeContext.RefreshTokens.RemoveRange(refreshTokens); + await writeContext.SaveChangesAsync(cancellationToken); return Response.Success(Unit.Value); } } \ No newline at end of file diff --git a/src/Application/Commands/Auth/RefreshCommand.cs b/src/Application/Commands/Auth/RefreshCommand.cs index 3e5c5a9..447170c 100644 --- a/src/Application/Commands/Auth/RefreshCommand.cs +++ b/src/Application/Commands/Auth/RefreshCommand.cs @@ -19,14 +19,20 @@ public class RefreshCommandHandler : IHandlerWrapper _userManager; - public RefreshCommandHandler(IRefreshTokenValidator refreshTokenValidator, IApplicationDbContext context, - UserManager userManager,IAuthenticateService authenticateService,IForbid forbid) + public RefreshCommandHandler(IRefreshTokenValidator refreshTokenValidator, + IUsersDbReadOnlyContext readOnlyContext, + IUsersDbWriteContext writeContext, + UserManager userManager, + IAuthenticateService authenticateService, + IForbid forbid) { _refreshTokenValidator = refreshTokenValidator; - _context = context; + this.readOnlyContext = readOnlyContext; + this.writeContext = writeContext; _userManager = userManager; _authenticateService = authenticateService; _forbid = forbid; @@ -38,11 +44,11 @@ public async Task> Handle(RefreshCommand request var isValidRefreshToken = _refreshTokenValidator.Validate(refreshRequest.RefreshToken); _forbid.False(isValidRefreshToken, InvalidRefreshTokenException.Instance); var refreshToken = - await _context.RefreshTokens.FirstOrDefaultAsync(x => x.Token == refreshRequest.RefreshToken, + await readOnlyContext.RefreshTokens.FirstOrDefaultAsync(x => x.Token == refreshRequest.RefreshToken, cancellationToken); _forbid.Null(refreshToken, InvalidRefreshTokenException.Instance); - _context.RefreshTokens.Remove(refreshToken); - await _context.SaveChangesAsync(cancellationToken); + writeContext.RefreshTokens.Remove(refreshToken); + await writeContext.SaveChangesAsync(cancellationToken); var user = await _userManager.FindByIdAsync(refreshToken.UserId); _forbid.Null(user, UserNotFoundException.Instance); diff --git a/src/Application/Commands/Auth/RegisterUserCommand.cs b/src/Application/Commands/Auth/RegisterUserCommand.cs index b85b570..e719140 100644 --- a/src/Application/Commands/Auth/RegisterUserCommand.cs +++ b/src/Application/Commands/Auth/RegisterUserCommand.cs @@ -23,7 +23,7 @@ public RegisterUserCommandHandler(UserManager userManager, IForbid forbid) public async Task> Handle(RegisterUserCommand request, CancellationToken cancellationToken) { - var user = new User(request.RegisterUserRequest.Email); + var user = new User() { Email= request.RegisterUserRequest.Email }; var createResult = await _userManager.CreateAsync(user, request.RegisterUserRequest.Password); _forbid.False(createResult.Succeeded, RegisterException.Instance); return Response.Success(Unit.Value); diff --git a/src/Application/Common/DTOs/Auth/GoogleLoginUserRequest.cs b/src/Application/Common/DTOs/Auth/GoogleLoginUserRequest.cs new file mode 100644 index 0000000..4f59d20 --- /dev/null +++ b/src/Application/Common/DTOs/Auth/GoogleLoginUserRequest.cs @@ -0,0 +1,12 @@ +using Application.Common.SwaggerSchemaFilters.Auth; +using Swashbuckle.AspNetCore.Annotations; + +namespace Application.Common.DTOs.Auth; + +[SwaggerSchemaFilter(typeof(GoogleLoginUserDtoSchemaFilter))] +[SwaggerSchema(Required = new[] { "User" })] +public class GoogleLoginUserRequest +{ + [SwaggerSchema(Required = new[] { "Google id token" })] + public string ExternalAccessToken { get; set; } +} \ No newline at end of file diff --git a/src/Application/Common/DTOs/Auth/LoginUserRequest.cs b/src/Application/Common/DTOs/Auth/LoginUserRequest.cs index ff56516..ab16dde 100644 --- a/src/Application/Common/DTOs/Auth/LoginUserRequest.cs +++ b/src/Application/Common/DTOs/Auth/LoginUserRequest.cs @@ -11,4 +11,4 @@ public class LoginUserRequest public string Email { get; set; } [SwaggerSchema(Required = new[] { "The User Password" })] public string Password { get; set; } -} \ No newline at end of file +} diff --git a/src/Application/Common/Interfaces/IUsersDbReadOnlyContext.cs b/src/Application/Common/Interfaces/IUsersDbReadOnlyContext.cs new file mode 100644 index 0000000..5d8bf7d --- /dev/null +++ b/src/Application/Common/Interfaces/IUsersDbReadOnlyContext.cs @@ -0,0 +1,13 @@ +using System.Threading; +using System.Threading.Tasks; +using Domain.Entities; +using Microsoft.EntityFrameworkCore; + +namespace Application.Common.Interfaces; + +public interface IUsersDbReadOnlyContext +{ + public DbSet RefreshTokens { get; set; } + + Task SaveChangesAsync(CancellationToken cancellationToken); +} diff --git a/src/Application/Common/Interfaces/IApplicationDbContext.cs b/src/Application/Common/Interfaces/IUsersDbWriteContext.cs similarity index 88% rename from src/Application/Common/Interfaces/IApplicationDbContext.cs rename to src/Application/Common/Interfaces/IUsersDbWriteContext.cs index 770b60e..e9cdc53 100644 --- a/src/Application/Common/Interfaces/IApplicationDbContext.cs +++ b/src/Application/Common/Interfaces/IUsersDbWriteContext.cs @@ -5,7 +5,7 @@ namespace Application.Common.Interfaces; -public interface IApplicationDbContext +public interface IUsersDbWriteContext { public DbSet RefreshTokens { get; set; } diff --git a/src/Application/Common/Settings/JwtSettings.cs b/src/Application/Common/Settings/JwtSettings.cs index facf577..30964bc 100644 --- a/src/Application/Common/Settings/JwtSettings.cs +++ b/src/Application/Common/Settings/JwtSettings.cs @@ -8,4 +8,12 @@ public class JwtSettings public double RefreshTokenExpirationMinutes { get; set; } public string Issuer { get; set; } public string Audience { get; set; } + + public GoogleSettings Google { get; set; } +} + +public class GoogleSettings +{ + public string ClientId { get; set; } + public string ClientSecret { get; set; } } \ No newline at end of file diff --git a/src/Application/Common/SwaggerSchemaFilters/Auth/GoogleLoginUserDtoSchemaFilter.cs b/src/Application/Common/SwaggerSchemaFilters/Auth/GoogleLoginUserDtoSchemaFilter.cs new file mode 100644 index 0000000..13d47e0 --- /dev/null +++ b/src/Application/Common/SwaggerSchemaFilters/Auth/GoogleLoginUserDtoSchemaFilter.cs @@ -0,0 +1,16 @@ +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace Application.Common.SwaggerSchemaFilters.Auth; + +public class GoogleLoginUserDtoSchemaFilter : ISchemaFilter +{ + public void Apply(OpenApiSchema schema, SchemaFilterContext context) + { + schema.Example = new OpenApiObject + { + ["ExternalAccessToken"] = new OpenApiString("fdsk3sdfdfp3dgf") + }; + } +} \ No newline at end of file diff --git a/src/Application/Common/SwaggerSchemaFilters/Auth/LoginUserDtoSchemaFilter.cs b/src/Application/Common/SwaggerSchemaFilters/Auth/LoginUserDtoSchemaFilter.cs index 2c95d13..7447874 100644 --- a/src/Application/Common/SwaggerSchemaFilters/Auth/LoginUserDtoSchemaFilter.cs +++ b/src/Application/Common/SwaggerSchemaFilters/Auth/LoginUserDtoSchemaFilter.cs @@ -14,4 +14,4 @@ public void Apply(OpenApiSchema schema, SchemaFilterContext context) ["Password"] = new OpenApiString("yourstringpassword") }; } -} \ No newline at end of file +} diff --git a/src/Domain/Entities/User.cs b/src/Domain/Entities/User.cs index 43e36dc..88184b3 100644 --- a/src/Domain/Entities/User.cs +++ b/src/Domain/Entities/User.cs @@ -4,15 +4,6 @@ namespace Domain.Entities; public class User : IdentityUser { - /// - /// Initializes a new instance of . - /// - /// The user email. - public User(string email) : base(email) - { - Email = email; - } - /// /// Gets or sets the Personal Number for this user. /// diff --git a/src/Infrastructure/Infrastructure.csproj b/src/Infrastructure/Infrastructure.csproj index c45e871..4c057ef 100644 --- a/src/Infrastructure/Infrastructure.csproj +++ b/src/Infrastructure/Infrastructure.csproj @@ -1,4 +1,4 @@ - + net6.0 @@ -8,7 +8,7 @@ - + diff --git a/src/Infrastructure/Persistence/Migrations/20210822072902_initial.Designer.cs b/src/Infrastructure/Persistence/Migrations/20210822072902_initial.Designer.cs index e5a3f07..e276519 100644 --- a/src/Infrastructure/Persistence/Migrations/20210822072902_initial.Designer.cs +++ b/src/Infrastructure/Persistence/Migrations/20210822072902_initial.Designer.cs @@ -1,294 +1,294 @@ -// -using System; -using Infrastructure.Persistence; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +//// +//using System; +//using Infrastructure.Persistence; +//using Microsoft.EntityFrameworkCore; +//using Microsoft.EntityFrameworkCore.Infrastructure; +//using Microsoft.EntityFrameworkCore.Metadata; +//using Microsoft.EntityFrameworkCore.Migrations; +//using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -namespace Infrastructure.Persistence.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20210822072902_initial")] - partial class initial - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("ProductVersion", "5.0.9") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); +//namespace Infrastructure.Persistence.Migrations +//{ +// [DbContext(typeof(ApplicationDbContext))] +// [Migration("20210822072902_initial")] +// partial class initial +// { +// protected override void BuildTargetModel(ModelBuilder modelBuilder) +// { +//#pragma warning disable 612, 618 +// modelBuilder +// .HasAnnotation("Relational:MaxIdentifierLength", 128) +// .HasAnnotation("ProductVersion", "5.0.9") +// .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - modelBuilder.Entity("Domain.Entities.User", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); +// modelBuilder.Entity("Domain.Entities.User", b => +// { +// b.Property("Id") +// .HasColumnType("nvarchar(450)"); - b.Property("AccessFailedCount") - .HasColumnType("int"); +// b.Property("AccessFailedCount") +// .HasColumnType("int"); - b.Property("Address") - .HasColumnType("nvarchar(max)"); +// b.Property("Address") +// .HasColumnType("nvarchar(max)"); - b.Property("Compensation") - .HasColumnType("float"); +// b.Property("Compensation") +// .HasColumnType("float"); - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("bit"); - - b.Property("Employed") - .HasColumnType("bit"); +// b.Property("ConcurrencyStamp") +// .IsConcurrencyToken() +// .HasColumnType("nvarchar(max)"); + +// b.Property("Email") +// .HasMaxLength(256) +// .HasColumnType("nvarchar(256)"); + +// b.Property("EmailConfirmed") +// .HasColumnType("bit"); + +// b.Property("Employed") +// .HasColumnType("bit"); - b.Property("IsMarried") - .HasColumnType("bit"); +// b.Property("IsMarried") +// .HasColumnType("bit"); - b.Property("LockoutEnabled") - .HasColumnType("bit"); +// b.Property("LockoutEnabled") +// .HasColumnType("bit"); - b.Property("LockoutEnd") - .HasColumnType("datetimeoffset"); +// b.Property("LockoutEnd") +// .HasColumnType("datetimeoffset"); - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); +// b.Property("NormalizedEmail") +// .HasMaxLength(256) +// .HasColumnType("nvarchar(256)"); - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); +// b.Property("NormalizedUserName") +// .HasMaxLength(256) +// .HasColumnType("nvarchar(256)"); - b.Property("PasswordHash") - .HasColumnType("nvarchar(max)"); +// b.Property("PasswordHash") +// .HasColumnType("nvarchar(max)"); - b.Property("PhoneNumber") - .HasColumnType("nvarchar(max)"); +// b.Property("PhoneNumber") +// .HasColumnType("nvarchar(max)"); - b.Property("PhoneNumberConfirmed") - .HasColumnType("bit"); +// b.Property("PhoneNumberConfirmed") +// .HasColumnType("bit"); - b.Property("Pin") - .HasMaxLength(11) - .HasColumnType("nchar(11)") - .IsFixedLength(true); +// b.Property("Pin") +// .HasMaxLength(11) +// .HasColumnType("nchar(11)") +// .IsFixedLength(true); - b.Property("SecurityStamp") - .HasColumnType("nvarchar(max)"); +// b.Property("SecurityStamp") +// .HasColumnType("nvarchar(max)"); - b.Property("TwoFactorEnabled") - .HasColumnType("bit"); +// b.Property("TwoFactorEnabled") +// .HasColumnType("bit"); - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); +// b.Property("UserName") +// .HasMaxLength(256) +// .HasColumnType("nvarchar(256)"); - b.HasKey("Id"); +// b.HasKey("Id"); - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); +// b.HasIndex("NormalizedEmail") +// .HasDatabaseName("EmailIndex"); - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex") - .HasFilter("[NormalizedUserName] IS NOT NULL"); +// b.HasIndex("NormalizedUserName") +// .IsUnique() +// .HasDatabaseName("UserNameIndex") +// .HasFilter("[NormalizedUserName] IS NOT NULL"); - b.HasIndex("Pin") - .IsUnique() - .HasFilter("[Pin] IS NOT NULL"); +// b.HasIndex("Pin") +// .IsUnique() +// .HasFilter("[Pin] IS NOT NULL"); - b.ToTable("AspNetUsers"); - }); +// b.ToTable("AspNetUsers"); +// }); - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); +// modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => +// { +// b.Property("Id") +// .HasColumnType("nvarchar(450)"); - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); +// b.Property("ConcurrencyStamp") +// .IsConcurrencyToken() +// .HasColumnType("nvarchar(max)"); - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); +// b.Property("Name") +// .HasMaxLength(256) +// .HasColumnType("nvarchar(256)"); - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); +// b.Property("NormalizedName") +// .HasMaxLength(256) +// .HasColumnType("nvarchar(256)"); - b.HasKey("Id"); +// b.HasKey("Id"); - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex") - .HasFilter("[NormalizedName] IS NOT NULL"); +// b.HasIndex("NormalizedName") +// .IsUnique() +// .HasDatabaseName("RoleNameIndex") +// .HasFilter("[NormalizedName] IS NOT NULL"); - b.ToTable("AspNetRoles"); - }); +// b.ToTable("AspNetRoles"); +// }); - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); +// modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => +// { +// b.Property("Id") +// .ValueGeneratedOnAdd() +// .HasColumnType("int") +// .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); +// b.Property("ClaimType") +// .HasColumnType("nvarchar(max)"); - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); +// b.Property("ClaimValue") +// .HasColumnType("nvarchar(max)"); - b.Property("RoleId") - .IsRequired() - .HasColumnType("nvarchar(450)"); +// b.Property("RoleId") +// .IsRequired() +// .HasColumnType("nvarchar(450)"); - b.HasKey("Id"); +// b.HasKey("Id"); - b.HasIndex("RoleId"); +// b.HasIndex("RoleId"); - b.ToTable("AspNetRoleClaims"); - }); +// b.ToTable("AspNetRoleClaims"); +// }); - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); +// modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => +// { +// b.Property("Id") +// .ValueGeneratedOnAdd() +// .HasColumnType("int") +// .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); +// b.Property("ClaimType") +// .HasColumnType("nvarchar(max)"); - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); +// b.Property("ClaimValue") +// .HasColumnType("nvarchar(max)"); - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); +// b.Property("UserId") +// .IsRequired() +// .HasColumnType("nvarchar(450)"); - b.HasKey("Id"); +// b.HasKey("Id"); - b.HasIndex("UserId"); +// b.HasIndex("UserId"); - b.ToTable("AspNetUserClaims"); - }); +// b.ToTable("AspNetUserClaims"); +// }); - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); +// modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => +// { +// b.Property("LoginProvider") +// .HasColumnType("nvarchar(450)"); - b.Property("ProviderKey") - .HasColumnType("nvarchar(450)"); +// b.Property("ProviderKey") +// .HasColumnType("nvarchar(450)"); - b.Property("ProviderDisplayName") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserLogins"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("RoleId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetUserRoles"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("Name") - .HasColumnType("nvarchar(450)"); - - b.Property("Value") - .HasColumnType("nvarchar(max)"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Domain.Entities.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Domain.Entities.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Domain.Entities.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); -#pragma warning restore 612, 618 - } - } -} +// b.Property("ProviderDisplayName") +// .HasColumnType("nvarchar(max)"); + +// b.Property("UserId") +// .IsRequired() +// .HasColumnType("nvarchar(450)"); + +// b.HasKey("LoginProvider", "ProviderKey"); + +// b.HasIndex("UserId"); + +// b.ToTable("AspNetUserLogins"); +// }); + +// modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => +// { +// b.Property("UserId") +// .HasColumnType("nvarchar(450)"); + +// b.Property("RoleId") +// .HasColumnType("nvarchar(450)"); + +// b.HasKey("UserId", "RoleId"); + +// b.HasIndex("RoleId"); + +// b.ToTable("AspNetUserRoles"); +// }); + +// modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => +// { +// b.Property("UserId") +// .HasColumnType("nvarchar(450)"); + +// b.Property("LoginProvider") +// .HasColumnType("nvarchar(450)"); + +// b.Property("Name") +// .HasColumnType("nvarchar(450)"); + +// b.Property("Value") +// .HasColumnType("nvarchar(max)"); + +// b.HasKey("UserId", "LoginProvider", "Name"); + +// b.ToTable("AspNetUserTokens"); +// }); + +// modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => +// { +// b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) +// .WithMany() +// .HasForeignKey("RoleId") +// .OnDelete(DeleteBehavior.Cascade) +// .IsRequired(); +// }); + +// modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => +// { +// b.HasOne("Domain.Entities.User", null) +// .WithMany() +// .HasForeignKey("UserId") +// .OnDelete(DeleteBehavior.Cascade) +// .IsRequired(); +// }); + +// modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => +// { +// b.HasOne("Domain.Entities.User", null) +// .WithMany() +// .HasForeignKey("UserId") +// .OnDelete(DeleteBehavior.Cascade) +// .IsRequired(); +// }); + +// modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => +// { +// b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) +// .WithMany() +// .HasForeignKey("RoleId") +// .OnDelete(DeleteBehavior.Cascade) +// .IsRequired(); + +// b.HasOne("Domain.Entities.User", null) +// .WithMany() +// .HasForeignKey("UserId") +// .OnDelete(DeleteBehavior.Cascade) +// .IsRequired(); +// }); + +// modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => +// { +// b.HasOne("Domain.Entities.User", null) +// .WithMany() +// .HasForeignKey("UserId") +// .OnDelete(DeleteBehavior.Cascade) +// .IsRequired(); +// }); +//#pragma warning restore 612, 618 +// } +// } +//} diff --git a/src/Infrastructure/Persistence/Migrations/ApplicationDbContextModelSnapshot.cs b/src/Infrastructure/Persistence/Migrations/ApplicationDbContextModelSnapshot.cs index 3eb6d00..1bfa350 100644 --- a/src/Infrastructure/Persistence/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/src/Infrastructure/Persistence/Migrations/ApplicationDbContextModelSnapshot.cs @@ -1,292 +1,292 @@ -// -using System; -using Infrastructure.Persistence; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +//// +//using System; +//using Infrastructure.Persistence; +//using Microsoft.EntityFrameworkCore; +//using Microsoft.EntityFrameworkCore.Infrastructure; +//using Microsoft.EntityFrameworkCore.Metadata; +//using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -namespace Infrastructure.Persistence.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - partial class ApplicationDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("ProductVersion", "5.0.9") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); +//namespace Infrastructure.Persistence.Migrations +//{ +// [DbContext(typeof(ApplicationDbContext))] +// partial class ApplicationDbContextModelSnapshot : ModelSnapshot +// { +// protected override void BuildModel(ModelBuilder modelBuilder) +// { +//#pragma warning disable 612, 618 +// modelBuilder +// .HasAnnotation("Relational:MaxIdentifierLength", 128) +// .HasAnnotation("ProductVersion", "5.0.9") +// .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - modelBuilder.Entity("Domain.Entities.User", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); +// modelBuilder.Entity("Domain.Entities.User", b => +// { +// b.Property("Id") +// .HasColumnType("nvarchar(450)"); - b.Property("AccessFailedCount") - .HasColumnType("int"); +// b.Property("AccessFailedCount") +// .HasColumnType("int"); - b.Property("Address") - .HasColumnType("nvarchar(max)"); +// b.Property("Address") +// .HasColumnType("nvarchar(max)"); - b.Property("Compensation") - .HasColumnType("float"); +// b.Property("Compensation") +// .HasColumnType("float"); - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); +// b.Property("ConcurrencyStamp") +// .IsConcurrencyToken() +// .HasColumnType("nvarchar(max)"); - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); +// b.Property("Email") +// .HasMaxLength(256) +// .HasColumnType("nvarchar(256)"); - b.Property("EmailConfirmed") - .HasColumnType("bit"); +// b.Property("EmailConfirmed") +// .HasColumnType("bit"); - b.Property("Employed") - .HasColumnType("bit"); +// b.Property("Employed") +// .HasColumnType("bit"); - b.Property("IsMarried") - .HasColumnType("bit"); +// b.Property("IsMarried") +// .HasColumnType("bit"); - b.Property("LockoutEnabled") - .HasColumnType("bit"); +// b.Property("LockoutEnabled") +// .HasColumnType("bit"); - b.Property("LockoutEnd") - .HasColumnType("datetimeoffset"); +// b.Property("LockoutEnd") +// .HasColumnType("datetimeoffset"); - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); +// b.Property("NormalizedEmail") +// .HasMaxLength(256) +// .HasColumnType("nvarchar(256)"); - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); +// b.Property("NormalizedUserName") +// .HasMaxLength(256) +// .HasColumnType("nvarchar(256)"); - b.Property("PasswordHash") - .HasColumnType("nvarchar(max)"); +// b.Property("PasswordHash") +// .HasColumnType("nvarchar(max)"); - b.Property("PhoneNumber") - .HasColumnType("nvarchar(max)"); +// b.Property("PhoneNumber") +// .HasColumnType("nvarchar(max)"); - b.Property("PhoneNumberConfirmed") - .HasColumnType("bit"); +// b.Property("PhoneNumberConfirmed") +// .HasColumnType("bit"); - b.Property("Pin") - .HasMaxLength(11) - .HasColumnType("nchar(11)") - .IsFixedLength(true); +// b.Property("Pin") +// .HasMaxLength(11) +// .HasColumnType("nchar(11)") +// .IsFixedLength(true); - b.Property("SecurityStamp") - .HasColumnType("nvarchar(max)"); +// b.Property("SecurityStamp") +// .HasColumnType("nvarchar(max)"); - b.Property("TwoFactorEnabled") - .HasColumnType("bit"); +// b.Property("TwoFactorEnabled") +// .HasColumnType("bit"); - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); +// b.Property("UserName") +// .HasMaxLength(256) +// .HasColumnType("nvarchar(256)"); - b.HasKey("Id"); +// b.HasKey("Id"); - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); +// b.HasIndex("NormalizedEmail") +// .HasDatabaseName("EmailIndex"); - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex") - .HasFilter("[NormalizedUserName] IS NOT NULL"); +// b.HasIndex("NormalizedUserName") +// .IsUnique() +// .HasDatabaseName("UserNameIndex") +// .HasFilter("[NormalizedUserName] IS NOT NULL"); - b.HasIndex("Pin") - .IsUnique() - .HasFilter("[Pin] IS NOT NULL"); +// b.HasIndex("Pin") +// .IsUnique() +// .HasFilter("[Pin] IS NOT NULL"); - b.ToTable("AspNetUsers"); - }); +// b.ToTable("AspNetUsers"); +// }); - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); +// modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => +// { +// b.Property("Id") +// .HasColumnType("nvarchar(450)"); - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); +// b.Property("ConcurrencyStamp") +// .IsConcurrencyToken() +// .HasColumnType("nvarchar(max)"); - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); +// b.Property("Name") +// .HasMaxLength(256) +// .HasColumnType("nvarchar(256)"); - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); +// b.Property("NormalizedName") +// .HasMaxLength(256) +// .HasColumnType("nvarchar(256)"); - b.HasKey("Id"); +// b.HasKey("Id"); - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex") - .HasFilter("[NormalizedName] IS NOT NULL"); +// b.HasIndex("NormalizedName") +// .IsUnique() +// .HasDatabaseName("RoleNameIndex") +// .HasFilter("[NormalizedName] IS NOT NULL"); - b.ToTable("AspNetRoles"); - }); +// b.ToTable("AspNetRoles"); +// }); - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); +// modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => +// { +// b.Property("Id") +// .ValueGeneratedOnAdd() +// .HasColumnType("int") +// .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); +// b.Property("ClaimType") +// .HasColumnType("nvarchar(max)"); - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); +// b.Property("ClaimValue") +// .HasColumnType("nvarchar(max)"); - b.Property("RoleId") - .IsRequired() - .HasColumnType("nvarchar(450)"); +// b.Property("RoleId") +// .IsRequired() +// .HasColumnType("nvarchar(450)"); - b.HasKey("Id"); +// b.HasKey("Id"); - b.HasIndex("RoleId"); +// b.HasIndex("RoleId"); - b.ToTable("AspNetRoleClaims"); - }); +// b.ToTable("AspNetRoleClaims"); +// }); - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); +// modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => +// { +// b.Property("Id") +// .ValueGeneratedOnAdd() +// .HasColumnType("int") +// .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); +// b.Property("ClaimType") +// .HasColumnType("nvarchar(max)"); - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); +// b.Property("ClaimValue") +// .HasColumnType("nvarchar(max)"); - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); +// b.Property("UserId") +// .IsRequired() +// .HasColumnType("nvarchar(450)"); - b.HasKey("Id"); +// b.HasKey("Id"); - b.HasIndex("UserId"); +// b.HasIndex("UserId"); - b.ToTable("AspNetUserClaims"); - }); +// b.ToTable("AspNetUserClaims"); +// }); - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); +// modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => +// { +// b.Property("LoginProvider") +// .HasColumnType("nvarchar(450)"); - b.Property("ProviderKey") - .HasColumnType("nvarchar(450)"); +// b.Property("ProviderKey") +// .HasColumnType("nvarchar(450)"); - b.Property("ProviderDisplayName") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserLogins"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("RoleId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetUserRoles"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("Name") - .HasColumnType("nvarchar(450)"); - - b.Property("Value") - .HasColumnType("nvarchar(max)"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Domain.Entities.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Domain.Entities.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Domain.Entities.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Domain.Entities.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); -#pragma warning restore 612, 618 - } - } -} +// b.Property("ProviderDisplayName") +// .HasColumnType("nvarchar(max)"); + +// b.Property("UserId") +// .IsRequired() +// .HasColumnType("nvarchar(450)"); + +// b.HasKey("LoginProvider", "ProviderKey"); + +// b.HasIndex("UserId"); + +// b.ToTable("AspNetUserLogins"); +// }); + +// modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => +// { +// b.Property("UserId") +// .HasColumnType("nvarchar(450)"); + +// b.Property("RoleId") +// .HasColumnType("nvarchar(450)"); + +// b.HasKey("UserId", "RoleId"); + +// b.HasIndex("RoleId"); + +// b.ToTable("AspNetUserRoles"); +// }); + +// modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => +// { +// b.Property("UserId") +// .HasColumnType("nvarchar(450)"); + +// b.Property("LoginProvider") +// .HasColumnType("nvarchar(450)"); + +// b.Property("Name") +// .HasColumnType("nvarchar(450)"); + +// b.Property("Value") +// .HasColumnType("nvarchar(max)"); + +// b.HasKey("UserId", "LoginProvider", "Name"); + +// b.ToTable("AspNetUserTokens"); +// }); + +// modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => +// { +// b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) +// .WithMany() +// .HasForeignKey("RoleId") +// .OnDelete(DeleteBehavior.Cascade) +// .IsRequired(); +// }); + +// modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => +// { +// b.HasOne("Domain.Entities.User", null) +// .WithMany() +// .HasForeignKey("UserId") +// .OnDelete(DeleteBehavior.Cascade) +// .IsRequired(); +// }); + +// modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => +// { +// b.HasOne("Domain.Entities.User", null) +// .WithMany() +// .HasForeignKey("UserId") +// .OnDelete(DeleteBehavior.Cascade) +// .IsRequired(); +// }); + +// modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => +// { +// b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) +// .WithMany() +// .HasForeignKey("RoleId") +// .OnDelete(DeleteBehavior.Cascade) +// .IsRequired(); + +// b.HasOne("Domain.Entities.User", null) +// .WithMany() +// .HasForeignKey("UserId") +// .OnDelete(DeleteBehavior.Cascade) +// .IsRequired(); +// }); + +// modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => +// { +// b.HasOne("Domain.Entities.User", null) +// .WithMany() +// .HasForeignKey("UserId") +// .OnDelete(DeleteBehavior.Cascade) +// .IsRequired(); +// }); +//#pragma warning restore 612, 618 +// } +// } +//} diff --git a/src/Infrastructure/Persistence/UsersDbReadOnlyContext.cs b/src/Infrastructure/Persistence/UsersDbReadOnlyContext.cs new file mode 100644 index 0000000..3d17d65 --- /dev/null +++ b/src/Infrastructure/Persistence/UsersDbReadOnlyContext.cs @@ -0,0 +1,44 @@ +using System; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Application.Common.Interfaces; +using Domain.Common; +using Domain.Entities; +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; + +namespace Infrastructure.Persistence; + +public class UsersDbReadOnlyContext : IdentityDbContext,IUsersDbReadOnlyContext +{ + public UsersDbReadOnlyContext(DbContextOptions options) : base(options) { } + + public override async Task SaveChangesAsync(CancellationToken cancellationToken = new()) + { + foreach (var entry in ChangeTracker.Entries()) + { + switch (entry.State) + { + case EntityState.Added: + entry.Entity.CreatedBy = "API"; + entry.Entity.Created = DateTime.Now; + entry.Entity.LastModified = DateTime.Now; + entry.Entity.LastModifiedBy = "API"; + break; + case EntityState.Modified: + entry.Entity.LastModified = DateTime.Now; + entry.Entity.LastModifiedBy = "API"; + break; + } + } + return await base.SaveChangesAsync(cancellationToken); + } + + public DbSet RefreshTokens { get; set; } + protected override void OnModelCreating(ModelBuilder builder) + { + builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); + base.OnModelCreating(builder); + } +} diff --git a/src/Infrastructure/Persistence/ApplicationDbContext.cs b/src/Infrastructure/Persistence/UsersDbWriteContext.cs similarity index 86% rename from src/Infrastructure/Persistence/ApplicationDbContext.cs rename to src/Infrastructure/Persistence/UsersDbWriteContext.cs index 3859b18..12b8655 100644 --- a/src/Infrastructure/Persistence/ApplicationDbContext.cs +++ b/src/Infrastructure/Persistence/UsersDbWriteContext.cs @@ -10,11 +10,11 @@ namespace Infrastructure.Persistence; -public class ApplicationDbContext : IdentityDbContext,IApplicationDbContext +public class UsersDbWriteContext : IdentityDbContext, IUsersDbWriteContext { - public ApplicationDbContext(DbContextOptions options) : base(options) { } + public UsersDbWriteContext(DbContextOptions options) : base(options) { } - public override async Task SaveChangesAsync(CancellationToken cancellationToken = new ()) + public override async Task SaveChangesAsync(CancellationToken cancellationToken = new()) { foreach (var entry in ChangeTracker.Entries()) { diff --git a/src/Infrastructure/ServiceCollectionExtension.cs b/src/Infrastructure/ServiceCollectionExtension.cs index a0b5f20..4b72430 100644 --- a/src/Infrastructure/ServiceCollectionExtension.cs +++ b/src/Infrastructure/ServiceCollectionExtension.cs @@ -23,20 +23,23 @@ public static class ServiceCollectionExtension public static void AddInfrastructure(this IServiceCollection services,IConfiguration configuration) { if (Convert.ToBoolean(configuration.GetValue("UseInMemoryDatabase"))) - services.AddDbContext(options => options.UseInMemoryDatabase("TestDb")); + { + services.AddDbContext(options => options.UseInMemoryDatabase("TestDb")); + services.AddDbContext(options => options.UseInMemoryDatabase("TestDb")); + } else { - services.AddDbContext(options => + services.AddDbContext(options => + { + var connectionString = configuration.GetConnectionString("UsersDbReadOnlyConnection"); + options.UseMySql(connectionString, + ServerVersion.AutoDetect(connectionString)); + }); + services.AddDbContext(options => { - options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"), - builder => - { - builder.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName); - //EF allows you to specify that a given LINQ query should be split into multiple SQL queries. - //Instead of JOINs, split queries generate an additional SQL query for each included collection navigation - //More about that: https://docs.microsoft.com/en-us/ef/core/querying/single-split-queries - builder.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery); - }); + var connectionString = configuration.GetConnectionString("UsersDbWriteConnection"); + options.UseMySql(connectionString, + ServerVersion.AutoDetect(connectionString)); }); } @@ -48,10 +51,12 @@ public static void AddInfrastructure(this IServiceCollection services,IConfigura options.Password.RequireUppercase = false; options.User.RequireUniqueEmail = true; }) - .AddEntityFrameworkStores() + .AddEntityFrameworkStores() + .AddEntityFrameworkStores() .AddDefaultTokenProviders(); - services.AddScoped(x => x.GetService()!); + services.AddScoped(x => x.GetService()!); + services.AddScoped(x => x.GetService()!); services.AddScoped(); services.AddScoped(); services.AddScoped(); diff --git a/src/Infrastructure/Services/AuthenticateService.cs b/src/Infrastructure/Services/AuthenticateService.cs index 8a123ea..6485f4f 100644 --- a/src/Infrastructure/Services/AuthenticateService.cs +++ b/src/Infrastructure/Services/AuthenticateService.cs @@ -10,8 +10,10 @@ public class AuthenticateService : IAuthenticateService { private readonly IAccessTokenService _accessTokenService; private readonly IRefreshTokenService _refreshTokenService; - private readonly IApplicationDbContext _context; - public AuthenticateService(IAccessTokenService accessTokenService, IRefreshTokenService refreshTokenService, IApplicationDbContext context) + private readonly IUsersDbWriteContext _context; + public AuthenticateService(IAccessTokenService accessTokenService, + IRefreshTokenService refreshTokenService, + IUsersDbWriteContext context) { _accessTokenService = accessTokenService; _refreshTokenService = refreshTokenService; From 610e2e31bfb09c7f478125d649a7b6b18376ec79 Mon Sep 17 00:00:00 2001 From: Sunil Buddala Date: Sun, 12 Jun 2022 13:24:25 +1200 Subject: [PATCH 2/3] working google sign in --- src/API/appsettings.Development.json | 15 +----- src/API/appsettings.json | 12 ++--- .../Commands/Auth/GoogleLoginCommand.cs | 7 ++- .../Commands/Auth/RegisterUserCommand.cs | 6 +-- .../Interfaces/IUsersDbReadOnlyContext.cs | 6 +-- src/Domain/Entities/User.cs | 46 +++++++++---------- .../Configuration/UserConfiguration.cs | 4 +- .../Persistence/UsersDbReadOnlyContext.cs | 27 +---------- 8 files changed, 43 insertions(+), 80 deletions(-) diff --git a/src/API/appsettings.Development.json b/src/API/appsettings.Development.json index 07ef731..97d1e19 100644 --- a/src/API/appsettings.Development.json +++ b/src/API/appsettings.Development.json @@ -7,18 +7,7 @@ } }, "AllowedHosts": "*", - "UseInMemoryDatabase": true, - "ConnectionStrings": { - "DefaultConnection": "Server=localhost;Database=UserDb;Trusted_Connection=True;MultipleActiveResultSets=true;" - }, + "UseInMemoryDatabase": false, "Cors":[], - "LongRunningRequestTime":500, - "JwtSettings":{ - "AccessTokenSecret":"my_too_strong_access_secret_key", - "RefreshTokenSecret":"my_too_strong_refresh_secret_key", - "AccessTokenExpirationMinutes":0.3, - "RefreshTokenExpirationMinutes":60, - "Issuer":"https://localhost:5001", - "Audience":"https://localhost:5001" - } + "LongRunningRequestTime":500 } diff --git a/src/API/appsettings.json b/src/API/appsettings.json index af54598..256d9a0 100644 --- a/src/API/appsettings.json +++ b/src/API/appsettings.json @@ -7,23 +7,23 @@ } }, "AllowedHosts": "*", - "UseInMemoryDatabase": true, + "UseInMemoryDatabase": false, "ConnectionStrings": { - "UsersDbReadOnlyConnection": "Server=localhost;Database=DevasevaUsers;uid=root;password=Test$123;Trusted_Connection=True;MultipleActiveResultSets=true;SSLMode=Required", - "UsersDbWriteConnection": "Server=localhost;Database=DevasevaUsers;uid=root;password=Test$123;Trusted_Connection=True;MultipleActiveResultSets=true;SSLMode=Required" + "UsersDbReadOnlyConnection": "Server=localhost;Database=DevasevaUsers;uid=root;password=Test$123;SSLMode=Required", + "UsersDbWriteConnection": "Server=localhost;Database=DevasevaUsers;uid=root;password=Test$123;SSLMode=Required" }, "Cors":[], "LongRunningRequestTime":500, "JwtSettings": { - "AccessTokenSecret": "NcRfUjXnZr4u7x!A%D*G-Kv8y/B?E(H+MbQeThVmw9z$C&F)J@agYq3t6UkXp2s5", + "AccessTokenSecret": "gUkXp2s5v8y/B?E(H+MbQeThVmYq3t6w9z$C&F)J@NcRfUjXnZr4u7x!A%D*G-Ka", "RefreshTokenSecret": "dRgUkXp2s5v8y/B?D(G+KbPeShVmYq3t", "AccessTokenExpirationMinutes": 10080, "RefreshTokenExpirationMinutes": 100800, "Issuer": "https://localhost:5001", "Audience": "https://localhost:5001", "Google": { - "ClientId": "916270787251-0fgln86oih6ef3u6ftdpgl6telrg3m16.apps.googleusercontent.com", - "ClientSecret": "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" + "ClientId": "833381955269-73058j9r1cmcp76vb83ijgu7tnvgn0ep.apps.googleusercontent.com", + "ClientSecret": "zjg7E6gTh_RYw_q6sTj4mShm" } } } diff --git a/src/Application/Commands/Auth/GoogleLoginCommand.cs b/src/Application/Commands/Auth/GoogleLoginCommand.cs index 696e2e7..b6fd0d3 100644 --- a/src/Application/Commands/Auth/GoogleLoginCommand.cs +++ b/src/Application/Commands/Auth/GoogleLoginCommand.cs @@ -21,6 +21,7 @@ public class GoogleLoginCommandHandler : IHandlerWrapper _userManager; private readonly SignInManager _signInManager; + private const string GoogleProviderKey = "Google"; public GoogleLoginCommandHandler(UserManager userManager, SignInManager signInManager, IAuthenticateService authenticateService, IForbid forbid, JwtSettings jwtSettings) @@ -40,15 +41,17 @@ public async Task> Handle(GoogleLoginCommand req Audience = new[] { jwtSettings.Google.ClientId } }); - var user = await _userManager.FindByLoginAsync("Google", payload.Subject); + var user = await _userManager.FindByLoginAsync(GoogleProviderKey, payload.Subject); if (user == null) { user = new User() { - Email = payload.Email + Email = payload.Email, + UserName = payload.Email }; await _userManager.CreateAsync(user); + await _userManager.AddLoginAsync(user, new UserLoginInfo(GoogleProviderKey, payload.Subject, GoogleProviderKey.ToUpperInvariant())); } await _signInManager.SignInAsync(user, false); diff --git a/src/Application/Commands/Auth/RegisterUserCommand.cs b/src/Application/Commands/Auth/RegisterUserCommand.cs index e719140..3ef6d37 100644 --- a/src/Application/Commands/Auth/RegisterUserCommand.cs +++ b/src/Application/Commands/Auth/RegisterUserCommand.cs @@ -13,17 +13,17 @@ namespace Application.Commands.Auth; public record RegisterUserCommand(RegisterUserRequest RegisterUserRequest) : IRequestWrapper; -public class RegisterUserCommandHandler : IHandlerWrapper +public class RegisterUserCommandHandler : IHandlerWrapper { private readonly UserManager _userManager; private readonly IForbid _forbid; public RegisterUserCommandHandler(UserManager userManager, IForbid forbid) => (_userManager, _forbid) = (userManager, forbid); - + public async Task> Handle(RegisterUserCommand request, CancellationToken cancellationToken) { - var user = new User() { Email= request.RegisterUserRequest.Email }; + var user = new User() { Email = request.RegisterUserRequest.Email, UserName = request.RegisterUserRequest.Email }; var createResult = await _userManager.CreateAsync(user, request.RegisterUserRequest.Password); _forbid.False(createResult.Succeeded, RegisterException.Instance); return Response.Success(Unit.Value); diff --git a/src/Application/Common/Interfaces/IUsersDbReadOnlyContext.cs b/src/Application/Common/Interfaces/IUsersDbReadOnlyContext.cs index 5d8bf7d..71edda4 100644 --- a/src/Application/Common/Interfaces/IUsersDbReadOnlyContext.cs +++ b/src/Application/Common/Interfaces/IUsersDbReadOnlyContext.cs @@ -1,6 +1,4 @@ -using System.Threading; -using System.Threading.Tasks; -using Domain.Entities; +using Domain.Entities; using Microsoft.EntityFrameworkCore; namespace Application.Common.Interfaces; @@ -8,6 +6,4 @@ namespace Application.Common.Interfaces; public interface IUsersDbReadOnlyContext { public DbSet RefreshTokens { get; set; } - - Task SaveChangesAsync(CancellationToken cancellationToken); } diff --git a/src/Domain/Entities/User.cs b/src/Domain/Entities/User.cs index 88184b3..72454b6 100644 --- a/src/Domain/Entities/User.cs +++ b/src/Domain/Entities/User.cs @@ -4,27 +4,27 @@ namespace Domain.Entities; public class User : IdentityUser { - /// - /// Gets or sets the Personal Number for this user. - /// - public string? Pin { get; set; } - /// - /// Gets or sets a flag indicating user is married or not. - /// - /// True if user is married, otherwise false. - public bool IsMarried { get; set; } - /// - /// Gets or sets a flag indicating user is employed or not. - /// - /// True if user is employed, otherwise false. - public bool Employed { get; set; } - /// - /// Gets or sets the compensation for this user. - /// - /// Has value if user is employed. - public double? Compensation { get; set; } - /// - /// Gets or sets the physical address for this user. - /// - public string? Address { get; set; } + ///// + ///// Gets or sets the Personal Number for this user. + ///// + //public string? Pin { get; set; } + ///// + ///// Gets or sets a flag indicating user is married or not. + ///// + ///// True if user is married, otherwise false. + //public bool IsMarried { get; set; } + ///// + ///// Gets or sets a flag indicating user is employed or not. + ///// + ///// True if user is employed, otherwise false. + //public bool Employed { get; set; } + ///// + ///// Gets or sets the compensation for this user. + ///// + ///// Has value if user is employed. + //public double? Compensation { get; set; } + ///// + ///// Gets or sets the physical address for this user. + ///// + //public string? Address { get; set; } } \ No newline at end of file diff --git a/src/Infrastructure/Persistence/Configuration/UserConfiguration.cs b/src/Infrastructure/Persistence/Configuration/UserConfiguration.cs index f05ec76..cbb8ee9 100644 --- a/src/Infrastructure/Persistence/Configuration/UserConfiguration.cs +++ b/src/Infrastructure/Persistence/Configuration/UserConfiguration.cs @@ -8,7 +8,7 @@ public class UserConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { - builder.HasIndex(x => x.Pin).IsUnique(); - builder.Property(x => x.Pin).HasMaxLength(11).IsFixedLength(); + //builder.HasIndex(x => x.Pin).IsUnique(); + //builder.Property(x => x.Pin).HasMaxLength(11).IsFixedLength(); } } \ No newline at end of file diff --git a/src/Infrastructure/Persistence/UsersDbReadOnlyContext.cs b/src/Infrastructure/Persistence/UsersDbReadOnlyContext.cs index 3d17d65..b6889f6 100644 --- a/src/Infrastructure/Persistence/UsersDbReadOnlyContext.cs +++ b/src/Infrastructure/Persistence/UsersDbReadOnlyContext.cs @@ -1,9 +1,5 @@ -using System; -using System.Reflection; -using System.Threading; -using System.Threading.Tasks; +using System.Reflection; using Application.Common.Interfaces; -using Domain.Common; using Domain.Entities; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; @@ -14,27 +10,6 @@ public class UsersDbReadOnlyContext : IdentityDbContext,IUsersDbReadOnlyCo { public UsersDbReadOnlyContext(DbContextOptions options) : base(options) { } - public override async Task SaveChangesAsync(CancellationToken cancellationToken = new()) - { - foreach (var entry in ChangeTracker.Entries()) - { - switch (entry.State) - { - case EntityState.Added: - entry.Entity.CreatedBy = "API"; - entry.Entity.Created = DateTime.Now; - entry.Entity.LastModified = DateTime.Now; - entry.Entity.LastModifiedBy = "API"; - break; - case EntityState.Modified: - entry.Entity.LastModified = DateTime.Now; - entry.Entity.LastModifiedBy = "API"; - break; - } - } - return await base.SaveChangesAsync(cancellationToken); - } - public DbSet RefreshTokens { get; set; } protected override void OnModelCreating(ModelBuilder builder) { From 4ff0ef90ccf5f7c0db73bb41651d3e27adb06e99 Mon Sep 17 00:00:00 2001 From: Sunil Buddala Date: Sun, 12 Jun 2022 15:43:43 +1200 Subject: [PATCH 3/3] login with phone number --- src/API/Endpoints/Auth/LoginWithPhone.cs | 35 ++++++++++++ src/API/Routes/AuthRoutes.cs | 1 + .../Commands/Auth/PhoneLoginUserCommand.cs | 57 +++++++++++++++++++ .../DTOs/Auth/GoogleLoginUserRequest.cs | 1 - .../Common/DTOs/Auth/PhoneLoginUserRequest.cs | 11 ++++ .../Auth/PhoneLoginUserDtoSchemaFilter.cs | 17 ++++++ .../ServiceCollectionExtension.cs | 2 +- .../Services/AccessTokenService.cs | 12 +++- 8 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 src/API/Endpoints/Auth/LoginWithPhone.cs create mode 100644 src/Application/Commands/Auth/PhoneLoginUserCommand.cs create mode 100644 src/Application/Common/DTOs/Auth/PhoneLoginUserRequest.cs create mode 100644 src/Application/Common/SwaggerSchemaFilters/Auth/PhoneLoginUserDtoSchemaFilter.cs diff --git a/src/API/Endpoints/Auth/LoginWithPhone.cs b/src/API/Endpoints/Auth/LoginWithPhone.cs new file mode 100644 index 0000000..b54bd29 --- /dev/null +++ b/src/API/Endpoints/Auth/LoginWithPhone.cs @@ -0,0 +1,35 @@ +using System.Threading; +using System.Threading.Tasks; +using API.Routes; +using Application.Commands.Auth; +using Application.Common.DTOs.Auth; +using Application.Common.Models; +using Ardalis.ApiEndpoints; +using MediatR; +using Microsoft.AspNetCore.Mvc; +using Swashbuckle.AspNetCore.Annotations; + +namespace API.Endpoints.Auth; + +[Route(AuthRoutes.LoginWithPhone)] +public class LoginWithPhone : BaseAsyncEndpoint + .WithRequest + .WithResponse> +{ + private readonly IMediator _mediator; + + public LoginWithPhone(IMediator mediator) => _mediator = mediator; + + [HttpPost, + SwaggerOperation(Description = "Signs in or signs up provided user and return token", + Summary = "Sign In or Sing Up wiht phone number", + OperationId = "Auth.LoginWithPhone", + Tags = new[] { "Auth" }), + SwaggerResponse(200, "User logged in successfully", typeof(IResponse)), + Produces("application/json"), Consumes("application/json")] + public override async Task>> HandleAsync( + [SwaggerRequestBody("User phone login payload", Required = true)] + PhoneLoginUserRequest loginUserRequest, + CancellationToken cancellationToken = new()) => + Ok(await _mediator.Send(new PhoneLoginUserCommand(loginUserRequest), cancellationToken)); +} \ No newline at end of file diff --git a/src/API/Routes/AuthRoutes.cs b/src/API/Routes/AuthRoutes.cs index c596b73..2c71799 100644 --- a/src/API/Routes/AuthRoutes.cs +++ b/src/API/Routes/AuthRoutes.cs @@ -5,6 +5,7 @@ public static class AuthRoutes public const string Register = "api/Auth/Register"; public const string Login = "api/Auth/Login"; public const string LoginWithGoogle = "api/Auth/LoginWithGoogle"; + public const string LoginWithPhone = "api/Auth/LoginWithPhone"; public const string Refresh = "api/Auth/Refresh"; public const string LogOut = "api/Auth/LogOut"; } \ No newline at end of file diff --git a/src/Application/Commands/Auth/PhoneLoginUserCommand.cs b/src/Application/Commands/Auth/PhoneLoginUserCommand.cs new file mode 100644 index 0000000..bd43964 --- /dev/null +++ b/src/Application/Commands/Auth/PhoneLoginUserCommand.cs @@ -0,0 +1,57 @@ +using System.Threading; +using System.Threading.Tasks; +using Application.Common.DTOs.Auth; +using Application.Common.Interfaces; +using Application.Common.Models; +using Application.Common.Wrappers; +using Domain.Entities; +using Domain.Exceptions; +using Forbids; +using Microsoft.AspNetCore.Identity; + +namespace Application.Commands.Auth; + +public record PhoneLoginUserCommand(PhoneLoginUserRequest LoginUserRequest) : IRequestWrapper; + +public class PhoneLoginUserCommandHandler : IHandlerWrapper +{ + private readonly IAuthenticateService _authenticateService; + private readonly IForbid _forbid; + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + + public PhoneLoginUserCommandHandler(UserManager userManager, + SignInManager signInManager, + IAuthenticateService authenticateService, + IForbid forbid) + { + _userManager = userManager; + _signInManager = signInManager; + _authenticateService = authenticateService; + _forbid = forbid; + } + + public async Task> Handle(PhoneLoginUserCommand request, CancellationToken cancellationToken) + { + var user = new User + { + PhoneNumber = request.LoginUserRequest.Phone, + UserName = request.LoginUserRequest.Phone, + PhoneNumberConfirmed = true + }; + + var existingUser = await _userManager.FindByNameAsync(request.LoginUserRequest.Phone); + if (existingUser == null) + { + var createResult = await _userManager.CreateAsync(user); + _forbid.False(createResult.Succeeded, RegisterException.Instance); + } + else + { + user = existingUser; + } + + await _signInManager.SignInAsync(user, false); + return Response.Success(await _authenticateService.Authenticate(user, cancellationToken)); + } +} diff --git a/src/Application/Common/DTOs/Auth/GoogleLoginUserRequest.cs b/src/Application/Common/DTOs/Auth/GoogleLoginUserRequest.cs index 4f59d20..d53890f 100644 --- a/src/Application/Common/DTOs/Auth/GoogleLoginUserRequest.cs +++ b/src/Application/Common/DTOs/Auth/GoogleLoginUserRequest.cs @@ -4,7 +4,6 @@ namespace Application.Common.DTOs.Auth; [SwaggerSchemaFilter(typeof(GoogleLoginUserDtoSchemaFilter))] -[SwaggerSchema(Required = new[] { "User" })] public class GoogleLoginUserRequest { [SwaggerSchema(Required = new[] { "Google id token" })] diff --git a/src/Application/Common/DTOs/Auth/PhoneLoginUserRequest.cs b/src/Application/Common/DTOs/Auth/PhoneLoginUserRequest.cs new file mode 100644 index 0000000..7df76a8 --- /dev/null +++ b/src/Application/Common/DTOs/Auth/PhoneLoginUserRequest.cs @@ -0,0 +1,11 @@ +using Application.Common.SwaggerSchemaFilters.Auth; +using Swashbuckle.AspNetCore.Annotations; + +namespace Application.Common.DTOs.Auth; + +[SwaggerSchemaFilter(typeof(PhoneLoginUserDtoSchemaFilter))] +public class PhoneLoginUserRequest +{ + [SwaggerSchema(Required = new[] { "The User Phone" })] + public string Phone { get; set; } +} diff --git a/src/Application/Common/SwaggerSchemaFilters/Auth/PhoneLoginUserDtoSchemaFilter.cs b/src/Application/Common/SwaggerSchemaFilters/Auth/PhoneLoginUserDtoSchemaFilter.cs new file mode 100644 index 0000000..3e8c206 --- /dev/null +++ b/src/Application/Common/SwaggerSchemaFilters/Auth/PhoneLoginUserDtoSchemaFilter.cs @@ -0,0 +1,17 @@ +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using System.Collections.Generic; + +namespace Application.Common.SwaggerSchemaFilters.Auth; + +public class PhoneLoginUserDtoSchemaFilter : ISchemaFilter +{ + public void Apply(OpenApiSchema schema, SchemaFilterContext context) + { + schema.Example = new OpenApiObject + { + ["Phone"] = new OpenApiString("+911234567890") + }; + } +} diff --git a/src/Infrastructure/ServiceCollectionExtension.cs b/src/Infrastructure/ServiceCollectionExtension.cs index 4b72430..e33d824 100644 --- a/src/Infrastructure/ServiceCollectionExtension.cs +++ b/src/Infrastructure/ServiceCollectionExtension.cs @@ -49,7 +49,7 @@ public static void AddInfrastructure(this IServiceCollection services,IConfigura options.Password.RequireDigit = false; options.Password.RequireNonAlphanumeric = false; options.Password.RequireUppercase = false; - options.User.RequireUniqueEmail = true; + options.User.RequireUniqueEmail = false; }) .AddEntityFrameworkStores() .AddEntityFrameworkStores() diff --git a/src/Infrastructure/Services/AccessTokenService.cs b/src/Infrastructure/Services/AccessTokenService.cs index 7a0c36a..8e3cb8b 100644 --- a/src/Infrastructure/Services/AccessTokenService.cs +++ b/src/Infrastructure/Services/AccessTokenService.cs @@ -19,9 +19,19 @@ public string Generate(User user) List claims = new() { new Claim("id", user.Id), - new Claim(ClaimTypes.Email, user.Email), new Claim(ClaimTypes.Name, user.UserName), }; + + if(!string.IsNullOrEmpty(user.Email)) + { + claims.Add(new Claim(ClaimTypes.Email, user.Email)); + } + + if (!string.IsNullOrEmpty(user.PhoneNumber)) + { + claims.Add(new Claim("PhoneNumber", user.PhoneNumber)); + } + return _tokenGenerator.Generate(_jwtSettings.AccessTokenSecret, _jwtSettings.Issuer, _jwtSettings.Audience, _jwtSettings.AccessTokenExpirationMinutes, claims); }