From 0c2bd27207f794358b7192f68d8872e2c458106e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Sat, 28 Feb 2026 13:34:37 +0100 Subject: [PATCH] Initialize the database before starting the web host instead of using an IHostedService implementation --- .../Program.cs | 34 +- .../Startup.cs | 4 - .../Worker.cs | 21 - .../Program.cs | 439 +++++++++++++++++- .../Startup.cs | 4 - .../Worker.cs | 430 ----------------- .../Program.cs | 18 +- .../Worker.cs | 23 - .../{Worker.cs => MauiDatabaseInitializer.cs} | 2 +- .../MauiProgram.cs | 2 +- .../Program.cs | 37 +- .../Worker.cs | 46 -- .../OpenIddict.Sandbox.Wpf.Client/Program.cs | 39 +- .../OpenIddict.Sandbox.Wpf.Client/Worker.cs | 46 -- 14 files changed, 523 insertions(+), 622 deletions(-) delete mode 100644 sandbox/OpenIddict.Sandbox.AspNetCore.Client/Worker.cs delete mode 100644 sandbox/OpenIddict.Sandbox.AspNetCore.Server/Worker.cs delete mode 100644 sandbox/OpenIddict.Sandbox.Console.Client/Worker.cs rename sandbox/OpenIddict.Sandbox.Maui.Client/{Worker.cs => MauiDatabaseInitializer.cs} (81%) delete mode 100644 sandbox/OpenIddict.Sandbox.WinForms.Client/Worker.cs delete mode 100644 sandbox/OpenIddict.Sandbox.Wpf.Client/Worker.cs diff --git a/sandbox/OpenIddict.Sandbox.AspNetCore.Client/Program.cs b/sandbox/OpenIddict.Sandbox.AspNetCore.Client/Program.cs index b5bbf62a..d1ba3810 100644 --- a/sandbox/OpenIddict.Sandbox.AspNetCore.Client/Program.cs +++ b/sandbox/OpenIddict.Sandbox.AspNetCore.Client/Program.cs @@ -1,22 +1,24 @@ using Microsoft.AspNetCore; +using OpenIddict.Sandbox.AspNetCore.Client; +using OpenIddict.Sandbox.AspNetCore.Client.Models; -namespace OpenIddict.Sandbox.AspNetCore.Client; - -public static class Program -{ #if SUPPORTS_WEB_INTEGRATION_IN_GENERIC_HOST - public static void Main(string[] args) => - CreateHostBuilder(args).Build().Run(); - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(builder => builder.UseStartup()); +var builder = Host.CreateDefaultBuilder(args); +builder.ConfigureWebHostDefaults(builder => builder.UseStartup()); #else - public static void Main(string[] args) => - CreateWebHostBuilder(args).Build().Run(); - - public static IWebHostBuilder CreateWebHostBuilder(string[] args) => - WebHost.CreateDefaultBuilder(args) - .UseStartup(); +var builder = WebHost.CreateDefaultBuilder(args); +builder.UseStartup(); #endif + +var app = builder.Build(); + +// Before starting the host, create the database used to store the application data. +// +// Note: in a real world application, this step should be part of a setup script. +await using (var scope = app.Services.CreateAsyncScope()) +{ + var context = scope.ServiceProvider.GetRequiredService(); + await context.Database.EnsureCreatedAsync(); } + +await app.RunAsync(); diff --git a/sandbox/OpenIddict.Sandbox.AspNetCore.Client/Startup.cs b/sandbox/OpenIddict.Sandbox.AspNetCore.Client/Startup.cs index 73394e40..3a6392dd 100644 --- a/sandbox/OpenIddict.Sandbox.AspNetCore.Client/Startup.cs +++ b/sandbox/OpenIddict.Sandbox.AspNetCore.Client/Startup.cs @@ -210,10 +210,6 @@ public class Startup #endif services.AddMvc(); - - // Register the worker responsible for creating the database used to store tokens. - // Note: in a real world application, this step should be part of a setup script. - services.AddHostedService(); } public void Configure(IApplicationBuilder app) diff --git a/sandbox/OpenIddict.Sandbox.AspNetCore.Client/Worker.cs b/sandbox/OpenIddict.Sandbox.AspNetCore.Client/Worker.cs deleted file mode 100644 index 62108875..00000000 --- a/sandbox/OpenIddict.Sandbox.AspNetCore.Client/Worker.cs +++ /dev/null @@ -1,21 +0,0 @@ -using OpenIddict.Sandbox.AspNetCore.Client.Models; - -namespace OpenIddict.Sandbox.AspNetCore.Client; - -public class Worker : IHostedService -{ - private readonly IServiceProvider _provider; - - public Worker(IServiceProvider provider) - => _provider = provider; - - public async Task StartAsync(CancellationToken cancellationToken) - { - await using var scope = _provider.CreateAsyncScope(); - - var context = scope.ServiceProvider.GetRequiredService(); - await context.Database.EnsureCreatedAsync(cancellationToken); - } - - public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; -} diff --git a/sandbox/OpenIddict.Sandbox.AspNetCore.Server/Program.cs b/sandbox/OpenIddict.Sandbox.AspNetCore.Server/Program.cs index ccb00df5..b31a0b5c 100644 --- a/sandbox/OpenIddict.Sandbox.AspNetCore.Server/Program.cs +++ b/sandbox/OpenIddict.Sandbox.AspNetCore.Server/Program.cs @@ -1,22 +1,433 @@ -using Microsoft.AspNetCore; +using System.Globalization; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using Microsoft.AspNetCore; +using Microsoft.IdentityModel.Tokens; +using OpenIddict.Abstractions; +using OpenIddict.Sandbox.AspNetCore.Server; +using OpenIddict.Sandbox.AspNetCore.Server.Models; +using static OpenIddict.Abstractions.OpenIddictConstants; -namespace OpenIddict.Sandbox.AspNetCore.Server; +#if SUPPORTS_WEB_INTEGRATION_IN_GENERIC_HOST +var builder = Host.CreateDefaultBuilder(args); +builder.ConfigureWebHostDefaults(builder => builder.UseStartup()); +#else +var builder = WebHost.CreateDefaultBuilder(args); +builder.UseStartup(); +#endif -public static class Program +var app = builder.Build(); + +// Before starting the host, create the database used to store the application data. +// +// Note: in a real world application, this step should be part of a setup script. +await using (var scope = app.Services.CreateAsyncScope()) { -#if SUPPORTS_WEB_INTEGRATION_IN_GENERIC_HOST - public static void Main(string[] args) => - CreateHostBuilder(args).Build().Run(); + var context = scope.ServiceProvider.GetRequiredService(); + await context.Database.EnsureCreatedAsync(); + + await RegisterApplicationsAsync(scope.ServiceProvider); + await RegisterScopesAsync(scope.ServiceProvider); +} + +await app.RunAsync(); + +static async Task RegisterApplicationsAsync(IServiceProvider provider) +{ + var manager = provider.GetRequiredService(); - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(builder => builder.UseStartup()); + if (await manager.FindByClientIdAsync("console") is null) + { + var descriptor = new OpenIddictApplicationDescriptor + { + // Note: the application must be registered as a native application to force OpenIddict + // to apply a relaxed redirect_uri validation policy that allows specifying a random port. + ApplicationType = ApplicationTypes.Native, + ClientId = "console", + ClientType = ClientTypes.Public, + ConsentType = ConsentTypes.Systematic, + DisplayName = "Console client application", + DisplayNames = + { + [CultureInfo.GetCultureInfo("fr-FR")] = "Application cliente console" + }, + PostLogoutRedirectUris = + { + // Note: the port must not be explicitly specified as it is selected + // dynamically at runtime by the OpenIddict client system integration. + new Uri("http://localhost/callback/logout/local") + }, + RedirectUris = + { + // Note: the port must not be explicitly specified as it is selected + // dynamically at runtime by the OpenIddict client system integration. + new Uri("http://localhost/callback/login/local") + }, + Permissions = + { + Permissions.Endpoints.Authorization, + Permissions.Endpoints.DeviceAuthorization, + Permissions.Endpoints.Introspection, + Permissions.Endpoints.EndSession, + Permissions.Endpoints.PushedAuthorization, + Permissions.Endpoints.Revocation, + Permissions.Endpoints.Token, + Permissions.GrantTypes.AuthorizationCode, + Permissions.GrantTypes.DeviceCode, + Permissions.GrantTypes.Implicit, + Permissions.GrantTypes.Password, + Permissions.GrantTypes.RefreshToken, + Permissions.GrantTypes.TokenExchange, + Permissions.ResponseTypes.Code, + Permissions.ResponseTypes.CodeIdToken, + Permissions.ResponseTypes.CodeIdTokenToken, + Permissions.ResponseTypes.CodeToken, + Permissions.ResponseTypes.IdToken, + Permissions.ResponseTypes.IdTokenToken, + Permissions.ResponseTypes.None, + Permissions.Scopes.Email, + Permissions.Scopes.Profile, + Permissions.Scopes.Roles + }, + Requirements = + { + Requirements.Features.ProofKeyForCodeExchange, + Requirements.Features.PushedAuthorizationRequests + } + }; + + descriptor.AddScopePermissions("demo_api"); + + await manager.CreateAsync(descriptor); + } + + if (await manager.FindByClientIdAsync("maui") is null) + { + var descriptor = new OpenIddictApplicationDescriptor + { + ApplicationType = ApplicationTypes.Native, + ClientId = "maui", + ClientType = ClientTypes.Public, + ConsentType = ConsentTypes.Systematic, + DisplayName = "MAUI client application", + DisplayNames = + { + [CultureInfo.GetCultureInfo("fr-FR")] = "Application cliente MAUI" + }, + PostLogoutRedirectUris = + { + new Uri("com.openiddict.sandbox.maui.client:/callback/logout/local") + }, + RedirectUris = + { + new Uri("com.openiddict.sandbox.maui.client:/callback/login/local") + }, + Permissions = + { + Permissions.Endpoints.Authorization, + Permissions.Endpoints.EndSession, + Permissions.Endpoints.PushedAuthorization, + Permissions.Endpoints.Token, + Permissions.GrantTypes.AuthorizationCode, + Permissions.GrantTypes.RefreshToken, + Permissions.ResponseTypes.Code, + Permissions.Scopes.Email, + Permissions.Scopes.Profile, + Permissions.Scopes.Roles + }, + Requirements = + { + Requirements.Features.ProofKeyForCodeExchange, + Requirements.Features.PushedAuthorizationRequests + } + }; + + descriptor.AddScopePermissions("demo_api"); + + await manager.CreateAsync(descriptor); + } + + if (await manager.FindByClientIdAsync("mvc") is null) + { + var descriptor = new OpenIddictApplicationDescriptor + { + ApplicationType = ApplicationTypes.Web, + ClientId = "mvc", + ClientType = ClientTypes.Confidential, + ConsentType = ConsentTypes.Systematic, + DisplayName = "MVC client application", + DisplayNames = + { + [CultureInfo.GetCultureInfo("fr-FR")] = "Application cliente MVC" + }, +#if SUPPORTS_PEM_ENCODED_KEY_IMPORT + JsonWebKeySet = new JsonWebKeySet + { + Keys = + { + // On supported platforms, this application can authenticate by using a + // self-signed client authentication certificate during the TLS handshake + // (a method known as "mutual TLS" or mTLS). + // + // Note: while the client needs access to the private key, the server only needs + // to know the public part to be able to validate the certificates it receives. + JsonWebKeyConverter.ConvertFromX509SecurityKey(new X509SecurityKey( + X509Certificate2.CreateFromPem($""" + -----BEGIN CERTIFICATE----- + MIIC8zCCAdugAwIBAgIJAIZ9BN3TUnZQMA0GCSqGSIb3DQEBCwUAMCIxIDAeBgNV + BAMTF1NlbGYtc2lnbmVkIGNlcnRpZmljYXRlMCAXDTI2MDIwMjE0MzM0OVoYDzIx + MjYwMjAyMTQzMzQ5WjAiMSAwHgYDVQQDExdTZWxmLXNpZ25lZCBjZXJ0aWZpY2F0 + ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOtfKVPM7ghVFh4U/sz4 + sTrpaNJGQ2NORqawYxAHwluhr101yIOW7rWvFlFncA64Lkq9SAbFFCVSAbo28c6B + 2Mi41jyC4LHQU11jhv08K/3FUuckCuzEpzTnXUhxJHWxrRDVEuvKINGPs1VgVtTT + ra8rjP8s1YRAzCYnByxSx+8GXNGHprylLh0agpWKb2+2FYwDqY5ME2g3xTL9FTUu + FYWTcyspsvN0U1Eo1vlCeOxSYGPRct0MK0AS6eXEGBv+3kCYI7a5+UhQok0WvErF + pjIVo7USISDgKhW9GhTsWN+WywwdG4Kx4V6SB8ZLAHFSBSR3gjWS3TGOyqAWoBXc + znkCAwEAAaMqMCgwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsGAQUF + BwMCMA0GCSqGSIb3DQEBCwUAA4IBAQBf5i/S7shmNalVxMuP8/Mk8cOhRRZjnAXd + zz3eOuXu0CH8iY/DwCgss04O2NTxuz87rKiuNKOrtY0oN/G4aFjWPvbgoQ+N1XP1 + zvbhqbyo3fQr07FyjWkrIUoHYFQ3JRfL+GPGjWizJsgdpdCRJSK6G9VX8eU3Akjv + YhMRLmbkrH5etOURqFtLpZlxNmLzCpqWIvzRiYyyj74iOipA2I0acgcvkakWn6rE + Wio7luBAZ3dXlukEfHTOg+ft4k0nOlRXPTtASOmyFQBOs6iYJeztHDz6MQnknAPe + +W53US8kLWktspcOQmxhVVH1g1/T4ynl9iX7tzqvUbdYwZNi92+x + -----END CERTIFICATE----- + """))), + + // On supported platforms, this application can also authenticate by + // generating JWT client assertions that are signed using a signing key. + // + // Note: while the client needs access to the private key, the server only needs + // to know the public key to be able to validate the client assertions it receives. + JsonWebKeyConverter.ConvertFromECDsaSecurityKey(GetECDsaSigningKey($""" + -----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEI23kaVsRRAWIez/pqEZOByJFmlXd + a6iSQ4QqcH23Ir8aYPPX5lsVnBsExNsl7SOYOiIhgTaX6+PTS7yxTnmvSw== + -----END PUBLIC KEY----- + """)) + } + }, #else - public static void Main(string[] args) => - CreateWebHostBuilder(args).Build().Run(); + ClientSecret = "emCimpdc9SeOaZzN5jzm4_eek-STF6VenfVlKO1_qt0", +#endif + RedirectUris = + { + new Uri("https://localhost:44381/callback/login/local") + }, + PostLogoutRedirectUris = + { + new Uri("https://localhost:44381/callback/logout/local") + }, + Permissions = + { + Permissions.Endpoints.Authorization, + Permissions.Endpoints.EndSession, + Permissions.Endpoints.PushedAuthorization, + Permissions.Endpoints.Token, + Permissions.GrantTypes.AuthorizationCode, + Permissions.GrantTypes.RefreshToken, + Permissions.ResponseTypes.Code, + Permissions.Scopes.Email, + Permissions.Scopes.Profile, + Permissions.Scopes.Roles + }, + Requirements = + { + Requirements.Features.ProofKeyForCodeExchange, + Requirements.Features.PushedAuthorizationRequests + } + }; + + descriptor.AddScopePermissions("demo_api"); - public static IWebHostBuilder CreateWebHostBuilder(string[] args) => - WebHost.CreateDefaultBuilder(args) - .UseStartup(); + await manager.CreateAsync(descriptor); + } + + if (await manager.FindByClientIdAsync("winforms") is null) + { + var descriptor = new OpenIddictApplicationDescriptor + { + ApplicationType = ApplicationTypes.Native, + ClientId = "winforms", + ClientType = ClientTypes.Public, + ConsentType = ConsentTypes.Systematic, + DisplayName = "WinForms client application", + DisplayNames = + { + [CultureInfo.GetCultureInfo("fr-FR")] = "Application cliente WinForms" + }, + PostLogoutRedirectUris = + { + new Uri("com.openiddict.sandbox.winforms.client:/callback/logout/local") + }, + RedirectUris = + { + new Uri("com.openiddict.sandbox.winforms.client:/callback/login/local") + }, + Permissions = + { + Permissions.Endpoints.Authorization, + Permissions.Endpoints.EndSession, + Permissions.Endpoints.PushedAuthorization, + Permissions.Endpoints.Token, + Permissions.GrantTypes.AuthorizationCode, + Permissions.GrantTypes.RefreshToken, + Permissions.ResponseTypes.Code, + Permissions.Scopes.Email, + Permissions.Scopes.Profile, + Permissions.Scopes.Roles + }, + Requirements = + { + Requirements.Features.ProofKeyForCodeExchange, + Requirements.Features.PushedAuthorizationRequests + } + }; + + descriptor.AddScopePermissions("demo_api"); + + await manager.CreateAsync(descriptor); + } + + if (await manager.FindByClientIdAsync("wpf") is null) + { + var descriptor = new OpenIddictApplicationDescriptor + { + ApplicationType = ApplicationTypes.Native, + ClientId = "wpf", + ClientType = ClientTypes.Public, + ConsentType = ConsentTypes.Systematic, + DisplayName = "WPF client application", + DisplayNames = + { + [CultureInfo.GetCultureInfo("fr-FR")] = "Application cliente WPF" + }, + PostLogoutRedirectUris = + { + new Uri("com.openiddict.sandbox.wpf.client:/callback/logout/local") + }, + RedirectUris = + { + new Uri("com.openiddict.sandbox.wpf.client:/callback/login/local") + }, + Permissions = + { + Permissions.Endpoints.Authorization, + Permissions.Endpoints.EndSession, + Permissions.Endpoints.PushedAuthorization, + Permissions.Endpoints.Token, + Permissions.GrantTypes.AuthorizationCode, + Permissions.GrantTypes.RefreshToken, + Permissions.ResponseTypes.Code, + Permissions.Scopes.Email, + Permissions.Scopes.Profile, + Permissions.Scopes.Roles + }, + Requirements = + { + Requirements.Features.ProofKeyForCodeExchange, + Requirements.Features.PushedAuthorizationRequests + } + }; + + descriptor.AddScopePermissions("demo_api"); + + await manager.CreateAsync(descriptor); + } + + // Note: when using introspection instead of local token validation, + // an application entry MUST be created to allow the resource server + // to communicate with OpenIddict's introspection endpoint. + if (await manager.FindByClientIdAsync("resource_server") is null) + { + var descriptor = new OpenIddictApplicationDescriptor + { + ClientId = "resource_server", + ClientSecret = "vVQ-yjr42sXP5VHj6AswkXuS7MU1i2gFjvJjY0TdGMk", + ClientType = ClientTypes.Confidential, + Permissions = + { + Permissions.Endpoints.Introspection + } + }; + + await manager.CreateAsync(descriptor); + } + + // To test this sample with Postman, use the following settings: + // + // * Authorization URL: https://localhost:44395/connect/authorize + // * Access token URL: https://localhost:44395/connect/token + // * Client ID: postman + // * Client secret: [blank] (not used with public clients) + // * Scope: openid email profile roles + // * Grant type: authorization code + // * Request access token locally: yes + if (await manager.FindByClientIdAsync("postman") is null) + { + var descriptor = new OpenIddictApplicationDescriptor + { + ApplicationType = ApplicationTypes.Native, + ClientId = "postman", + ClientType = ClientTypes.Public, + ConsentType = ConsentTypes.Systematic, + DisplayName = "Postman", + RedirectUris = + { + new Uri("https://oauth.pstmn.io/v1/callback") + }, + Permissions = + { + Permissions.Endpoints.Authorization, + Permissions.Endpoints.DeviceAuthorization, + Permissions.Endpoints.Token, + Permissions.GrantTypes.AuthorizationCode, + Permissions.GrantTypes.DeviceCode, + Permissions.GrantTypes.Password, + Permissions.GrantTypes.RefreshToken, + Permissions.ResponseTypes.Code, + Permissions.Scopes.Email, + Permissions.Scopes.Profile, + Permissions.Scopes.Roles + } + }; + + // Use a shorter access token lifetime for tokens issued to the Postman application. + descriptor.SetAccessTokenLifetime(TimeSpan.FromMinutes(10)); + + await manager.CreateAsync(descriptor); + } + +#if SUPPORTS_PEM_ENCODED_KEY_IMPORT + static ECDsaSecurityKey GetECDsaSigningKey(ReadOnlySpan key) + { + var algorithm = ECDsa.Create(); + algorithm.ImportFromPem(key); + + return new ECDsaSecurityKey(algorithm); + } #endif } + +static async Task RegisterScopesAsync(IServiceProvider provider) +{ + var manager = provider.GetRequiredService(); + + if (await manager.FindByNameAsync("demo_api") is null) + { + var descriptor = new OpenIddictScopeDescriptor + { + DisplayName = "Demo API access", + DisplayNames = + { + [CultureInfo.GetCultureInfo("fr-FR")] = "Accès à l'API de démo" + }, + Name = "demo_api", + Resources = + { + "resource_server" + } + }; + + await manager.CreateAsync(descriptor); + } +} diff --git a/sandbox/OpenIddict.Sandbox.AspNetCore.Server/Startup.cs b/sandbox/OpenIddict.Sandbox.AspNetCore.Server/Startup.cs index 361c92cb..183c3b8c 100644 --- a/sandbox/OpenIddict.Sandbox.AspNetCore.Server/Startup.cs +++ b/sandbox/OpenIddict.Sandbox.AspNetCore.Server/Startup.cs @@ -315,10 +315,6 @@ public class Startup services.AddTransient(); services.AddTransient(); - // Register the worker responsible for seeding the database with the sample clients. - // Note: in a real world application, this step should be part of a setup script. - services.AddHostedService(); - #if SUPPORTS_KESTREL_TLS_HANDSHAKE_CALLBACK_OPTIONS // Configure Kestrel to listen on the 44395 port and configure it to enforce mTLS. // diff --git a/sandbox/OpenIddict.Sandbox.AspNetCore.Server/Worker.cs b/sandbox/OpenIddict.Sandbox.AspNetCore.Server/Worker.cs deleted file mode 100644 index 8653b910..00000000 --- a/sandbox/OpenIddict.Sandbox.AspNetCore.Server/Worker.cs +++ /dev/null @@ -1,430 +0,0 @@ -using System.Globalization; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; -using Microsoft.IdentityModel.Tokens; -using OpenIddict.Abstractions; -using OpenIddict.Sandbox.AspNetCore.Server.Models; -using static OpenIddict.Abstractions.OpenIddictConstants; - -namespace OpenIddict.Sandbox.AspNetCore.Server; - -public class Worker : IHostedService -{ - private readonly IServiceProvider _provider; - - public Worker(IServiceProvider provider) - => _provider = provider; - - public async Task StartAsync(CancellationToken cancellationToken) - { - await using var scope = _provider.CreateAsyncScope(); - - var context = scope.ServiceProvider.GetRequiredService(); - await context.Database.EnsureCreatedAsync(cancellationToken); - - await RegisterApplicationsAsync(scope.ServiceProvider); - await RegisterScopesAsync(scope.ServiceProvider); - - static async Task RegisterApplicationsAsync(IServiceProvider provider) - { - var manager = provider.GetRequiredService(); - - if (await manager.FindByClientIdAsync("console") is null) - { - var descriptor = new OpenIddictApplicationDescriptor - { - // Note: the application must be registered as a native application to force OpenIddict - // to apply a relaxed redirect_uri validation policy that allows specifying a random port. - ApplicationType = ApplicationTypes.Native, - ClientId = "console", - ClientType = ClientTypes.Public, - ConsentType = ConsentTypes.Systematic, - DisplayName = "Console client application", - DisplayNames = - { - [CultureInfo.GetCultureInfo("fr-FR")] = "Application cliente console" - }, - PostLogoutRedirectUris = - { - // Note: the port must not be explicitly specified as it is selected - // dynamically at runtime by the OpenIddict client system integration. - new Uri("http://localhost/callback/logout/local") - }, - RedirectUris = - { - // Note: the port must not be explicitly specified as it is selected - // dynamically at runtime by the OpenIddict client system integration. - new Uri("http://localhost/callback/login/local") - }, - Permissions = - { - Permissions.Endpoints.Authorization, - Permissions.Endpoints.DeviceAuthorization, - Permissions.Endpoints.Introspection, - Permissions.Endpoints.EndSession, - Permissions.Endpoints.PushedAuthorization, - Permissions.Endpoints.Revocation, - Permissions.Endpoints.Token, - Permissions.GrantTypes.AuthorizationCode, - Permissions.GrantTypes.DeviceCode, - Permissions.GrantTypes.Implicit, - Permissions.GrantTypes.Password, - Permissions.GrantTypes.RefreshToken, - Permissions.GrantTypes.TokenExchange, - Permissions.ResponseTypes.Code, - Permissions.ResponseTypes.CodeIdToken, - Permissions.ResponseTypes.CodeIdTokenToken, - Permissions.ResponseTypes.CodeToken, - Permissions.ResponseTypes.IdToken, - Permissions.ResponseTypes.IdTokenToken, - Permissions.ResponseTypes.None, - Permissions.Scopes.Email, - Permissions.Scopes.Profile, - Permissions.Scopes.Roles - }, - Requirements = - { - Requirements.Features.ProofKeyForCodeExchange, - Requirements.Features.PushedAuthorizationRequests - } - }; - - descriptor.AddScopePermissions("demo_api"); - - await manager.CreateAsync(descriptor); - } - - if (await manager.FindByClientIdAsync("maui") is null) - { - var descriptor = new OpenIddictApplicationDescriptor - { - ApplicationType = ApplicationTypes.Native, - ClientId = "maui", - ClientType = ClientTypes.Public, - ConsentType = ConsentTypes.Systematic, - DisplayName = "MAUI client application", - DisplayNames = - { - [CultureInfo.GetCultureInfo("fr-FR")] = "Application cliente MAUI" - }, - PostLogoutRedirectUris = - { - new Uri("com.openiddict.sandbox.maui.client:/callback/logout/local") - }, - RedirectUris = - { - new Uri("com.openiddict.sandbox.maui.client:/callback/login/local") - }, - Permissions = - { - Permissions.Endpoints.Authorization, - Permissions.Endpoints.EndSession, - Permissions.Endpoints.PushedAuthorization, - Permissions.Endpoints.Token, - Permissions.GrantTypes.AuthorizationCode, - Permissions.GrantTypes.RefreshToken, - Permissions.ResponseTypes.Code, - Permissions.Scopes.Email, - Permissions.Scopes.Profile, - Permissions.Scopes.Roles - }, - Requirements = - { - Requirements.Features.ProofKeyForCodeExchange, - Requirements.Features.PushedAuthorizationRequests - } - }; - - descriptor.AddScopePermissions("demo_api"); - - await manager.CreateAsync(descriptor); - } - - if (await manager.FindByClientIdAsync("mvc") is null) - { - var descriptor = new OpenIddictApplicationDescriptor - { - ApplicationType = ApplicationTypes.Web, - ClientId = "mvc", - ClientType = ClientTypes.Confidential, - ConsentType = ConsentTypes.Systematic, - DisplayName = "MVC client application", - DisplayNames = - { - [CultureInfo.GetCultureInfo("fr-FR")] = "Application cliente MVC" - }, -#if SUPPORTS_PEM_ENCODED_KEY_IMPORT - JsonWebKeySet = new JsonWebKeySet - { - Keys = - { - // On supported platforms, this application can authenticate by using a - // self-signed client authentication certificate during the TLS handshake - // (a method known as "mutual TLS" or mTLS). - // - // Note: while the client needs access to the private key, the server only needs - // to know the public part to be able to validate the certificates it receives. - JsonWebKeyConverter.ConvertFromX509SecurityKey(new X509SecurityKey( - X509Certificate2.CreateFromPem($""" - -----BEGIN CERTIFICATE----- - MIIC8zCCAdugAwIBAgIJAIZ9BN3TUnZQMA0GCSqGSIb3DQEBCwUAMCIxIDAeBgNV - BAMTF1NlbGYtc2lnbmVkIGNlcnRpZmljYXRlMCAXDTI2MDIwMjE0MzM0OVoYDzIx - MjYwMjAyMTQzMzQ5WjAiMSAwHgYDVQQDExdTZWxmLXNpZ25lZCBjZXJ0aWZpY2F0 - ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOtfKVPM7ghVFh4U/sz4 - sTrpaNJGQ2NORqawYxAHwluhr101yIOW7rWvFlFncA64Lkq9SAbFFCVSAbo28c6B - 2Mi41jyC4LHQU11jhv08K/3FUuckCuzEpzTnXUhxJHWxrRDVEuvKINGPs1VgVtTT - ra8rjP8s1YRAzCYnByxSx+8GXNGHprylLh0agpWKb2+2FYwDqY5ME2g3xTL9FTUu - FYWTcyspsvN0U1Eo1vlCeOxSYGPRct0MK0AS6eXEGBv+3kCYI7a5+UhQok0WvErF - pjIVo7USISDgKhW9GhTsWN+WywwdG4Kx4V6SB8ZLAHFSBSR3gjWS3TGOyqAWoBXc - znkCAwEAAaMqMCgwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsGAQUF - BwMCMA0GCSqGSIb3DQEBCwUAA4IBAQBf5i/S7shmNalVxMuP8/Mk8cOhRRZjnAXd - zz3eOuXu0CH8iY/DwCgss04O2NTxuz87rKiuNKOrtY0oN/G4aFjWPvbgoQ+N1XP1 - zvbhqbyo3fQr07FyjWkrIUoHYFQ3JRfL+GPGjWizJsgdpdCRJSK6G9VX8eU3Akjv - YhMRLmbkrH5etOURqFtLpZlxNmLzCpqWIvzRiYyyj74iOipA2I0acgcvkakWn6rE - Wio7luBAZ3dXlukEfHTOg+ft4k0nOlRXPTtASOmyFQBOs6iYJeztHDz6MQnknAPe - +W53US8kLWktspcOQmxhVVH1g1/T4ynl9iX7tzqvUbdYwZNi92+x - -----END CERTIFICATE----- - """))), - - // On supported platforms, this application can also authenticate by - // generating JWT client assertions that are signed using a signing key. - // - // Note: while the client needs access to the private key, the server only needs - // to know the public key to be able to validate the client assertions it receives. - JsonWebKeyConverter.ConvertFromECDsaSecurityKey(GetECDsaSigningKey($""" - -----BEGIN PUBLIC KEY----- - MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEI23kaVsRRAWIez/pqEZOByJFmlXd - a6iSQ4QqcH23Ir8aYPPX5lsVnBsExNsl7SOYOiIhgTaX6+PTS7yxTnmvSw== - -----END PUBLIC KEY----- - """)) - } - }, -#else - ClientSecret = "emCimpdc9SeOaZzN5jzm4_eek-STF6VenfVlKO1_qt0", -#endif - RedirectUris = - { - new Uri("https://localhost:44381/callback/login/local") - }, - PostLogoutRedirectUris = - { - new Uri("https://localhost:44381/callback/logout/local") - }, - Permissions = - { - Permissions.Endpoints.Authorization, - Permissions.Endpoints.EndSession, - Permissions.Endpoints.PushedAuthorization, - Permissions.Endpoints.Token, - Permissions.GrantTypes.AuthorizationCode, - Permissions.GrantTypes.RefreshToken, - Permissions.ResponseTypes.Code, - Permissions.Scopes.Email, - Permissions.Scopes.Profile, - Permissions.Scopes.Roles - }, - Requirements = - { - Requirements.Features.ProofKeyForCodeExchange, - Requirements.Features.PushedAuthorizationRequests - } - }; - - descriptor.AddScopePermissions("demo_api"); - - await manager.CreateAsync(descriptor); - } - - if (await manager.FindByClientIdAsync("winforms") is null) - { - var descriptor = new OpenIddictApplicationDescriptor - { - ApplicationType = ApplicationTypes.Native, - ClientId = "winforms", - ClientType = ClientTypes.Public, - ConsentType = ConsentTypes.Systematic, - DisplayName = "WinForms client application", - DisplayNames = - { - [CultureInfo.GetCultureInfo("fr-FR")] = "Application cliente WinForms" - }, - PostLogoutRedirectUris = - { - new Uri("com.openiddict.sandbox.winforms.client:/callback/logout/local") - }, - RedirectUris = - { - new Uri("com.openiddict.sandbox.winforms.client:/callback/login/local") - }, - Permissions = - { - Permissions.Endpoints.Authorization, - Permissions.Endpoints.EndSession, - Permissions.Endpoints.PushedAuthorization, - Permissions.Endpoints.Token, - Permissions.GrantTypes.AuthorizationCode, - Permissions.GrantTypes.RefreshToken, - Permissions.ResponseTypes.Code, - Permissions.Scopes.Email, - Permissions.Scopes.Profile, - Permissions.Scopes.Roles - }, - Requirements = - { - Requirements.Features.ProofKeyForCodeExchange, - Requirements.Features.PushedAuthorizationRequests - } - }; - - descriptor.AddScopePermissions("demo_api"); - - await manager.CreateAsync(descriptor); - } - - if (await manager.FindByClientIdAsync("wpf") is null) - { - var descriptor = new OpenIddictApplicationDescriptor - { - ApplicationType = ApplicationTypes.Native, - ClientId = "wpf", - ClientType = ClientTypes.Public, - ConsentType = ConsentTypes.Systematic, - DisplayName = "WPF client application", - DisplayNames = - { - [CultureInfo.GetCultureInfo("fr-FR")] = "Application cliente WPF" - }, - PostLogoutRedirectUris = - { - new Uri("com.openiddict.sandbox.wpf.client:/callback/logout/local") - }, - RedirectUris = - { - new Uri("com.openiddict.sandbox.wpf.client:/callback/login/local") - }, - Permissions = - { - Permissions.Endpoints.Authorization, - Permissions.Endpoints.EndSession, - Permissions.Endpoints.PushedAuthorization, - Permissions.Endpoints.Token, - Permissions.GrantTypes.AuthorizationCode, - Permissions.GrantTypes.RefreshToken, - Permissions.ResponseTypes.Code, - Permissions.Scopes.Email, - Permissions.Scopes.Profile, - Permissions.Scopes.Roles - }, - Requirements = - { - Requirements.Features.ProofKeyForCodeExchange, - Requirements.Features.PushedAuthorizationRequests - } - }; - - descriptor.AddScopePermissions("demo_api"); - - await manager.CreateAsync(descriptor); - } - - // Note: when using introspection instead of local token validation, - // an application entry MUST be created to allow the resource server - // to communicate with OpenIddict's introspection endpoint. - if (await manager.FindByClientIdAsync("resource_server") is null) - { - var descriptor = new OpenIddictApplicationDescriptor - { - ClientId = "resource_server", - ClientSecret = "vVQ-yjr42sXP5VHj6AswkXuS7MU1i2gFjvJjY0TdGMk", - ClientType = ClientTypes.Confidential, - Permissions = - { - Permissions.Endpoints.Introspection - } - }; - - await manager.CreateAsync(descriptor); - } - - // To test this sample with Postman, use the following settings: - // - // * Authorization URL: https://localhost:44395/connect/authorize - // * Access token URL: https://localhost:44395/connect/token - // * Client ID: postman - // * Client secret: [blank] (not used with public clients) - // * Scope: openid email profile roles - // * Grant type: authorization code - // * Request access token locally: yes - if (await manager.FindByClientIdAsync("postman") is null) - { - var descriptor = new OpenIddictApplicationDescriptor - { - ApplicationType = ApplicationTypes.Native, - ClientId = "postman", - ClientType = ClientTypes.Public, - ConsentType = ConsentTypes.Systematic, - DisplayName = "Postman", - RedirectUris = - { - new Uri("https://oauth.pstmn.io/v1/callback") - }, - Permissions = - { - Permissions.Endpoints.Authorization, - Permissions.Endpoints.DeviceAuthorization, - Permissions.Endpoints.Token, - Permissions.GrantTypes.AuthorizationCode, - Permissions.GrantTypes.DeviceCode, - Permissions.GrantTypes.Password, - Permissions.GrantTypes.RefreshToken, - Permissions.ResponseTypes.Code, - Permissions.Scopes.Email, - Permissions.Scopes.Profile, - Permissions.Scopes.Roles - } - }; - - // Use a shorter access token lifetime for tokens issued to the Postman application. - descriptor.SetAccessTokenLifetime(TimeSpan.FromMinutes(10)); - - await manager.CreateAsync(descriptor); - } - -#if SUPPORTS_PEM_ENCODED_KEY_IMPORT - static ECDsaSecurityKey GetECDsaSigningKey(ReadOnlySpan key) - { - var algorithm = ECDsa.Create(); - algorithm.ImportFromPem(key); - - return new ECDsaSecurityKey(algorithm); - } -#endif - } - - static async Task RegisterScopesAsync(IServiceProvider provider) - { - var manager = provider.GetRequiredService(); - - if (await manager.FindByNameAsync("demo_api") is null) - { - var descriptor = new OpenIddictScopeDescriptor - { - DisplayName = "Demo API access", - DisplayNames = - { - [CultureInfo.GetCultureInfo("fr-FR")] = "Accès à l'API de démo" - }, - Name = "demo_api", - Resources = - { - "resource_server" - } - }; - - await manager.CreateAsync(descriptor); - } - } - } - - public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; -} diff --git a/sandbox/OpenIddict.Sandbox.Console.Client/Program.cs b/sandbox/OpenIddict.Sandbox.Console.Client/Program.cs index 56b0a82c..8801d9f0 100644 --- a/sandbox/OpenIddict.Sandbox.Console.Client/Program.cs +++ b/sandbox/OpenIddict.Sandbox.Console.Client/Program.cs @@ -7,7 +7,7 @@ using OpenIddict.Client.WebIntegration; using OpenIddict.Sandbox.Console.Client; using static OpenIddict.Abstractions.OpenIddictConstants; -var builder = Host.CreateApplicationBuilder(); +var builder = Host.CreateApplicationBuilder(args); builder.Logging.ClearProviders(); builder.Logging.AddDebug(); @@ -129,14 +129,18 @@ builder.Services.AddOpenIddict() }); }); -// Register the worker responsible for creating the database used to store tokens -// and adding the registry entries required to register the custom URI scheme. -// -// Note: in a real world application, this step should be part of a setup script. -builder.Services.AddHostedService(); - // Register the background service responsible for handling the console interactions. builder.Services.AddHostedService(); var app = builder.Build(); + +// Before starting the host, create the database used to store the application data. +// +// Note: in a real world application, this step should be part of a setup script. +await using (var scope = app.Services.CreateAsyncScope()) +{ + var context = scope.ServiceProvider.GetRequiredService(); + await context.Database.EnsureCreatedAsync(); +} + await app.RunAsync(); diff --git a/sandbox/OpenIddict.Sandbox.Console.Client/Worker.cs b/sandbox/OpenIddict.Sandbox.Console.Client/Worker.cs deleted file mode 100644 index 00df826d..00000000 --- a/sandbox/OpenIddict.Sandbox.Console.Client/Worker.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace OpenIddict.Sandbox.Console.Client; - -public class Worker : IHostedService -{ - private readonly IServiceProvider _provider; - - public Worker(IServiceProvider provider) - => _provider = provider; - - public async Task StartAsync(CancellationToken cancellationToken) - { - await using var scope = _provider.CreateAsyncScope(); - - var context = scope.ServiceProvider.GetRequiredService(); - await context.Database.EnsureCreatedAsync(cancellationToken); - } - - public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; -} diff --git a/sandbox/OpenIddict.Sandbox.Maui.Client/Worker.cs b/sandbox/OpenIddict.Sandbox.Maui.Client/MauiDatabaseInitializer.cs similarity index 81% rename from sandbox/OpenIddict.Sandbox.Maui.Client/Worker.cs rename to sandbox/OpenIddict.Sandbox.Maui.Client/MauiDatabaseInitializer.cs index 2146f842..f831e228 100644 --- a/sandbox/OpenIddict.Sandbox.Maui.Client/Worker.cs +++ b/sandbox/OpenIddict.Sandbox.Maui.Client/MauiDatabaseInitializer.cs @@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore; namespace OpenIddict.Sandbox.Maui.Client; -public class Worker : IMauiInitializeScopedService +public class MauiDatabaseInitializer : IMauiInitializeScopedService { public void Initialize(IServiceProvider services) { diff --git a/sandbox/OpenIddict.Sandbox.Maui.Client/MauiProgram.cs b/sandbox/OpenIddict.Sandbox.Maui.Client/MauiProgram.cs index 2cbaf100..b7b870e5 100644 --- a/sandbox/OpenIddict.Sandbox.Maui.Client/MauiProgram.cs +++ b/sandbox/OpenIddict.Sandbox.Maui.Client/MauiProgram.cs @@ -130,7 +130,7 @@ public static class MauiProgram builder.Services.AddSingleton(); // Register the initialization service responsible for creating the SQLite database. - builder.Services.AddScoped(); + builder.Services.AddScoped(); return builder.Build(); } diff --git a/sandbox/OpenIddict.Sandbox.WinForms.Client/Program.cs b/sandbox/OpenIddict.Sandbox.WinForms.Client/Program.cs index 6107b61d..9b3b1025 100644 --- a/sandbox/OpenIddict.Sandbox.WinForms.Client/Program.cs +++ b/sandbox/OpenIddict.Sandbox.WinForms.Client/Program.cs @@ -1,8 +1,10 @@ +using System.Diagnostics; using Dapplo.Microsoft.Extensions.Hosting.WinForms; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using Microsoft.Win32; using OpenIddict.Client; using OpenIddict.Sandbox.WinForms.Client; using static OpenIddict.Abstractions.OpenIddictConstants; @@ -97,14 +99,43 @@ var host = new HostBuilder() }); }); - // Register the worker responsible for creating the database used to store tokens - // and adding the registry entries required to register the custom URI scheme. // // Note: in a real world application, this step should be part of a setup script. - services.AddHostedService(); + // services.Insert(0, ServiceDescriptor.Singleton()); }) .ConfigureWinForms() .UseWinFormsLifetime() .Build(); +// Before starting the host, create the database used to store the application data +// and add the registry entries required to register the custom URI scheme. +// +// Note: in a real world application, this step should be part of a setup script. +await using (var scope = host.Services.CreateAsyncScope()) +{ + var context = scope.ServiceProvider.GetRequiredService(); + await context.Database.EnsureCreatedAsync(); + + // Create the registry entries necessary to handle URI protocol activations. + // + // Note: this sample creates the entry under the current user account (as it doesn't + // require administrator rights), but the registration can also be added globally + // in HKEY_CLASSES_ROOT (in this case, it should be added by a dedicated installer). + // + // Alternatively, the application can be packaged and use windows.protocol to + // register the protocol handler/custom URI scheme with the operating system. + using var root = Registry.CurrentUser.CreateSubKey("SOFTWARE\\Classes\\com.openiddict.sandbox.winforms.client"); + root.SetValue(string.Empty, "URL:com.openiddict.sandbox.winforms.client"); + root.SetValue("URL Protocol", string.Empty); + + using var command = root.CreateSubKey("shell\\open\\command"); + command.SetValue(string.Empty, string.Format("\"{0}\" \"%1\"", +#if SUPPORTS_ENVIRONMENT_PROCESS_PATH + Environment.ProcessPath +#else + Process.GetCurrentProcess().MainModule.FileName +#endif + )); +} + await host.RunAsync(); \ No newline at end of file diff --git a/sandbox/OpenIddict.Sandbox.WinForms.Client/Worker.cs b/sandbox/OpenIddict.Sandbox.WinForms.Client/Worker.cs deleted file mode 100644 index 27027265..00000000 --- a/sandbox/OpenIddict.Sandbox.WinForms.Client/Worker.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Diagnostics; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Win32; - -namespace OpenIddict.Sandbox.WinForms.Client; - -public class Worker : IHostedService -{ - private readonly IServiceProvider _provider; - - public Worker(IServiceProvider provider) - => _provider = provider; - - public async Task StartAsync(CancellationToken cancellationToken) - { - await using var scope = _provider.CreateAsyncScope(); - - var context = scope.ServiceProvider.GetRequiredService(); - await context.Database.EnsureCreatedAsync(cancellationToken); - - // Create the registry entries necessary to handle URI protocol activations. - // - // Note: this sample creates the entry under the current user account (as it doesn't - // require administrator rights), but the registration can also be added globally - // in HKEY_CLASSES_ROOT (in this case, it should be added by a dedicated installer). - // - // Alternatively, the application can be packaged and use windows.protocol to - // register the protocol handler/custom URI scheme with the operating system. - using var root = Registry.CurrentUser.CreateSubKey("SOFTWARE\\Classes\\com.openiddict.sandbox.winforms.client"); - root.SetValue(string.Empty, "URL:com.openiddict.sandbox.winforms.client"); - root.SetValue("URL Protocol", string.Empty); - - using var command = root.CreateSubKey("shell\\open\\command"); - command.SetValue(string.Empty, string.Format("\"{0}\" \"%1\"", -#if SUPPORTS_ENVIRONMENT_PROCESS_PATH - Environment.ProcessPath -#else - Process.GetCurrentProcess().MainModule.FileName -#endif - )); - } - - public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; -} diff --git a/sandbox/OpenIddict.Sandbox.Wpf.Client/Program.cs b/sandbox/OpenIddict.Sandbox.Wpf.Client/Program.cs index 03d91103..f89a7c30 100644 --- a/sandbox/OpenIddict.Sandbox.Wpf.Client/Program.cs +++ b/sandbox/OpenIddict.Sandbox.Wpf.Client/Program.cs @@ -1,9 +1,11 @@ +using System.Diagnostics; using System.IO; using Dapplo.Microsoft.Extensions.Hosting.Wpf; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using Microsoft.Win32; using OpenIddict.Client; using OpenIddict.Sandbox.Wpf.Client; using static OpenIddict.Abstractions.OpenIddictConstants; @@ -93,12 +95,6 @@ var host = new HostBuilder() .SetRedirectUri("com.openiddict.sandbox.wpf.client://callback/login/github"); }); }); - - // Register the worker responsible for creating the database used to store tokens - // and adding the registry entries required to register the custom URI scheme. - // - // Note: in a real world application, this step should be part of a setup script. - services.AddHostedService(); }) .ConfigureWpf(options => { @@ -108,4 +104,35 @@ var host = new HostBuilder() .UseWpfLifetime() .Build(); +// Before starting the host, create the database used to store the application data +// and add the registry entries required to register the custom URI scheme. +// +// Note: in a real world application, this step should be part of a setup script. +await using (var scope = host.Services.CreateAsyncScope()) +{ + var context = scope.ServiceProvider.GetRequiredService(); + await context.Database.EnsureCreatedAsync(); + + // Create the registry entries necessary to handle URI protocol activations. + // + // Note: this sample creates the entry under the current user account (as it doesn't + // require administrator rights), but the registration can also be added globally + // in HKEY_CLASSES_ROOT (in this case, it should be added by a dedicated installer). + // + // Alternatively, the application can be packaged and use windows.protocol to + // register the protocol handler/custom URI scheme with the operating system. + using var root = Registry.CurrentUser.CreateSubKey("SOFTWARE\\Classes\\com.openiddict.sandbox.wpf.client"); + root.SetValue(string.Empty, "URL:com.openiddict.sandbox.wpf.client"); + root.SetValue("URL Protocol", string.Empty); + + using var command = root.CreateSubKey("shell\\open\\command"); + command.SetValue(string.Empty, string.Format("\"{0}\" \"%1\"", +#if SUPPORTS_ENVIRONMENT_PROCESS_PATH + Environment.ProcessPath +#else + Process.GetCurrentProcess().MainModule.FileName +#endif + )); +} + await host.RunAsync(); \ No newline at end of file diff --git a/sandbox/OpenIddict.Sandbox.Wpf.Client/Worker.cs b/sandbox/OpenIddict.Sandbox.Wpf.Client/Worker.cs deleted file mode 100644 index b4d21290..00000000 --- a/sandbox/OpenIddict.Sandbox.Wpf.Client/Worker.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Diagnostics; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Win32; - -namespace OpenIddict.Sandbox.Wpf.Client; - -public class Worker : IHostedService -{ - private readonly IServiceProvider _provider; - - public Worker(IServiceProvider provider) - => _provider = provider; - - public async Task StartAsync(CancellationToken cancellationToken) - { - await using var scope = _provider.CreateAsyncScope(); - - var context = scope.ServiceProvider.GetRequiredService(); - await context.Database.EnsureCreatedAsync(cancellationToken); - - // Create the registry entries necessary to handle URI protocol activations. - // - // Note: this sample creates the entry under the current user account (as it doesn't - // require administrator rights), but the registration can also be added globally - // in HKEY_CLASSES_ROOT (in this case, it should be added by a dedicated installer). - // - // Alternatively, the application can be packaged and use windows.protocol to - // register the protocol handler/custom URI scheme with the operating system. - using var root = Registry.CurrentUser.CreateSubKey("SOFTWARE\\Classes\\com.openiddict.sandbox.wpf.client"); - root.SetValue(string.Empty, "URL:com.openiddict.sandbox.wpf.client"); - root.SetValue("URL Protocol", string.Empty); - - using var command = root.CreateSubKey("shell\\open\\command"); - command.SetValue(string.Empty, string.Format("\"{0}\" \"%1\"", -#if SUPPORTS_ENVIRONMENT_PROCESS_PATH - Environment.ProcessPath -#else - Process.GetCurrentProcess().MainModule.FileName -#endif - )); - } - - public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; -}