using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using IdentityServer4.Extensions; using IdentityServer4.Models; using IdentityServer4.Services; using Kyoo.Authentication.Models; using Kyoo.Authentication.Views; using Kyoo.Controllers; using Kyoo.Models.Permissions; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.IdentityModel.Logging; using SameSiteMode = Microsoft.AspNetCore.Http.SameSiteMode; namespace Kyoo.Authentication { /// /// A module that enable OpenID authentication for Kyoo. /// public class AuthenticationModule : IPlugin { /// public string Slug => "auth"; /// public string Name => "Authentication"; /// public string Description => "Enable OpenID authentication for Kyoo."; /// public Dictionary Configuration => new() { { AuthenticationOption.Path, typeof(AuthenticationOption) } }; /// /// The configuration to use. /// private readonly IConfiguration _configuration; /// /// A logger factory to allow IdentityServer to log things. /// private readonly ILoggerFactory _loggerFactory; /// /// The environment information to check if the app runs in debug mode /// private readonly IWebHostEnvironment _environment; /// /// Create a new authentication module instance and use the given configuration and environment. /// /// The configuration to use /// The logger factory to allow IdentityServer to log things /// The environment information to check if the app runs in debug mode public AuthenticationModule(IConfiguration configuration, ILoggerFactory loggerFactory, IWebHostEnvironment environment) { _configuration = configuration; _loggerFactory = loggerFactory; _environment = environment; } /// public void Configure(IServiceCollection services) { string publicUrl = _configuration.GetPublicUrl(); if (_environment.IsDevelopment()) IdentityModelEventSource.ShowPII = true; services.AddControllers(); // TODO handle direct-videos with bearers (probably add a cookie and a app.Use to translate that for videos) // TODO Check if tokens should be stored. List clients = new(); _configuration.GetSection("authentication:clients").Bind(clients); CertificateOption certificateOptions = new(); _configuration.GetSection(CertificateOption.Path).Bind(certificateOptions); services.AddIdentityServer(options => { options.IssuerUri = publicUrl; options.UserInteraction.LoginUrl = $"{publicUrl}/login"; options.UserInteraction.ErrorUrl = $"{publicUrl}/error"; options.UserInteraction.LogoutUrl = $"{publicUrl}/logout"; }) .AddInMemoryIdentityResources(IdentityContext.GetIdentityResources()) .AddInMemoryApiScopes(IdentityContext.GetScopes()) .AddInMemoryApiResources(IdentityContext.GetApis()) .AddInMemoryClients(IdentityContext.GetClients().Concat(clients)) .AddProfileService() .AddSigninKeys(certificateOptions); services.AddAuthentication() .AddJwtBearer(options => { options.Authority = publicUrl; options.Audience = "kyoo"; options.RequireHttpsMetadata = false; }); services.AddSingleton(); DefaultCorsPolicyService cors = new(_loggerFactory.CreateLogger()) { AllowedOrigins = {new Uri(publicUrl).GetLeftPart(UriPartial.Authority)} }; services.AddSingleton(cors); } /// public IEnumerable ConfigureSteps => new IStartupAction[] { SA.New(app => { PhysicalFileProvider provider = new(Path.Combine( Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "login")); app.UseDefaultFiles(new DefaultFilesOptions { RequestPath = new PathString("/login"), FileProvider = provider, RedirectToAppendTrailingSlash = true }); app.UseStaticFiles(new StaticFileOptions { RequestPath = new PathString("/login"), FileProvider = provider }); }, SA.StaticFiles), SA.New(app => { app.UseCookiePolicy(new CookiePolicyOptions { MinimumSameSitePolicy = SameSiteMode.Strict }); app.UseAuthentication(); }, SA.Authentication), SA.New(app => { app.Use((ctx, next) => { ctx.SetIdentityServerOrigin(_configuration.GetPublicUrl()); return next(); }); app.UseIdentityServer(); }, SA.Endpoint), SA.New(app => app.UseAuthorization(), SA.Authorization) }; } }