Index: ChapterX.Application/Auth/IJwtTokenService.cs
===================================================================
--- ChapterX.Application/Auth/IJwtTokenService.cs	(revision e294f7d2cedd462a923c69ae9f85c996b5377c69)
+++ ChapterX.Application/Auth/IJwtTokenService.cs	(revision e294f7d2cedd462a923c69ae9f85c996b5377c69)
@@ -0,0 +1,7 @@
+namespace ChapterX.Application.Auth
+{
+    public interface IJwtTokenService
+    {
+        string GenerateToken(ChapterX.Domain.Entities.User user);
+    }
+}
Index: ChapterX.Application/Auth/LoginHandler.cs
===================================================================
--- ChapterX.Application/Auth/LoginHandler.cs	(revision e294f7d2cedd462a923c69ae9f85c996b5377c69)
+++ ChapterX.Application/Auth/LoginHandler.cs	(revision e294f7d2cedd462a923c69ae9f85c996b5377c69)
@@ -0,0 +1,30 @@
+using ChapterX.Domain.Repositories;
+using MediatR;
+
+namespace ChapterX.Application.Auth
+{
+    public class LoginHandler : IRequestHandler<LoginRequest, LoginResponse>
+    {
+        private readonly IUserRepository _userRepository;
+        private readonly IJwtTokenService _jwtTokenService;
+
+        public LoginHandler(IUserRepository userRepository, IJwtTokenService jwtTokenService)
+        {
+            _userRepository = userRepository;
+            _jwtTokenService = jwtTokenService;
+        }
+
+        public async Task<LoginResponse> Handle(LoginRequest request, CancellationToken cancellationToken)
+        {
+            var user = await _userRepository.GetByEmailAsync(request.Email, cancellationToken)
+                ?? throw new UnauthorizedAccessException("Invalid email or password.");
+
+            if (!BCrypt.Net.BCrypt.Verify(request.Password, user.Password))
+                throw new UnauthorizedAccessException("Invalid email or password.");
+
+            var token = _jwtTokenService.GenerateToken(user);
+
+            return new LoginResponse(token, user.Id, user.Username, user.Email);
+        }
+    }
+}
Index: ChapterX.Application/Auth/LoginRequest.cs
===================================================================
--- ChapterX.Application/Auth/LoginRequest.cs	(revision e294f7d2cedd462a923c69ae9f85c996b5377c69)
+++ ChapterX.Application/Auth/LoginRequest.cs	(revision e294f7d2cedd462a923c69ae9f85c996b5377c69)
@@ -0,0 +1,6 @@
+using MediatR;
+
+namespace ChapterX.Application.Auth
+{
+    public record LoginRequest(string Email, string Password) : IRequest<LoginResponse>;
+}
Index: ChapterX.Application/Auth/LoginResponse.cs
===================================================================
--- ChapterX.Application/Auth/LoginResponse.cs	(revision e294f7d2cedd462a923c69ae9f85c996b5377c69)
+++ ChapterX.Application/Auth/LoginResponse.cs	(revision e294f7d2cedd462a923c69ae9f85c996b5377c69)
@@ -0,0 +1,4 @@
+namespace ChapterX.Application.Auth
+{
+    public record LoginResponse(string Token, int UserId, string Username, string Email);
+}
Index: ChapterX.Application/Auth/RegisterHandler.cs
===================================================================
--- ChapterX.Application/Auth/RegisterHandler.cs	(revision e294f7d2cedd462a923c69ae9f85c996b5377c69)
+++ ChapterX.Application/Auth/RegisterHandler.cs	(revision e294f7d2cedd462a923c69ae9f85c996b5377c69)
@@ -0,0 +1,37 @@
+using ChapterX.Domain.Repositories;
+using MediatR;
+
+namespace ChapterX.Application.Auth
+{
+    public class RegisterHandler : IRequestHandler<RegisterRequest, RegisterResponse>
+    {
+        private readonly IUserRepository _userRepository;
+
+        public RegisterHandler(IUserRepository userRepository)
+        {
+            _userRepository = userRepository;
+        }
+
+        public async Task<RegisterResponse> Handle(RegisterRequest request, CancellationToken cancellationToken)
+        {
+            var existing = await _userRepository.GetByEmailAsync(request.Email, cancellationToken);
+            if (existing != null)
+                throw new InvalidOperationException("Email already in use.");
+
+            var user = new Domain.Entities.User
+            {
+                Username = request.Username,
+                Email = request.Email,
+                Name = request.Name,
+                Surname = request.Surname,
+                Password = BCrypt.Net.BCrypt.HashPassword(request.Password),
+                CreatedAt = DateTime.UtcNow,
+                UpdatedAt = DateTime.UtcNow
+            };
+
+            await _userRepository.AddAsync(user, cancellationToken);
+
+            return new RegisterResponse(user.Id, user.Username, user.Email);
+        }
+    }
+}
Index: ChapterX.Application/Auth/RegisterRequest.cs
===================================================================
--- ChapterX.Application/Auth/RegisterRequest.cs	(revision e294f7d2cedd462a923c69ae9f85c996b5377c69)
+++ ChapterX.Application/Auth/RegisterRequest.cs	(revision e294f7d2cedd462a923c69ae9f85c996b5377c69)
@@ -0,0 +1,6 @@
+using MediatR;
+
+namespace ChapterX.Application.Auth
+{
+    public record RegisterRequest(string Username, string Email, string Name, string Surname, string Password) : IRequest<RegisterResponse>;
+}
Index: ChapterX.Application/Auth/RegisterResponse.cs
===================================================================
--- ChapterX.Application/Auth/RegisterResponse.cs	(revision e294f7d2cedd462a923c69ae9f85c996b5377c69)
+++ ChapterX.Application/Auth/RegisterResponse.cs	(revision e294f7d2cedd462a923c69ae9f85c996b5377c69)
@@ -0,0 +1,4 @@
+namespace ChapterX.Application.Auth
+{
+    public record RegisterResponse(int UserId, string Username, string Email);
+}
