Foxchat.NET/Foxchat.Identity/Controllers/Oauth/PasswordAuthController.cs

81 lines
3.3 KiB
C#
Raw Normal View History

2024-05-20 19:42:04 +02:00
using Foxchat.Identity.Middleware;
using Foxchat.Identity.Database;
using Microsoft.AspNetCore.Mvc;
2024-05-20 20:37:22 +02:00
using Microsoft.AspNetCore.Identity;
using Foxchat.Identity.Database.Models;
using Foxchat.Core;
using System.Diagnostics;
using NodaTime;
2024-05-20 21:59:30 +02:00
using Microsoft.EntityFrameworkCore;
2024-05-20 19:42:04 +02:00
namespace Foxchat.Identity.Controllers.Oauth;
[ApiController]
[Authenticate]
[Route("/_fox/ident/oauth/password")]
2024-05-20 20:37:22 +02:00
public class PasswordAuthController(ILogger logger, IdentityContext db, IClock clock) : ControllerBase
2024-05-20 19:42:04 +02:00
{
2024-05-20 20:37:22 +02:00
private readonly PasswordHasher<Account> _passwordHasher = new();
2024-05-20 19:42:04 +02:00
[HttpPost("register")]
2024-05-20 20:37:22 +02:00
public async Task<IActionResult> Register([FromBody] RegisterRequest req)
2024-05-20 19:42:04 +02:00
{
var app = HttpContext.GetApplicationOrThrow();
2024-05-21 17:45:35 +02:00
var appToken =
HttpContext.GetToken() ??
throw new UnreachableException(); // GetApplicationOrThrow already gets the token and throws if it's null
2024-05-20 20:37:22 +02:00
if (req.Scopes.Except(appToken.Scopes).Any())
2024-05-21 17:45:35 +02:00
throw new ApiError.Forbidden("Cannot request token scopes that are not allowed for this token",
req.Scopes.Except(appToken.Scopes));
2024-05-20 20:37:22 +02:00
var acct = new Account
{
Username = req.Username,
Email = req.Email,
Role = Account.AccountRole.User
};
2024-05-21 17:45:35 +02:00
db.Add(acct);
2024-05-20 20:37:22 +02:00
var hashedPassword = await Task.Run(() => _passwordHasher.HashPassword(acct, req.Password));
acct.Password = hashedPassword;
2024-05-20 21:59:30 +02:00
// TODO: make token expiry configurable
2024-05-20 20:37:22 +02:00
var (tokenStr, token) = Token.Create(acct, app, req.Scopes, clock.GetCurrentInstant() + Duration.FromDays(365));
2024-05-21 17:45:35 +02:00
db.Add(token);
2024-05-20 20:37:22 +02:00
await db.SaveChangesAsync();
2024-05-20 19:42:04 +02:00
2024-05-20 21:59:30 +02:00
return Ok(new AuthResponse(acct.Id, acct.Username, acct.Email, tokenStr));
}
[HttpPost("login")]
public async Task<IActionResult> Login([FromBody] LoginRequest req)
{
var app = HttpContext.GetApplicationOrThrow();
var appToken = HttpContext.GetToken() ?? throw new UnreachableException();
if (req.Scopes.Except(appToken.Scopes).Any())
2024-05-21 17:45:35 +02:00
throw new ApiError.Forbidden("Cannot request token scopes that are not allowed for this token",
req.Scopes.Except(appToken.Scopes));
2024-05-20 21:59:30 +02:00
var acct = await db.Accounts.FirstOrDefaultAsync(a => a.Email == req.Email)
2024-05-21 17:45:35 +02:00
?? throw new ApiError.NotFound("No user with that email found, or password is incorrect");
2024-05-20 21:59:30 +02:00
var pwResult = await Task.Run(() => _passwordHasher.VerifyHashedPassword(acct, acct.Password, req.Password));
if (pwResult == PasswordVerificationResult.Failed)
throw new ApiError.NotFound("No user with that email found, or password is incorrect");
if (pwResult == PasswordVerificationResult.SuccessRehashNeeded)
acct.Password = await Task.Run(() => _passwordHasher.HashPassword(acct, req.Password));
2024-05-21 17:45:35 +02:00
2024-05-20 21:59:30 +02:00
var (tokenStr, token) = Token.Create(acct, app, req.Scopes, clock.GetCurrentInstant() + Duration.FromDays(365));
2024-05-21 17:45:35 +02:00
db.Add(token);
2024-05-20 21:59:30 +02:00
await db.SaveChangesAsync();
return Ok(new AuthResponse(acct.Id, acct.Username, acct.Email, tokenStr));
2024-05-20 19:42:04 +02:00
}
2024-05-20 20:37:22 +02:00
public record RegisterRequest(string Username, string Password, string Email, string[] Scopes);
2024-05-21 17:45:35 +02:00
2024-05-20 21:59:30 +02:00
public record LoginRequest(string Email, string Password, string[] Scopes);
2024-05-21 17:45:35 +02:00
2024-05-20 21:59:30 +02:00
public record AuthResponse(Ulid Id, string Username, string Email, string Token);
2024-05-20 19:42:04 +02:00
}