diff --git a/OAuthServer/Controllers/OAuthController.cs b/OAuthServer/Controllers/OAuthController.cs index 9a67766..c2700d3 100644 --- a/OAuthServer/Controllers/OAuthController.cs +++ b/OAuthServer/Controllers/OAuthController.cs @@ -1,5 +1,7 @@ using System.ComponentModel.DataAnnotations; +using System.Text.Json; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Mvc; using OAuthServer.Services; @@ -7,23 +9,24 @@ namespace OAuthServer.Controllers; [ApiController] [Route("oauth")] -public class OAuthController : ControllerBase { - private readonly ILogger _logger; - private readonly JwtService _jwt; +public class OAuthController( + ILogger logger, + JwtService jwt, + IDataProtectionProvider dataProtectionProvider +) : ControllerBase { + private readonly Dictionary _clients = new() { + {"lmao", "yeet"}, + }; - public OAuthController(ILogger logger, JwtService jwt) { - _logger = logger; - _jwt = jwt; - } [Authorize] [HttpGet("authorize")] // ReSharper disable InconsistentNaming public ActionResult Authorize( [Required, Url] string redirect_uri, - string response_type, - string client_id, - string state + string? response_type, + string? client_id, + string? state ) { if (string.IsNullOrEmpty(response_type) || string.IsNullOrEmpty(client_id) || string.IsNullOrEmpty(state)) { return Redirect($"{redirect_uri}?error=invalid_request"); @@ -33,12 +36,19 @@ public class OAuthController : ControllerBase { return Redirect($"{redirect_uri}?error=unsupported_response_type&state={state}"); } - if (client_id != "lmao") { - return Redirect($"{redirect_uri}?error=access_denied&error_description=Invalid+client+id&state={state}"); + if (!_clients.ContainsKey(client_id)) { + logger.LogInformation("Unknown client id"); + return Redirect($"{redirect_uri}?error=unauthorized_client&state={state}"); } - // TODO: generate code - string code = Guid.NewGuid().ToString(); + IDataProtector protector = dataProtectionProvider.CreateProtector("oauth"); + CodeObject codeObject = new CodeObject( + ClientId: client_id, + RedirectUri: redirect_uri, + Expiry: DateTime.UtcNow.AddMinutes(5) + ); + + string code = protector.Protect(JsonSerializer.Serialize(codeObject)); return Redirect($"{redirect_uri}?code={code}&state={state}"); } @@ -54,9 +64,11 @@ public class OAuthController : ControllerBase { [HttpPost("token")] [Consumes("application/x-www-form-urlencoded")] public ActionResult GenerateToken([FromForm] GenerateTokenRequest request) { - if (string.IsNullOrEmpty(request.grant_type) || string.IsNullOrEmpty(request.code) || + if (string.IsNullOrEmpty(request.grant_type) || + string.IsNullOrEmpty(request.code) || string.IsNullOrEmpty(request.redirect_uri) || - string.IsNullOrEmpty(request.client_id)) { + string.IsNullOrEmpty(request.client_id) || + string.IsNullOrEmpty(request.client_secret)) { return BadRequest(new {error = "invalid_request"}); } @@ -64,15 +76,44 @@ public class OAuthController : ControllerBase { return BadRequest(new {error = "unsupported_grant_type"}); } - if (request.client_id != "lmao") { - return BadRequest(new {error = "invalid_client"}); + if (!_clients.TryGetValue(request.client_id, out string? clientSecret)) { + logger.LogInformation("Unknown client id"); + return BadRequest(new {error = "unauthorized_client"}); } - string token = _jwt.GenerateToken(); + if (request.client_secret != clientSecret) { + logger.LogInformation("Invalid client secret"); + return BadRequest(new {error = "unauthorized_client"}); + } + + IDataProtector protector = dataProtectionProvider.CreateProtector("oauth"); + CodeObject? codeObject; + try { + codeObject = JsonSerializer.Deserialize(protector.Unprotect(request.code)); + } catch (Exception) { + return BadRequest(new {error = "invalid_request"}); + } + + if (codeObject == null) { + return BadRequest(new {error = "invalid_request"}); + } + + if (codeObject.ClientId != request.client_id || codeObject.RedirectUri != request.redirect_uri) { + return BadRequest(new {error = "invalid_request"}); + } + + if (DateTime.UtcNow > codeObject.Expiry) { + logger.LogInformation("Expired token"); + return BadRequest(new {error = "invalid_grant"}); + } + + string token = jwt.GenerateToken(); Response.Headers.Append("Cache-Control", "no-store"); Response.Headers.Append("Pragma", "no-cache"); return Ok(new {access_token = token, token_type = "bearer"}); } + + private record CodeObject(string ClientId, string RedirectUri, DateTime Expiry); } \ No newline at end of file