Files
Hy2026/backend/Controllers/AuthController.cs
T
Rafał Tyński 482a4b41f0 feat(backend): add JWT authentication, RBAC and consent/audit services
Add signup/signin with backend-issued JWT (Argon2 passwords in SurrealDB), role-based authorization policies, per-user data access levels, consent register and read audit log. Protect user endpoints and record sensitive reads.
2026-10-04 02:11:56 +02:00

84 lines
2.4 KiB
C#

using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using TarninaHub.Application.Abstractions;
using TarninaHub.Application.Dtos;
using TarninaHub.Application.Mappings;
using TarninaHub.Application.Security;
namespace TarninaHub.Controllers;
[ApiController]
[ApiVersion("1.0")]
[Route("api/auth")]
public class AuthController : ControllerBase
{
private readonly IUserService _userService;
private readonly ITokenService _tokenService;
public AuthController(IUserService userService, ITokenService tokenService)
{
_userService = userService;
_tokenService = tokenService;
}
[HttpPost("signup")]
[AllowAnonymous]
public async Task<ActionResult<UserResponse>> SignUp(
RegisterRequest request,
CancellationToken cancellationToken)
{
var existing = await _userService.FindByPhoneAsync(request.Phone, cancellationToken);
if (existing is not null)
{
return Conflict(new ProblemDetails
{
Title = "Phone number already registered",
Status = StatusCodes.Status409Conflict,
});
}
var user = await _userService.CreateAsync(request, RoleNames.User, cancellationToken);
if (user is null)
{
return Problem(title: "User could not be created", statusCode: StatusCodes.Status400BadRequest);
}
return CreatedAtAction("GetById", "Users", new { userId = user.Id }, user.ToResponse());
}
[HttpPost("signin")]
[AllowAnonymous]
public async Task<ActionResult<TokenResponse>> SignIn(
LoginRequest request,
CancellationToken cancellationToken)
{
var valid = await _userService.VerifyCredentialsAsync(
request.Phone,
request.Password,
cancellationToken);
if (!valid)
{
return Unauthorized(new ProblemDetails
{
Title = "Invalid phone number or password",
Status = StatusCodes.Status401Unauthorized,
});
}
var user = await _userService.FindByPhoneAsync(request.Phone, cancellationToken);
if (user is null)
{
return Unauthorized();
}
var token = _tokenService.CreateToken(user);
return Ok(new TokenResponse(token.AccessToken, "Bearer", token.ExpiresAt, user.ToResponse()));
}
}