From 3962c0eb7a7e288122961fc08db899e2d9dcc136 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Thu, 9 Dec 2021 09:20:00 +0100 Subject: [PATCH] Removing Identity Server and creating a custom jwt --- .../AuthenticationModule.cs | 81 +++-------- .../Controllers/Certificates.cs | 137 ------------------ src/Kyoo.Authentication/Extensions.cs | 32 ---- .../Kyoo.Authentication.csproj | 11 +- .../Models/IdentityContext.cs | 121 ---------------- .../Models/Options/AuthenticationOption.cs | 4 +- .../Models/Options/CertificateOption.cs | 46 ------ src/Kyoo.Host.Generic/settings.json | 6 - 8 files changed, 23 insertions(+), 415 deletions(-) delete mode 100644 src/Kyoo.Authentication/Controllers/Certificates.cs delete mode 100644 src/Kyoo.Authentication/Models/IdentityContext.cs delete mode 100644 src/Kyoo.Authentication/Models/Options/CertificateOption.cs diff --git a/src/Kyoo.Authentication/AuthenticationModule.cs b/src/Kyoo.Authentication/AuthenticationModule.cs index ee3cfdc9..604521ca 100644 --- a/src/Kyoo.Authentication/AuthenticationModule.cs +++ b/src/Kyoo.Authentication/AuthenticationModule.cs @@ -18,27 +18,21 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.IO; -using System.Linq; using System.Reflection; +using System.Text; using Autofac; -using IdentityServer4.Extensions; -using IdentityServer4.Models; -using IdentityServer4.Services; using Kyoo.Abstractions; using Kyoo.Abstractions.Controllers; using Kyoo.Authentication.Models; -using Kyoo.Authentication.Views; +using Microsoft.AspNetCore.Authentication.JwtBearer; 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 Microsoft.IdentityModel.Tokens; using SameSiteMode = Microsoft.AspNetCore.Http.SameSiteMode; namespace Kyoo.Authentication @@ -55,14 +49,13 @@ namespace Kyoo.Authentication public string Name => "Authentication"; /// - public string Description => "Enable OpenID authentication for Kyoo."; + public string Description => "Enable an authentication/permission system for Kyoo (via Jwt or ApKeys)."; /// public Dictionary Configuration => new() { { AuthenticationOption.Path, typeof(AuthenticationOption) }, { PermissionOption.Path, typeof(PermissionOption) }, - { CertificateOption.Path, typeof(CertificateOption) } }; /// @@ -70,11 +63,6 @@ namespace Kyoo.Authentication /// private readonly IConfiguration _configuration; - /// - /// The logger used to allow IdentityServer to log things. - /// - private readonly ILogger _logger; - /// /// The environment information to check if the app runs in debug mode /// @@ -84,16 +72,11 @@ namespace Kyoo.Authentication /// Create a new authentication module instance and use the given configuration and environment. /// /// The configuration to use - /// The logger used to allow IdentityServer to log things /// The environment information to check if the app runs in debug mode - [SuppressMessage("ReSharper", "ContextualLoggerProblem", - Justification = "The logger is used for a dependency that is not created via the container.")] public AuthenticationModule(IConfiguration configuration, - ILogger logger, IWebHostEnvironment environment) { _configuration = configuration; - _logger = logger; _environment = environment; } @@ -101,59 +84,29 @@ namespace Kyoo.Authentication public void Configure(ContainerBuilder builder) { builder.RegisterType().As().SingleInstance(); - - DefaultCorsPolicyService cors = new(_logger) - { - AllowedOrigins = { _configuration.GetPublicUrl().GetLeftPart(UriPartial.Authority) } - }; - builder.RegisterInstance(cors).As().SingleInstance(); } /// public void Configure(IServiceCollection services) { Uri publicUrl = _configuration.GetPublicUrl(); - - if (_environment.IsDevelopment()) - IdentityModelEventSource.ShowPII = true; - - services.AddControllers(); + AuthenticationOption jwt = new(); + _configuration.GetSection(AuthenticationOption.Path).Bind(jwt); // 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); - - clients.AddRange(IdentityContext.GetClients()); - foreach (Client client in clients) - { - client.RedirectUris = client.RedirectUris - .Select(x => x.StartsWith("/") ? publicUrl.ToString().TrimEnd('/') + x : x) - .ToArray(); - } - - services.AddIdentityServer(options => - { - options.IssuerUri = publicUrl.ToString(); - 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(clients) - .AddProfileService() - .AddSigninKeys(certificateOptions); - - services.AddAuthentication() + services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { - options.Authority = publicUrl.ToString(); - options.Audience = "kyoo"; - options.RequireHttpsMetadata = false; + options.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuer = true, + ValidateAudience = true, + ValidateLifetime = true, + ValidateIssuerSigningKey = true, + ValidIssuer = publicUrl.ToString(), + ValidAudience = publicUrl.ToString(), + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwt.Secret)) + }; }); } diff --git a/src/Kyoo.Authentication/Controllers/Certificates.cs b/src/Kyoo.Authentication/Controllers/Certificates.cs deleted file mode 100644 index 05cb87cd..00000000 --- a/src/Kyoo.Authentication/Controllers/Certificates.cs +++ /dev/null @@ -1,137 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System; -using System.IO; -using System.Security.Cryptography.X509Certificates; -using Kyoo.Authentication.Models; -using Microsoft.Extensions.DependencyInjection; -using Org.BouncyCastle.Asn1.X509; -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Generators; -using Org.BouncyCastle.Crypto.Operators; -using Org.BouncyCastle.Math; -using Org.BouncyCastle.Pkcs; -using Org.BouncyCastle.Security; -using Org.BouncyCastle.Utilities; -using Org.BouncyCastle.X509; -using X509Certificate = Org.BouncyCastle.X509.X509Certificate; - -namespace Kyoo.Authentication -{ - /// - /// A class containing multiple extensions methods to manage certificates. - /// - public static class Certificates - { - /// - /// Add the certificate file to the identity server. If the certificate will expire soon, automatically renew it. - /// If no certificate exists, one is generated. - /// - /// The identity server that will be modified. - /// The certificate options - /// The initial builder to allow chain-calls. - public static IIdentityServerBuilder AddSigninKeys(this IIdentityServerBuilder builder, - CertificateOption options) - { - X509Certificate2 certificate = _GetCertificate(options); - builder.AddSigningCredential(certificate); - - if (certificate.NotAfter.AddDays(-7) <= DateTime.UtcNow) - { - Console.WriteLine("Signin certificate will expire soon, renewing it."); - if (File.Exists(options.OldFile)) - File.Delete(options.OldFile); - File.Move(options.File, options.OldFile); - builder.AddValidationKey(_GenerateCertificate(options.File, options.Password)); - } - else if (File.Exists(options.OldFile)) - builder.AddValidationKey(_GetExistingCredential(options.OldFile, options.Password)); - return builder; - } - - /// - /// Get or generate the sign-in certificate. - /// - /// The certificate options - /// A valid certificate - private static X509Certificate2 _GetCertificate(CertificateOption options) - { - return File.Exists(options.File) - ? _GetExistingCredential(options.File, options.Password) - : _GenerateCertificate(options.File, options.Password); - } - - /// - /// Load a certificate from a file - /// - /// The path of the certificate - /// The password of the certificate - /// The loaded certificate - private static X509Certificate2 _GetExistingCredential(string file, string password) - { - X509KeyStorageFlags storeFlags = X509KeyStorageFlags.MachineKeySet | - X509KeyStorageFlags.PersistKeySet | - X509KeyStorageFlags.Exportable; - return new X509Certificate2(file, password, storeFlags); - } - - /// - /// Generate a new certificate key and put it in the file at . - /// - /// The path of the output file - /// The password of the new certificate - /// The generated certificate - private static X509Certificate2 _GenerateCertificate(string file, string password) - { - SecureRandom random = new(); - - X509V3CertificateGenerator certificateGenerator = new(); - certificateGenerator.SetSerialNumber(BigIntegers.CreateRandomInRange(BigInteger.One, - BigInteger.ValueOf(long.MaxValue), random)); - certificateGenerator.SetIssuerDN(new X509Name($"C=NL, O=SDG, CN=Kyoo")); - certificateGenerator.SetSubjectDN(new X509Name($"C=NL, O=SDG, CN=Kyoo")); - certificateGenerator.SetNotBefore(DateTime.UtcNow.Date); - certificateGenerator.SetNotAfter(DateTime.UtcNow.Date.AddMonths(3)); - - KeyGenerationParameters keyGenerationParameters = new(random, 2048); - RsaKeyPairGenerator keyPairGenerator = new(); - keyPairGenerator.Init(keyGenerationParameters); - - AsymmetricCipherKeyPair subjectKeyPair = keyPairGenerator.GenerateKeyPair(); - certificateGenerator.SetPublicKey(subjectKeyPair.Public); - - const string signatureAlgorithm = "MD5WithRSA"; - Asn1SignatureFactory signatureFactory = new(signatureAlgorithm, subjectKeyPair.Private); - X509Certificate bouncyCert = certificateGenerator.Generate(signatureFactory); - - Pkcs12Store store = new Pkcs12StoreBuilder().Build(); - store.SetKeyEntry("Kyoo_key", new AsymmetricKeyEntry(subjectKeyPair.Private), new[] - { - new X509CertificateEntry(bouncyCert) - }); - - using MemoryStream pfxStream = new(); - store.Save(pfxStream, password.ToCharArray(), random); - X509Certificate2 certificate = new(pfxStream.ToArray(), password, X509KeyStorageFlags.Exportable); - using FileStream fileStream = File.OpenWrite(file); - pfxStream.WriteTo(fileStream); - return certificate; - } - } -} diff --git a/src/Kyoo.Authentication/Extensions.cs b/src/Kyoo.Authentication/Extensions.cs index 73b9deed..658da84e 100644 --- a/src/Kyoo.Authentication/Extensions.cs +++ b/src/Kyoo.Authentication/Extensions.cs @@ -19,9 +19,6 @@ using System.Collections.Generic; using System.Linq; using System.Security.Claims; -using IdentityModel; -using IdentityServer4; -using Kyoo.Abstractions.Models; namespace Kyoo.Authentication { @@ -30,35 +27,6 @@ namespace Kyoo.Authentication /// public static class Extensions { - /// - /// Get claims of an user. - /// - /// The user concerned - /// The list of claims the user has - public static ICollection GetClaims(this User user) - { - return new[] - { - new Claim(JwtClaimTypes.Subject, user.ID.ToString()), - new Claim(JwtClaimTypes.Name, user.Username), - new Claim(JwtClaimTypes.Picture, $"api/account/picture/{user.Slug}") - }; - } - - /// - /// Convert a user to an IdentityServerUser. - /// - /// The user to convert. - /// The corresponding identity server user. - public static IdentityServerUser ToIdentityUser(this User user) - { - return new IdentityServerUser(user.ID.ToString()) - { - DisplayName = user.Username, - AdditionalClaims = new[] { new Claim("permissions", string.Join(',', user.Permissions)) } - }; - } - /// /// Get the permissions of an user. /// diff --git a/src/Kyoo.Authentication/Kyoo.Authentication.csproj b/src/Kyoo.Authentication/Kyoo.Authentication.csproj index 2940dc1f..fd863584 100644 --- a/src/Kyoo.Authentication/Kyoo.Authentication.csproj +++ b/src/Kyoo.Authentication/Kyoo.Authentication.csproj @@ -1,19 +1,16 @@ - + ../Kyoo.WebLogin/ - - - - - + + - + diff --git a/src/Kyoo.Authentication/Models/IdentityContext.cs b/src/Kyoo.Authentication/Models/IdentityContext.cs deleted file mode 100644 index 4c946653..00000000 --- a/src/Kyoo.Authentication/Models/IdentityContext.cs +++ /dev/null @@ -1,121 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -using System.Collections.Generic; -using System.Linq; -using IdentityServer4.Models; - -namespace Kyoo.Authentication -{ - /// - /// The hard coded context of the identity server. - /// - public static class IdentityContext - { - /// - /// The list of identity resources supported (email, profile and openid) - /// - /// The list of identity resources supported - public static IEnumerable GetIdentityResources() - { - return new List - { - new IdentityResources.OpenId(), - new IdentityResources.Email(), - new IdentityResources.Profile() - }; - } - - /// - /// Get the list of officially supported clients. - /// - /// - /// You can add custom clients in the settings.json file. - /// - /// The list of officially supported clients. - public static IEnumerable GetClients() - { - return new List - { - new() - { - ClientId = "kyoo.webapp", - - AccessTokenType = AccessTokenType.Jwt, - AllowedGrantTypes = GrantTypes.Code, - RequirePkce = true, - RequireClientSecret = false, - - AllowAccessTokensViaBrowser = true, - AllowOfflineAccess = true, - RequireConsent = false, - - AllowedScopes = { "openid", "profile", "kyoo.read", "kyoo.write", "kyoo.play", "kyoo.admin" }, - RedirectUris = { "/", "/silent.html" }, - PostLogoutRedirectUris = { "/logout" } - } - }; - } - - /// - /// The list of scopes supported by the API. - /// - /// The list of scopes - public static IEnumerable GetScopes() - { - return new[] - { - new ApiScope - { - Name = "kyoo.read", - DisplayName = "Read only access to the API.", - }, - new ApiScope - { - Name = "kyoo.write", - DisplayName = "Read and write access to the public API" - }, - new ApiScope - { - Name = "kyoo.play", - DisplayName = "Allow playback of movies and episodes." - }, - new ApiScope - { - Name = "kyoo.admin", - DisplayName = "Full access to the admin's API and the public API." - } - }; - } - - /// - /// The list of APIs (this is used to create Audiences) - /// - /// The list of apis - public static IEnumerable GetApis() - { - return new[] - { - new ApiResource("kyoo", "Kyoo") - { - Scopes = GetScopes().Select(x => x.Name).ToArray() - } - }; - } - } -} diff --git a/src/Kyoo.Authentication/Models/Options/AuthenticationOption.cs b/src/Kyoo.Authentication/Models/Options/AuthenticationOption.cs index 1b43281f..dc8209aa 100644 --- a/src/Kyoo.Authentication/Models/Options/AuthenticationOption.cs +++ b/src/Kyoo.Authentication/Models/Options/AuthenticationOption.cs @@ -29,9 +29,9 @@ namespace Kyoo.Authentication.Models public const string Path = "authentication"; /// - /// The options for certificates + /// The secret used to encrypt the jwt. /// - public CertificateOption Certificate { get; set; } + public string Secret { get; set; } /// /// Options for permissions diff --git a/src/Kyoo.Authentication/Models/Options/CertificateOption.cs b/src/Kyoo.Authentication/Models/Options/CertificateOption.cs deleted file mode 100644 index 8e697a7f..00000000 --- a/src/Kyoo.Authentication/Models/Options/CertificateOption.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Kyoo - A portable and vast media library solution. -// Copyright (c) Kyoo. -// -// See AUTHORS.md and LICENSE file in the project root for full license information. -// -// Kyoo is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// any later version. -// -// Kyoo is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Kyoo. If not, see . - -namespace Kyoo.Authentication.Models -{ - /// - /// A typed option model for the certificate - /// - public class CertificateOption - { - /// - /// The path to get this option from the root configuration. - /// - public const string Path = "authentication:certificate"; - - /// - /// The path of the certificate file. - /// - public string File { get; set; } - - /// - /// The path of the old certificate file. - /// - public string OldFile { get; set; } - - /// - /// The password of the certificates. - /// - public string Password { get; set; } - } -} diff --git a/src/Kyoo.Host.Generic/settings.json b/src/Kyoo.Host.Generic/settings.json index bf5e6a4e..817536c2 100644 --- a/src/Kyoo.Host.Generic/settings.json +++ b/src/Kyoo.Host.Generic/settings.json @@ -60,17 +60,11 @@ }, "authentication": { - "certificate": { - "file": "certificate.pfx", - "oldFile": "oldCertificate.pfx", - "password": "passphrase" - }, "permissions": { "default": ["overall.read", "overall.write", "overall.create", "overall.delete", "admin.read", "admin.write"], "newUser": ["overall.read", "overall.write", "overall.create", "overall.delete", "admin.read", "admin.write"] }, "profilePicturePath": "users/", - "clients": [] }, "tvdb": {