Files
Media.JoshHeaps.Net/Media.JoshHeaps.Net/Pages/Sso/Authorize.cshtml.cs
T
2026-04-21 16:13:26 -06:00

60 lines
2.5 KiB
C#

using System.Security.Cryptography;
using System.Text;
using Media.JoshHeaps.Net.Services;
using Microsoft.AspNetCore.Mvc;
namespace Media.JoshHeaps.Net.Pages.Sso;
public class AuthorizeModel(DbExecutor db, IConfiguration config, ILogger<AuthorizeModel> logger) : AuthenticatedPageModel
{
public async Task<IActionResult> OnGetAsync(
[FromQuery(Name = "client_id")] string? clientId,
[FromQuery(Name = "redirect_uri")] string? redirectUri,
[FromQuery] string? state)
{
if (string.IsNullOrWhiteSpace(clientId) || string.IsNullOrWhiteSpace(redirectUri) || string.IsNullOrWhiteSpace(state))
{
return BadRequest("client_id, redirect_uri, and state are required");
}
var client = SsoClientRegistry.Find(config, clientId);
if (client == null) return BadRequest("unknown client_id");
if (!client.AllowsRedirectUri(redirectUri)) return BadRequest("redirect_uri is not registered for this client");
if (!IsAuthenticated())
{
var original = $"/sso/authorize?client_id={Uri.EscapeDataString(clientId)}&redirect_uri={Uri.EscapeDataString(redirectUri)}&state={Uri.EscapeDataString(state)}";
return Redirect($"/Login?ReturnUrl={Uri.EscapeDataString(original)}");
}
LoadUserSession();
var code = GenerateCode();
var codeHash = HashCode(code);
var lifetime = int.TryParse(config["Sso:CodeLifetimeSeconds"], out var s) ? s : 60;
var expiresAt = DateTimeOffset.UtcNow.AddSeconds(lifetime);
await db.ExecuteNonQueryAsync(
@"INSERT INTO app.sso_authorization_codes (code_hash, client_id, user_id, redirect_uri, expires_at)
VALUES (@codeHash, @clientId, @userId, @redirectUri, @expiresAt)",
new { codeHash, clientId, userId = UserId, redirectUri, expiresAt });
logger.LogInformation("SSO code issued for user {UserId} to client {ClientId}", UserId, clientId);
var separator = redirectUri.Contains('?') ? '&' : '?';
return Redirect($"{redirectUri}{separator}code={Uri.EscapeDataString(code)}&state={Uri.EscapeDataString(state)}");
}
private static string GenerateCode()
{
var bytes = RandomNumberGenerator.GetBytes(32);
return Convert.ToBase64String(bytes).Replace("+", "-").Replace("/", "_").TrimEnd('=');
}
private static string HashCode(string code)
{
var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(code));
return Convert.ToHexString(bytes).ToLowerInvariant();
}
}