Browse Source

Initialize the database before starting the web host instead of using an IHostedService implementation

pull/2436/head
Kévin Chalet 3 weeks ago
parent
commit
8da8eb39a2
  1. 34
      sandbox/OpenIddict.Sandbox.AspNetCore.Client/Program.cs
  2. 4
      sandbox/OpenIddict.Sandbox.AspNetCore.Client/Startup.cs
  3. 21
      sandbox/OpenIddict.Sandbox.AspNetCore.Client/Worker.cs
  4. 439
      sandbox/OpenIddict.Sandbox.AspNetCore.Server/Program.cs
  5. 4
      sandbox/OpenIddict.Sandbox.AspNetCore.Server/Startup.cs
  6. 430
      sandbox/OpenIddict.Sandbox.AspNetCore.Server/Worker.cs
  7. 18
      sandbox/OpenIddict.Sandbox.Console.Client/Program.cs
  8. 23
      sandbox/OpenIddict.Sandbox.Console.Client/Worker.cs
  9. 2
      sandbox/OpenIddict.Sandbox.Maui.Client/MauiDatabaseInitializer.cs
  10. 2
      sandbox/OpenIddict.Sandbox.Maui.Client/MauiProgram.cs
  11. 37
      sandbox/OpenIddict.Sandbox.WinForms.Client/Program.cs
  12. 46
      sandbox/OpenIddict.Sandbox.WinForms.Client/Worker.cs
  13. 39
      sandbox/OpenIddict.Sandbox.Wpf.Client/Program.cs
  14. 46
      sandbox/OpenIddict.Sandbox.Wpf.Client/Worker.cs

34
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<Startup>());
var builder = Host.CreateDefaultBuilder(args);
builder.ConfigureWebHostDefaults(builder => builder.UseStartup<Startup>());
#else
public static void Main(string[] args) =>
CreateWebHostBuilder(args).Build().Run();
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
var builder = WebHost.CreateDefaultBuilder(args);
builder.UseStartup<Startup>();
#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<ApplicationDbContext>();
await context.Database.EnsureCreatedAsync();
}
await app.RunAsync();

4
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<Worker>();
}
public void Configure(IApplicationBuilder app)

21
sandbox/OpenIddict.Sandbox.AspNetCore.Client/Worker.cs

@ -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<ApplicationDbContext>();
await context.Database.EnsureCreatedAsync(cancellationToken);
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}

439
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<Startup>());
#else
var builder = WebHost.CreateDefaultBuilder(args);
builder.UseStartup<Startup>();
#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<ApplicationDbContext>();
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<IOpenIddictApplicationManager>();
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(builder => builder.UseStartup<Startup>());
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<Startup>();
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<char> key)
{
var algorithm = ECDsa.Create();
algorithm.ImportFromPem(key);
return new ECDsaSecurityKey(algorithm);
}
#endif
}
static async Task RegisterScopesAsync(IServiceProvider provider)
{
var manager = provider.GetRequiredService<IOpenIddictScopeManager>();
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);
}
}

4
sandbox/OpenIddict.Sandbox.AspNetCore.Server/Startup.cs

@ -315,10 +315,6 @@ public class Startup
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
// 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<Worker>();
#if SUPPORTS_KESTREL_TLS_HANDSHAKE_CALLBACK_OPTIONS
// Configure Kestrel to listen on the 44395 port and configure it to enforce mTLS.
//

430
sandbox/OpenIddict.Sandbox.AspNetCore.Server/Worker.cs

@ -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<ApplicationDbContext>();
await context.Database.EnsureCreatedAsync(cancellationToken);
await RegisterApplicationsAsync(scope.ServiceProvider);
await RegisterScopesAsync(scope.ServiceProvider);
static async Task RegisterApplicationsAsync(IServiceProvider provider)
{
var manager = provider.GetRequiredService<IOpenIddictApplicationManager>();
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<char> key)
{
var algorithm = ECDsa.Create();
algorithm.ImportFromPem(key);
return new ECDsaSecurityKey(algorithm);
}
#endif
}
static async Task RegisterScopesAsync(IServiceProvider provider)
{
var manager = provider.GetRequiredService<IOpenIddictScopeManager>();
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;
}

18
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<Worker>();
// Register the background service responsible for handling the console interactions.
builder.Services.AddHostedService<InteractiveService>();
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<DbContext>();
await context.Database.EnsureCreatedAsync();
}
await app.RunAsync();

23
sandbox/OpenIddict.Sandbox.Console.Client/Worker.cs

@ -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<DbContext>();
await context.Database.EnsureCreatedAsync(cancellationToken);
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}

2
sandbox/OpenIddict.Sandbox.Maui.Client/Worker.cs → 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)
{

2
sandbox/OpenIddict.Sandbox.Maui.Client/MauiProgram.cs

@ -130,7 +130,7 @@ public static class MauiProgram
builder.Services.AddSingleton<MainPage>();
// Register the initialization service responsible for creating the SQLite database.
builder.Services.AddScoped<IMauiInitializeScopedService, Worker>();
builder.Services.AddScoped<IMauiInitializeScopedService, MauiDatabaseInitializer>();
return builder.Build();
}

37
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<Worker>();
// services.Insert(0, ServiceDescriptor.Singleton<IHostedService, Worker>());
})
.ConfigureWinForms<MainForm>()
.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<DbContext>();
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();

46
sandbox/OpenIddict.Sandbox.WinForms.Client/Worker.cs

@ -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<DbContext>();
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;
}

39
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<Worker>();
})
.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<DbContext>();
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();

46
sandbox/OpenIddict.Sandbox.Wpf.Client/Worker.cs

@ -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<DbContext>();
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;
}
Loading…
Cancel
Save