mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-02-16 10:42:08 +00:00
Update namespace organization in service layer
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
using BusinessLayer.Services;
|
||||
using DataAccessLayer.Entities;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using ServiceCore.Services;
|
||||
|
||||
namespace WebAPI.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class AuthController(IAuthService auth) : ControllerBase
|
||||
public class AuthController(IAuthService auth, IJwtService jwtService) : ControllerBase
|
||||
{
|
||||
public record RegisterRequest(
|
||||
string Username,
|
||||
@@ -39,9 +39,15 @@ namespace WebAPI.Controllers
|
||||
[HttpPost("login")]
|
||||
public async Task<ActionResult> Login([FromBody] LoginRequest req)
|
||||
{
|
||||
var ok = await auth.LoginAsync(req.UsernameOrEmail, req.Password);
|
||||
if (!ok) return Unauthorized();
|
||||
return Ok(new { success = true });
|
||||
var userAccount = await auth.LoginAsync(req.UsernameOrEmail, req.Password);
|
||||
if (userAccount is null)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
var jwt = jwtService.GenerateJwt(userAccount.UserAccountId, userAccount.Username, userAccount.DateOfBirth);
|
||||
|
||||
return Ok(new { AccessToken = jwt, Message = "Logged in successfully." });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using BusinessLayer.Services;
|
||||
using DataAccessLayer.Entities;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using ServiceCore.Services;
|
||||
|
||||
namespace WebAPI.Controllers
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using BusinessLayer.Services;
|
||||
using DataAccessLayer.Repositories.UserAccount;
|
||||
using DataAccessLayer.Repositories.UserCredential;
|
||||
using DataAccessLayer.Sql;
|
||||
using ServiceCore.Services;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
@@ -16,6 +16,7 @@ builder.Services.AddScoped<IUserAccountRepository, UserAccountRepository>();
|
||||
builder.Services.AddScoped<IUserService, UserService>();
|
||||
builder.Services.AddScoped<IUserCredentialRepository, UserCredentialRepository>();
|
||||
builder.Services.AddScoped<IAuthService, AuthService>();
|
||||
builder.Services.AddScoped<IJwtService, JwtService>();
|
||||
var app = builder.Build();
|
||||
|
||||
app.UseSwagger();
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace DataAccessLayer.Repositories.UserCredential
|
||||
command.CommandText = "USP_RotateUserCredential";
|
||||
command.CommandType = CommandType.StoredProcedure;
|
||||
|
||||
AddParameter(command, "@UserAccountId", userAccountId);
|
||||
AddParameter(command, "@UserAccountId_", userAccountId);
|
||||
AddParameter(command, "@Hash", credential.Hash);
|
||||
|
||||
await command.ExecuteNonQueryAsync();
|
||||
@@ -27,7 +27,7 @@ namespace DataAccessLayer.Repositories.UserCredential
|
||||
command.CommandText = "USP_GetActiveUserCredentialByUserAccountId";
|
||||
command.CommandType = CommandType.StoredProcedure;
|
||||
|
||||
AddParameter(command, "@UserAccountId", userAccountId);
|
||||
AddParameter(command, "@UserAccountId_", userAccountId);
|
||||
|
||||
await using var reader = await command.ExecuteReaderAsync();
|
||||
return await reader.ReadAsync() ? MapToEntity(reader) : null;
|
||||
@@ -40,7 +40,7 @@ namespace DataAccessLayer.Repositories.UserCredential
|
||||
command.CommandText = "USP_InvalidateUserCredential";
|
||||
command.CommandType = CommandType.StoredProcedure;
|
||||
|
||||
AddParameter(command, "@UserAccountId", userAccountId);
|
||||
AddParameter(command, "@UserAccountId_", userAccountId);
|
||||
await command.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>BusinessLayer</RootNamespace>
|
||||
<RootNamespace>ServiceCore</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using DataAccessLayer.Entities;
|
||||
using DataAccessLayer.Repositories.UserAccount;
|
||||
using DataAccessLayer.Repositories.UserCredential;
|
||||
|
||||
namespace BusinessLayer.Services
|
||||
namespace ServiceCore.Services
|
||||
{
|
||||
public class AuthService(IUserAccountRepository userRepo, IUserCredentialRepository credRepo) : IAuthService
|
||||
{
|
||||
@@ -26,18 +25,31 @@ namespace BusinessLayer.Services
|
||||
return userAccount;
|
||||
}
|
||||
|
||||
public async Task<bool> LoginAsync(string usernameOrEmail, string password)
|
||||
public async Task<UserAccount?> LoginAsync(string usernameOrEmail, string password)
|
||||
{
|
||||
// Attempt lookup by username, then email
|
||||
var user = await userRepo.GetByUsernameAsync(usernameOrEmail)
|
||||
?? await userRepo.GetByEmailAsync(usernameOrEmail);
|
||||
// the user was not found
|
||||
if (user is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (user is null) return false;
|
||||
|
||||
// they don't have an active credential
|
||||
// @todo handle expired passwords
|
||||
var activeCred = await credRepo.GetActiveCredentialByUserAccountIdAsync(user.UserAccountId);
|
||||
if (activeCred is null) return false;
|
||||
if (activeCred is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return PasswordHasher.Verify(password, activeCred.Hash);
|
||||
if (!PasswordHasher.Verify(password, activeCred.Hash))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
public async Task InvalidateAsync(Guid userAccountId)
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
using DataAccessLayer.Entities;
|
||||
|
||||
namespace BusinessLayer.Services
|
||||
namespace ServiceCore.Services
|
||||
{
|
||||
public interface IAuthService
|
||||
{
|
||||
Task<UserAccount> RegisterAsync(UserAccount userAccount, string password);
|
||||
Task<bool> LoginAsync(string usernameOrEmail, string password);
|
||||
Task InvalidateAsync(Guid userAccountId);
|
||||
Task<UserAccount?> LoginAsync(string usernameOrEmail, string password);
|
||||
}
|
||||
}
|
||||
6
src/Core/Service/Service.Core/Services/IJwtService.cs
Normal file
6
src/Core/Service/Service.Core/Services/IJwtService.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace ServiceCore.Services;
|
||||
|
||||
public interface IJwtService
|
||||
{
|
||||
string GenerateJwt(Guid userId, string username, DateTime expiry);
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
|
||||
|
||||
using DataAccessLayer.Entities;
|
||||
|
||||
namespace BusinessLayer.Services
|
||||
namespace ServiceCore.Services
|
||||
{
|
||||
public interface IUserService
|
||||
{
|
||||
|
||||
38
src/Core/Service/Service.Core/Services/JwtService.cs
Normal file
38
src/Core/Service/Service.Core/Services/JwtService.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.IdentityModel.JsonWebTokens;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using JwtRegisteredClaimNames = System.IdentityModel.Tokens.Jwt.JwtRegisteredClaimNames;
|
||||
|
||||
namespace ServiceCore.Services;
|
||||
public class JwtService(IConfiguration config) : IJwtService
|
||||
{
|
||||
private readonly string? _secret = config["Jwt:Secret"];
|
||||
|
||||
public string GenerateJwt(Guid userId, string username, DateTime expiry)
|
||||
{
|
||||
var handler = new JsonWebTokenHandler();
|
||||
|
||||
var key = Encoding.UTF8.GetBytes(_secret ?? throw new InvalidOperationException("secret not set"));
|
||||
|
||||
// Base claims (always present)
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new(JwtRegisteredClaimNames.Sub, userId.ToString()),
|
||||
new(JwtRegisteredClaimNames.UniqueName, username),
|
||||
new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
|
||||
};
|
||||
|
||||
var tokenDescriptor = new SecurityTokenDescriptor
|
||||
{
|
||||
Subject = new ClaimsIdentity(claims),
|
||||
Expires = expiry,
|
||||
SigningCredentials = new SigningCredentials(
|
||||
new SymmetricSecurityKey(key),
|
||||
SecurityAlgorithms.HmacSha256)
|
||||
};
|
||||
|
||||
return handler.CreateToken(tokenDescriptor);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Konscious.Security.Cryptography;
|
||||
|
||||
namespace BusinessLayer.Services
|
||||
namespace ServiceCore.Services
|
||||
{
|
||||
public static class PasswordHasher
|
||||
{
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using DataAccessLayer.Entities;
|
||||
using DataAccessLayer.Repositories;
|
||||
using DataAccessLayer.Repositories.UserAccount;
|
||||
|
||||
namespace BusinessLayer.Services
|
||||
namespace ServiceCore.Services
|
||||
{
|
||||
public class UserService(IUserAccountRepository repository) : IUserService
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user