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.
84 lines
2.4 KiB
C#
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()));
|
|
}
|
|
}
|