84 lines
No EOL
3.5 KiB
C#
84 lines
No EOL
3.5 KiB
C#
using Foxchat.Identity.Middleware;
|
|
using Foxchat.Identity.Database;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.AspNetCore.Identity;
|
|
using Foxchat.Identity.Database.Models;
|
|
using Foxchat.Core;
|
|
using System.Diagnostics;
|
|
using Foxchat.Identity.Utils;
|
|
using NodaTime;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace Foxchat.Identity.Controllers.Oauth;
|
|
|
|
[ApiController]
|
|
[ClientAuthenticate]
|
|
[Route("/_fox/ident/oauth/password")]
|
|
public class PasswordAuthController(ILogger logger, IdentityContext db, IClock clock) : ControllerBase
|
|
{
|
|
private readonly PasswordHasher<Account> _passwordHasher = new();
|
|
|
|
[HttpPost("register")]
|
|
public async Task<IActionResult> Register([FromBody] RegisterRequest req)
|
|
{
|
|
var app = HttpContext.GetApplicationOrThrow();
|
|
var appToken =
|
|
HttpContext.GetToken() ??
|
|
throw new UnreachableException(); // GetApplicationOrThrow already gets the token and throws if it's null
|
|
var appScopes = appToken.Scopes.ExpandScopes();
|
|
|
|
if (req.Scopes.Except(appScopes).Any())
|
|
throw new ApiError.Forbidden("Cannot request token scopes that are not allowed for this token",
|
|
req.Scopes.Except(appScopes));
|
|
|
|
var acct = new Account
|
|
{
|
|
Username = req.Username,
|
|
Email = req.Email,
|
|
Role = Account.AccountRole.User
|
|
};
|
|
|
|
db.Add(acct);
|
|
var hashedPassword = await Task.Run(() => _passwordHasher.HashPassword(acct, req.Password));
|
|
acct.Password = hashedPassword;
|
|
// TODO: make token expiry configurable
|
|
var (tokenStr, token) = Token.Create(acct, app, req.Scopes, clock.GetCurrentInstant() + Duration.FromDays(365));
|
|
db.Add(token);
|
|
await db.SaveChangesAsync();
|
|
|
|
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();
|
|
var appScopes = appToken.Scopes.ExpandScopes();
|
|
|
|
if (req.Scopes.Except(appScopes).Any())
|
|
throw new ApiError.Forbidden("Cannot request token scopes that are not allowed for this token",
|
|
req.Scopes.Except(appScopes));
|
|
|
|
var acct = await db.Accounts.FirstOrDefaultAsync(a => a.Email == req.Email)
|
|
?? throw new ApiError.NotFound("No user with that email found, or password is incorrect");
|
|
|
|
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));
|
|
|
|
var (tokenStr, token) = Token.Create(acct, app, req.Scopes, clock.GetCurrentInstant() + Duration.FromDays(365));
|
|
db.Add(token);
|
|
await db.SaveChangesAsync();
|
|
|
|
return Ok(new AuthResponse(acct.Id, acct.Username, acct.Email, tokenStr));
|
|
}
|
|
|
|
public record RegisterRequest(string Username, string Password, string Email, string[] Scopes);
|
|
|
|
public record LoginRequest(string Email, string Password, string[] Scopes);
|
|
|
|
public record AuthResponse(Ulid Id, string Username, string Email, string Token);
|
|
} |