using System.Security.Claims; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Moonlight.ApiServer.Configuration; using Moonlight.ApiServer.Implementations.LocalAuth; using Moonlight.ApiServer.Interfaces; using Moonlight.Shared.Http.Responses.Auth; namespace Moonlight.ApiServer.Http.Controllers.Auth; [ApiController] [Route("api/auth")] public class AuthController : Controller { private readonly IAuthenticationSchemeProvider SchemeProvider; private readonly IEnumerable Extensions; private readonly AppConfiguration Configuration; public AuthController( IAuthenticationSchemeProvider schemeProvider, IEnumerable extensions, AppConfiguration configuration ) { SchemeProvider = schemeProvider; Extensions = extensions; Configuration = configuration; } [HttpGet] public async Task GetSchemes() { var schemes = await SchemeProvider.GetAllSchemesAsync(); var allowedSchemes = Configuration.Authentication.EnabledSchemes; return schemes .Where(x => allowedSchemes.Contains(x.Name)) .Select(scheme => new AuthSchemeResponse() { DisplayName = scheme.DisplayName ?? scheme.Name, Identifier = scheme.Name }) .ToArray(); } [HttpGet("{identifier:alpha}")] public async Task StartScheme([FromRoute] string identifier) { // Validate identifier against our enable list var allowedSchemes = Configuration.Authentication.EnabledSchemes; if (!allowedSchemes.Contains(identifier)) { await Results .Problem( "Invalid scheme identifier provided", statusCode: 404 ) .ExecuteAsync(HttpContext); return; } // Now we can check if it even exists var scheme = await SchemeProvider.GetSchemeAsync(identifier); if (scheme == null) { await Results .Problem( "Invalid scheme identifier provided", statusCode: 404 ) .ExecuteAsync(HttpContext); return; } // Everything fine, challenge the frontend await HttpContext.ChallengeAsync( scheme.Name, new AuthenticationProperties() { RedirectUri = "/" } ); } [Authorize] [HttpGet("check")] public async Task Check() { var username = User.FindFirstValue(ClaimTypes.Name)!; var id = User.FindFirstValue(ClaimTypes.NameIdentifier)!; var email = User.FindFirstValue(ClaimTypes.Email)!; var userId = User.FindFirstValue("UserId")!; var permissions = User.FindFirstValue("Permissions")!; // Create basic set of claims used by the frontend var claims = new List() { new(ClaimTypes.Name, username), new(ClaimTypes.NameIdentifier, id), new(ClaimTypes.Email, email), new("UserId", userId), new("Permissions", permissions) }; // Enrich the frontend claims by extensions (used by plugins) foreach (var extension in Extensions) { claims.AddRange( await extension.GetFrontendClaims(User) ); } return claims.ToArray(); } [HttpGet("logout")] public async Task Logout() { await HttpContext.SignOutAsync(); await Results.Redirect("/").ExecuteAsync(HttpContext); } }