diff --git a/Foxchat.Identity/Controllers/Oauth/PasswordAuthController.cs b/Foxchat.Identity/Controllers/Oauth/PasswordAuthController.cs index 7d867a1..66df4bf 100644 --- a/Foxchat.Identity/Controllers/Oauth/PasswordAuthController.cs +++ b/Foxchat.Identity/Controllers/Oauth/PasswordAuthController.cs @@ -2,21 +2,47 @@ using Foxchat.Identity.Middleware; using Foxchat.Identity.Database; using Foxchat.Identity.Utils; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Identity; +using Foxchat.Identity.Database.Models; +using Foxchat.Core; +using System.Diagnostics; +using NodaTime; namespace Foxchat.Identity.Controllers.Oauth; [ApiController] [Authenticate] [Route("/_fox/ident/oauth/password")] -public class PasswordAuthController(ILogger logger, IdentityContext db) : ControllerBase +public class PasswordAuthController(ILogger logger, IdentityContext db, IClock clock) : ControllerBase { + private readonly PasswordHasher _passwordHasher = new(); + [HttpPost("register")] - public async Task Register() + public async Task 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 - throw new NotImplementedException(); + if (req.Scopes.Except(appToken.Scopes).Any()) + throw new ApiError.Forbidden("Cannot request token scopes that are not allowed for this token", req.Scopes.Except(appToken.Scopes)); + + var acct = new Account + { + Username = req.Username, + Email = req.Email, + Role = Account.AccountRole.User + }; + + await db.AddAsync(acct); + var hashedPassword = await Task.Run(() => _passwordHasher.HashPassword(acct, req.Password)); + acct.Password = hashedPassword; + var (tokenStr, token) = Token.Create(acct, app, req.Scopes, clock.GetCurrentInstant() + Duration.FromDays(365)); + await db.AddAsync(token); + await db.SaveChangesAsync(); + + return Ok(new RegisterResponse(acct.Id, acct.Username, acct.Email, tokenStr)); } - public record RegisterRequest(); + public record RegisterRequest(string Username, string Password, string Email, string[] Scopes); + public record RegisterResponse(Ulid Id, string Username, string Email, string Token); } \ No newline at end of file diff --git a/Foxchat.Identity/Controllers/Oauth/TokenController.cs b/Foxchat.Identity/Controllers/Oauth/TokenController.cs index 1f84414..5cff74e 100644 --- a/Foxchat.Identity/Controllers/Oauth/TokenController.cs +++ b/Foxchat.Identity/Controllers/Oauth/TokenController.cs @@ -26,6 +26,7 @@ public class TokenController(ILogger logger, IdentityContext db, IClock clock) : case "client_credentials": return await HandleClientCredentialsAsync(app, scopes); case "authorization_code": + // TODO break; default: throw new ApiError.BadRequest("Unknown grant_type"); diff --git a/Foxchat.Identity/Database/Models/Token.cs b/Foxchat.Identity/Database/Models/Token.cs index 1ecbfb3..4a0c2d4 100644 --- a/Foxchat.Identity/Database/Models/Token.cs +++ b/Foxchat.Identity/Database/Models/Token.cs @@ -24,4 +24,17 @@ public class Token : BaseModel return (token, hash); } + + public static (string, Token) Create(Account? account, Application application, string[] scopes, Instant expires) + { + var (token, hash) = Generate(); + return (token, new() + { + Hash = hash, + Scopes = scopes, + Expires = expires, + Account = account, + Application = application, + }); + } } diff --git a/Foxchat.Identity/Middleware/ErrorHandlerMiddleware.cs b/Foxchat.Identity/Middleware/ErrorHandlerMiddleware.cs index e56a660..6b8d7e5 100644 --- a/Foxchat.Identity/Middleware/ErrorHandlerMiddleware.cs +++ b/Foxchat.Identity/Middleware/ErrorHandlerMiddleware.cs @@ -1,4 +1,3 @@ - using System.Net; using Foxchat.Core; using Foxchat.Core.Models.Http;