Skip to content

Commit 0763ee7

Browse files
committed
refactor: reorganize domain structure to use Entities.Auth namespace for users, roles, permissions, and OTP codes
1 parent 047c5b5 commit 0763ee7

78 files changed

Lines changed: 762 additions & 211 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/backend/src/Application/Abstractions/Authentication/ITokenProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using Domain.Users;
1+
using Domain.Entities.Auth.Users;
22

33
namespace Application.Abstractions.Authentication;
44

apps/backend/src/Application/Abstractions/Data/IApplicationDbContext.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
using Domain.Joins;
2-
using Domain.OtpCodes;
3-
using Domain.Permissions;
4-
using Domain.RefreshTokens;
5-
using Domain.Roles;
6-
using Domain.UserProfiles;
7-
using Domain.Users;
1+
using Domain.Entities.Auth.OtpCodes;
2+
using Domain.Entities.Auth.Permissions;
3+
using Domain.Entities.Auth.RefreshTokens;
4+
using Domain.Entities.Auth.Roles;
5+
using Domain.Entities.Auth.UserProfiles;
6+
using Domain.Entities.Auth.Users;
7+
using Domain.Joins;
88
using Microsoft.EntityFrameworkCore;
99

1010
namespace Application.Abstractions.Data;

apps/backend/src/Application/Application.csproj

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,4 @@
1313
<ProjectReference Include="..\Domain\Domain.csproj" />
1414
</ItemGroup>
1515

16-
<ItemGroup>
17-
<Folder Include="Commands\Auth\ForgotPassword\" />
18-
<Folder Include="Commands\Auth\ResetPassword\" />
19-
</ItemGroup>
20-
2116
</Project>

apps/backend/src/Application/Commands/Auth/FinishSessions/FinishSessionsCommandHandler.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
using Application.Abstractions.Authorization;
33
using Application.Abstractions.Data;
44
using Application.Abstractions.Messaging;
5-
using Application.Abstractions.Services;
65
using Domain.Authorization;
6+
using Domain.Identifiers;
7+
using Domain.Services;
78
using Microsoft.EntityFrameworkCore;
89
using SharedKernel.Results;
910

@@ -40,6 +41,8 @@ await context
4041

4142
await cacheService.BlacklistTokenAsync(jwt, cancellationToken);
4243

44+
await cacheService.EvictByTagAsync(Tags.Users, cancellationToken);
45+
4346
return Result.Success();
4447
}
4548
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
using Application.Abstractions.Messaging;
2+
3+
namespace Application.Commands.Auth.ForgotPassword;
4+
5+
public sealed record ForgotPasswordCommand(string Email) : ICommand;
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using Application.Abstractions.Data;
2+
using Application.Abstractions.Messaging;
3+
using Domain.Entities.Auth.OtpCodes;
4+
using Domain.Entities.Auth.Users;
5+
using Domain.Enums;
6+
using Domain.Services;
7+
using Microsoft.EntityFrameworkCore;
8+
using SharedKernel.Errors;
9+
using SharedKernel.Results;
10+
using SharedKernel.Time;
11+
12+
namespace Application.Commands.Auth.ForgotPassword;
13+
14+
internal sealed class ForgotPasswordCommandHandler(
15+
IApplicationDbContext context,
16+
IDateTimeProvider timeProvider,
17+
IEmailService emailService
18+
) : ICommandHandler<ForgotPasswordCommand>
19+
{
20+
public async Task<Result> Handle(
21+
ForgotPasswordCommand command,
22+
CancellationToken cancellationToken
23+
)
24+
{
25+
User? user = await context
26+
.Users.AsNoTracking()
27+
.SingleOrDefaultAsync(x => x.Email == command.Email, cancellationToken);
28+
29+
if (!User.IsValidUser(user, command.Email, out Error? error))
30+
{
31+
return Result.Failure(error!);
32+
}
33+
34+
string otpCode = OtpCode.GenerateCode();
35+
36+
var otpCodeEntity = new OtpCode
37+
{
38+
UserId = user!.Id,
39+
Code = otpCode,
40+
Type = OtpCodeType.ResetPassword,
41+
CreatedOnUtc = timeProvider.UtcNow,
42+
ExpiredOnUtc = timeProvider.UtcNow.AddMinutes(15),
43+
};
44+
45+
context.OtpCodes.Add(otpCodeEntity);
46+
47+
await context.SaveChangesAsync(cancellationToken);
48+
49+
await User.SendOtpCodeAsync(emailService, command.Email, otpCode, cancellationToken);
50+
51+
return Result.Success();
52+
}
53+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using FluentValidation;
2+
3+
namespace Application.Commands.Auth.ForgotPassword;
4+
5+
internal sealed class ForgotPasswordCommandValidator : AbstractValidator<ForgotPasswordCommand>
6+
{
7+
public ForgotPasswordCommandValidator()
8+
{
9+
RuleFor(x => x.Email).NotEmpty().EmailAddress();
10+
}
11+
}

apps/backend/src/Application/Commands/Auth/Login/LoginCommandHandler.cs

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
using Application.Abstractions.Authentication;
22
using Application.Abstractions.Data;
33
using Application.Abstractions.Messaging;
4-
using Domain.Enums;
5-
using Domain.RefreshTokens;
6-
using Domain.Users;
4+
using Domain.Entities.Auth.RefreshTokens;
5+
using Domain.Entities.Auth.Users;
76
using Microsoft.EntityFrameworkCore;
7+
using SharedKernel.Errors;
88
using SharedKernel.Results;
99
using SharedKernel.Time;
1010

@@ -29,28 +29,18 @@ CancellationToken cancellationToken
2929
.Include(u => u.Profile)
3030
.SingleOrDefaultAsync(u => u.Email == command.Email, cancellationToken);
3131

32-
if (user is null)
32+
if (!User.IsValidUser(user, command.Email, out Error? error))
3333
{
34-
return Result.Failure<LoginResponse>(UserErrors.InvalidCredentials);
34+
return Result.Failure<LoginResponse>(error!);
3535
}
3636

37-
bool verified = passwordHasher.Verify(command.Password, user.PasswordHash);
37+
bool verified = passwordHasher.Verify(command.Password, user!.PasswordHash);
3838

3939
if (!verified)
4040
{
4141
return Result.Failure<LoginResponse>(UserErrors.InvalidCredentials);
4242
}
4343

44-
if (!user.EmailVerified || user.Status == UserStatus.Pending)
45-
{
46-
return Result.Failure<LoginResponse>(UserErrors.EmailNotVerified);
47-
}
48-
49-
if (user.Status == UserStatus.Blocked)
50-
{
51-
return Result.Failure<LoginResponse>(UserErrors.Forbidden);
52-
}
53-
5444
string accessToken = tokenProvider.Create(user);
5545

5646
var refreshToken = new RefreshToken

apps/backend/src/Application/Commands/Auth/LoginRefreshToken/LoginRefreshTokenCommandHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using Application.Abstractions.Authentication;
22
using Application.Abstractions.Data;
33
using Application.Abstractions.Messaging;
4-
using Domain.RefreshTokens;
4+
using Domain.Entities.Auth.RefreshTokens;
55
using Microsoft.EntityFrameworkCore;
66
using SharedKernel.Results;
77
using SharedKernel.Time;

apps/backend/src/Application/Commands/Auth/Logout/LogoutUserCommandHandler.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
using Application.Abstractions.Authorization;
33
using Application.Abstractions.Data;
44
using Application.Abstractions.Messaging;
5-
using Application.Abstractions.Services;
65
using Domain.Authorization;
7-
using Domain.RefreshTokens;
8-
using Domain.Users;
6+
using Domain.Entities.Auth.RefreshTokens;
7+
using Domain.Identifiers;
8+
using Domain.Services;
99
using Microsoft.EntityFrameworkCore;
1010
using SharedKernel.Results;
1111

@@ -38,7 +38,7 @@ public async Task<Result> Handle(LogoutUserCommand command, CancellationToken ca
3838

3939
if (refreshToken is null)
4040
{
41-
return Result.Failure(UserErrors.SessionNotFound);
41+
return Result.Failure(RefreshTokenErrors.SessionNotFound);
4242
}
4343

4444
context.RefreshTokens.Remove(refreshToken);
@@ -49,6 +49,8 @@ public async Task<Result> Handle(LogoutUserCommand command, CancellationToken ca
4949

5050
await cacheService.BlacklistTokenAsync(jwt, cancellationToken);
5151

52+
await cacheService.EvictByTagAsync(Tags.Users, cancellationToken);
53+
5254
return Result.Success();
5355
}
5456
}

0 commit comments

Comments
 (0)