diff --git a/samples/Aridka/Aridka.Server/Program.cs b/samples/Aridka/Aridka.Server/Program.cs index 2b8bc7c1..59d2905d 100644 --- a/samples/Aridka/Aridka.Server/Program.cs +++ b/samples/Aridka/Aridka.Server/Program.cs @@ -1,11 +1,92 @@ -namespace Aridka.Server; +using Aridka.Server; +using Aridka.Server.Models; +using Microsoft.EntityFrameworkCore; +using Quartz; -public static class Program +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddControllersWithViews(); + +builder.Services.AddDbContext(options => +{ + // Configure the context to use sqlite. + options.UseSqlite($"Filename={Path.Combine(Path.GetTempPath(), "openiddict-aridka-server.sqlite3")}"); + + // Register the entity sets needed by OpenIddict. + // Note: use the generic overload if you need + // to replace the default OpenIddict entities. + options.UseOpenIddict(); +}); + +// OpenIddict offers native integration with Quartz.NET to perform scheduled tasks +// (like pruning orphaned authorizations/tokens from the database) at regular intervals. +builder.Services.AddQuartz(options => { - public static void Main(string[] args) => - CreateHostBuilder(args).Build().Run(); + options.UseSimpleTypeLoader(); + options.UseInMemoryStore(); +}); + +// Register the Quartz.NET service and configure it to block shutdown until jobs are complete. +builder.Services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true); + +builder.Services.AddOpenIddict() + + // Register the OpenIddict core components. + .AddCore(options => + { + // Configure OpenIddict to use the Entity Framework Core stores and models. + // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities. + options.UseEntityFrameworkCore() + .UseDbContext(); + + // Enable Quartz.NET integration. + options.UseQuartz(); + }) + + // Register the OpenIddict server components. + .AddServer(options => + { + // Enable the token endpoint. + options.SetTokenEndpointUris("connect/token"); + + // Enable the client credentials flow. + options.AllowClientCredentialsFlow(); + + // Register the signing and encryption credentials. + options.AddDevelopmentEncryptionCertificate() + .AddDevelopmentSigningCertificate(); + + // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. + options.UseAspNetCore() + .EnableTokenEndpointPassthrough(); + }) + + // Register the OpenIddict validation components. + .AddValidation(options => + { + // Import the configuration from the local OpenIddict server instance. + options.UseLocalServer(); + + // Register the ASP.NET Core host. + options.UseAspNetCore(); + }); + +// Register the worker responsible for seeding the database. +// Note: in a real world application, this step should be part of a setup script. +builder.Services.AddHostedService(); + +var app = builder.Build(); + +app.UseDeveloperExceptionPage(); + +app.UseRouting(); + +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapControllers(); +app.MapDefaultControllerRoute(); + +app.UseWelcomePage("/"); - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(builder => builder.UseStartup()); -} +app.Run(); \ No newline at end of file diff --git a/samples/Aridka/Aridka.Server/Startup.cs b/samples/Aridka/Aridka.Server/Startup.cs deleted file mode 100644 index ed599da3..00000000 --- a/samples/Aridka/Aridka.Server/Startup.cs +++ /dev/null @@ -1,104 +0,0 @@ -using Aridka.Server.Models; -using Microsoft.EntityFrameworkCore; -using Quartz; - -namespace Aridka.Server; - -public class Startup -{ - public Startup(IConfiguration configuration) - => Configuration = configuration; - - public IConfiguration Configuration { get; } - - public void ConfigureServices(IServiceCollection services) - { - services.AddControllersWithViews(); - - services.AddDbContext(options => - { - // Configure the context to use sqlite. - options.UseSqlite($"Filename={Path.Combine(Path.GetTempPath(), "openiddict-aridka-server.sqlite3")}"); - - // Register the entity sets needed by OpenIddict. - // Note: use the generic overload if you need - // to replace the default OpenIddict entities. - options.UseOpenIddict(); - }); - - // OpenIddict offers native integration with Quartz.NET to perform scheduled tasks - // (like pruning orphaned authorizations/tokens from the database) at regular intervals. - services.AddQuartz(options => - { - options.UseSimpleTypeLoader(); - options.UseInMemoryStore(); - }); - - // Register the Quartz.NET service and configure it to block shutdown until jobs are complete. - services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true); - - services.AddOpenIddict() - - // Register the OpenIddict core components. - .AddCore(options => - { - // Configure OpenIddict to use the Entity Framework Core stores and models. - // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities. - options.UseEntityFrameworkCore() - .UseDbContext(); - - // Enable Quartz.NET integration. - options.UseQuartz(); - }) - - // Register the OpenIddict server components. - .AddServer(options => - { - // Enable the token endpoint. - options.SetTokenEndpointUris("connect/token"); - - // Enable the client credentials flow. - options.AllowClientCredentialsFlow(); - - // Register the signing and encryption credentials. - options.AddDevelopmentEncryptionCertificate() - .AddDevelopmentSigningCertificate(); - - // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. - options.UseAspNetCore() - .EnableTokenEndpointPassthrough(); - }) - - // Register the OpenIddict validation components. - .AddValidation(options => - { - // Import the configuration from the local OpenIddict server instance. - options.UseLocalServer(); - - // Register the ASP.NET Core host. - options.UseAspNetCore(); - }); - - // Register the worker responsible for seeding the database. - // Note: in a real world application, this step should be part of a setup script. - services.AddHostedService(); - } - - public void Configure(IApplicationBuilder app) - { - app.UseDeveloperExceptionPage(); - - app.UseRouting(); - - app.UseAuthentication(); - app.UseAuthorization(); - - app.UseEndpoints(endpoints => - { - endpoints.MapControllers(); - endpoints.MapDefaultControllerRoute(); - }); - - app.UseWelcomePage(); - } -} diff --git a/samples/Balosar/Balosar.Server/Program.cs b/samples/Balosar/Balosar.Server/Program.cs index 2a01c7ee..24507ca3 100644 --- a/samples/Balosar/Balosar.Server/Program.cs +++ b/samples/Balosar/Balosar.Server/Program.cs @@ -1,11 +1,162 @@ -namespace Balosar.Server; +using Balosar.Server; +using Balosar.Server.Data; +using Balosar.Server.Models; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Quartz; +using static OpenIddict.Abstractions.OpenIddictConstants; -public static class Program +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddDbContext(options => +{ + // Configure the context to use sqlite. + options.UseSqlite($"Filename={Path.Combine(Path.GetTempPath(), "openiddict-balosar-server.sqlite3")}"); + + // Register the entity sets needed by OpenIddict. + // Note: use the generic overload if you need + // to replace the default OpenIddict entities. + options.UseOpenIddict(); +}); + +// Register the Identity services. +builder.Services.AddIdentity() + .AddEntityFrameworkStores() + .AddDefaultTokenProviders() + .AddDefaultUI(); + +// OpenIddict offers native integration with Quartz.NET to perform scheduled tasks +// (like pruning orphaned authorizations/tokens from the database) at regular intervals. +builder.Services.AddQuartz(options => { - public static void Main(string[] args) => - CreateHostBuilder(args).Build().Run(); + options.UseSimpleTypeLoader(); + options.UseInMemoryStore(); +}); + +// Register the Quartz.NET service and configure it to block shutdown until jobs are complete. +builder.Services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true); + +builder.Services.AddOpenIddict() + + // Register the OpenIddict core components. + .AddCore(options => + { + // Configure OpenIddict to use the Entity Framework Core stores and models. + // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities. + options.UseEntityFrameworkCore() + .UseDbContext(); + + // Enable Quartz.NET integration. + options.UseQuartz(); + }) + + // Register the OpenIddict client components. + .AddClient(options => + { + // Note: this sample uses the code flow, but you can enable the other flows if necessary. + options.AllowAuthorizationCodeFlow(); + + // Register the signing and encryption credentials used to protect + // sensitive data like the state tokens produced by OpenIddict. + options.AddDevelopmentEncryptionCertificate() + .AddDevelopmentSigningCertificate(); + + // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. + options.UseAspNetCore() + .EnableStatusCodePagesIntegration() + .EnableRedirectionEndpointPassthrough(); + + // Register the System.Net.Http integration and use the identity of the current + // assembly as a more specific user agent, which can be useful when dealing with + // providers that use the user agent as a way to throttle requests (e.g Reddit). + options.UseSystemNetHttp() + .SetProductInformation(typeof(Program).Assembly); + + // Register the Web providers integrations. + // + // Note: to mitigate mix-up attacks, it's recommended to use a unique redirection endpoint + // URI per provider, unless all the registered providers support returning a special "iss" + // parameter containing their URL as part of authorization responses. For more information, + // see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.4. + options.UseWebProviders() + .AddGitHub(options => + { + options.SetClientId("c4ade52327b01ddacff3") + .SetClientSecret("da6bed851b75e317bf6b2cb67013679d9467c122") + .SetRedirectUri("callback/login/github"); + }); + }) + + // Register the OpenIddict server components. + .AddServer(options => + { + // Enable the authorization, logout, token and userinfo endpoints. + options.SetAuthorizationEndpointUris("connect/authorize") + .SetEndSessionEndpointUris("connect/logout") + .SetTokenEndpointUris("connect/token") + .SetUserInfoEndpointUris("connect/userinfo"); + + // Mark the "email", "profile" and "roles" scopes as supported scopes. + options.RegisterScopes(Scopes.Email, Scopes.Profile, Scopes.Roles); - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(builder => builder.UseStartup()); + // Note: the sample uses the code and refresh token flows but you can enable + // the other flows if you need to support implicit, password or client credentials. + options.AllowAuthorizationCodeFlow() + .AllowRefreshTokenFlow(); + + // Register the signing and encryption credentials. + options.AddDevelopmentEncryptionCertificate() + .AddDevelopmentSigningCertificate(); + + // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. + options.UseAspNetCore() + .EnableAuthorizationEndpointPassthrough() + .EnableEndSessionEndpointPassthrough() + .EnableStatusCodePagesIntegration() + .EnableTokenEndpointPassthrough(); + }) + + // Register the OpenIddict validation components. + .AddValidation(options => + { + // Import the configuration from the local OpenIddict server instance. + options.UseLocalServer(); + + // Register the ASP.NET Core host. + options.UseAspNetCore(); + }); + +builder.Services.AddControllersWithViews(); +builder.Services.AddRazorPages(); + +// Register the worker responsible for seeding the database. +// Note: in a real world application, this step should be part of a setup script. +builder.Services.AddHostedService(); + +var app = builder.Build(); + +if (builder.Environment.IsDevelopment()) +{ + app.UseDeveloperExceptionPage(); + app.UseWebAssemblyDebugging(); +} +else +{ + app.UseExceptionHandler("/Error"); + app.UseHsts(); } + +app.UseHttpsRedirection(); +app.UseBlazorFrameworkFiles(); +app.UseStaticFiles(); + +app.UseRouting(); + +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapRazorPages(); +app.MapControllers(); +app.MapFallbackToFile("index.html"); + +app.Run(); \ No newline at end of file diff --git a/samples/Balosar/Balosar.Server/Startup.cs b/samples/Balosar/Balosar.Server/Startup.cs deleted file mode 100644 index dc2de893..00000000 --- a/samples/Balosar/Balosar.Server/Startup.cs +++ /dev/null @@ -1,173 +0,0 @@ -using Balosar.Server.Data; -using Balosar.Server.Models; -using Microsoft.AspNetCore.Identity; -using Microsoft.EntityFrameworkCore; -using Quartz; -using static OpenIddict.Abstractions.OpenIddictConstants; - -namespace Balosar.Server; - -public class Startup -{ - public Startup(IConfiguration configuration) => Configuration = configuration; - - public IConfiguration Configuration { get; } - - public void ConfigureServices(IServiceCollection services) - { - services.AddDbContext(options => - { - // Configure the context to use sqlite. - options.UseSqlite($"Filename={Path.Combine(Path.GetTempPath(), "openiddict-balosar-server.sqlite3")}"); - - // Register the entity sets needed by OpenIddict. - // Note: use the generic overload if you need - // to replace the default OpenIddict entities. - options.UseOpenIddict(); - }); - - // Register the Identity services. - services.AddIdentity() - .AddEntityFrameworkStores() - .AddDefaultTokenProviders() - .AddDefaultUI(); - - // OpenIddict offers native integration with Quartz.NET to perform scheduled tasks - // (like pruning orphaned authorizations/tokens from the database) at regular intervals. - services.AddQuartz(options => - { - options.UseSimpleTypeLoader(); - options.UseInMemoryStore(); - }); - - // Register the Quartz.NET service and configure it to block shutdown until jobs are complete. - services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true); - - services.AddOpenIddict() - - // Register the OpenIddict core components. - .AddCore(options => - { - // Configure OpenIddict to use the Entity Framework Core stores and models. - // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities. - options.UseEntityFrameworkCore() - .UseDbContext(); - - // Enable Quartz.NET integration. - options.UseQuartz(); - }) - - // Register the OpenIddict client components. - .AddClient(options => - { - // Note: this sample uses the code flow, but you can enable the other flows if necessary. - options.AllowAuthorizationCodeFlow(); - - // Register the signing and encryption credentials used to protect - // sensitive data like the state tokens produced by OpenIddict. - options.AddDevelopmentEncryptionCertificate() - .AddDevelopmentSigningCertificate(); - - // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. - options.UseAspNetCore() - .EnableStatusCodePagesIntegration() - .EnableRedirectionEndpointPassthrough(); - - // Register the System.Net.Http integration and use the identity of the current - // assembly as a more specific user agent, which can be useful when dealing with - // providers that use the user agent as a way to throttle requests (e.g Reddit). - options.UseSystemNetHttp() - .SetProductInformation(typeof(Startup).Assembly); - - // Register the Web providers integrations. - // - // Note: to mitigate mix-up attacks, it's recommended to use a unique redirection endpoint - // URI per provider, unless all the registered providers support returning a special "iss" - // parameter containing their URL as part of authorization responses. For more information, - // see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.4. - options.UseWebProviders() - .AddGitHub(options => - { - options.SetClientId("c4ade52327b01ddacff3") - .SetClientSecret("da6bed851b75e317bf6b2cb67013679d9467c122") - .SetRedirectUri("callback/login/github"); - }); - }) - - // Register the OpenIddict server components. - .AddServer(options => - { - // Enable the authorization, logout, token and userinfo endpoints. - options.SetAuthorizationEndpointUris("connect/authorize") - .SetEndSessionEndpointUris("connect/logout") - .SetTokenEndpointUris("connect/token") - .SetUserInfoEndpointUris("connect/userinfo"); - - // Mark the "email", "profile" and "roles" scopes as supported scopes. - options.RegisterScopes(Scopes.Email, Scopes.Profile, Scopes.Roles); - - // Note: the sample uses the code and refresh token flows but you can enable - // the other flows if you need to support implicit, password or client credentials. - options.AllowAuthorizationCodeFlow() - .AllowRefreshTokenFlow(); - - // Register the signing and encryption credentials. - options.AddDevelopmentEncryptionCertificate() - .AddDevelopmentSigningCertificate(); - - // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. - options.UseAspNetCore() - .EnableAuthorizationEndpointPassthrough() - .EnableEndSessionEndpointPassthrough() - .EnableStatusCodePagesIntegration() - .EnableTokenEndpointPassthrough(); - }) - - // Register the OpenIddict validation components. - .AddValidation(options => - { - // Import the configuration from the local OpenIddict server instance. - options.UseLocalServer(); - - // Register the ASP.NET Core host. - options.UseAspNetCore(); - }); - - services.AddControllersWithViews(); - services.AddRazorPages(); - - // Register the worker responsible for seeding the database. - // Note: in a real world application, this step should be part of a setup script. - services.AddHostedService(); - } - - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - app.UseWebAssemblyDebugging(); - } - else - { - app.UseExceptionHandler("/Error"); - app.UseHsts(); - } - - app.UseHttpsRedirection(); - app.UseBlazorFrameworkFiles(); - app.UseStaticFiles(); - - app.UseRouting(); - - app.UseAuthentication(); - app.UseAuthorization(); - - app.UseEndpoints(endpoints => - { - endpoints.MapRazorPages(); - endpoints.MapControllers(); - endpoints.MapFallbackToFile("index.html"); - }); - } -} diff --git a/samples/Balosar/Balosar.Server/Worker.cs b/samples/Balosar/Balosar.Server/Worker.cs index 84f1c5bb..370a157c 100644 --- a/samples/Balosar/Balosar.Server/Worker.cs +++ b/samples/Balosar/Balosar.Server/Worker.cs @@ -54,7 +54,6 @@ await manager.CreateAsync(new OpenIddictApplicationDescriptor } }); } - } public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; diff --git a/samples/Contruum/Contruum.Server/Program.cs b/samples/Contruum/Contruum.Server/Program.cs index f1cb0792..2a5d0c2f 100644 --- a/samples/Contruum/Contruum.Server/Program.cs +++ b/samples/Contruum/Contruum.Server/Program.cs @@ -1,11 +1,170 @@ -namespace Contruum.Server; +using System.Globalization; +using System.Text.Json.Nodes; +using Contruum.Server; +using Contruum.Server.Models; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using OpenIddict.Abstractions; +using Quartz; +using static OpenIddict.Abstractions.OpenIddictConstants; +using static OpenIddict.Server.OpenIddictServerEvents; -public static class Program +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddRazorPages(); + +builder.Services.AddDbContext(options => +{ + // Configure the context to use an in-memory store. + options.UseSqlite($"Filename={Path.Combine(Path.GetTempPath(), "openiddict-contruum-server.sqlite3")}"); + + // Register the entity sets needed by OpenIddict. + options.UseOpenIddict(); +}); + +builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) + .AddCookie(options => + { + options.AccessDeniedPath = "/connect/signin"; + options.LoginPath = "/connect/signin"; + options.LogoutPath = "/connect/signout"; + }); + +// OpenIddict offers native integration with Quartz.NET to perform scheduled tasks +// (like pruning orphaned authorizations/tokens from the database) at regular intervals. +builder.Services.AddQuartz(options => { - public static void Main(string[] args) => - CreateHostBuilder(args).Build().Run(); + options.UseSimpleTypeLoader(); + options.UseInMemoryStore(); +}); + +// Register the Quartz.NET service and configure it to block shutdown until jobs are complete. +builder.Services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true); + +// Register the OpenIddict services. +builder.Services.AddOpenIddict() + .AddCore(options => + { + // Register the Entity Framework Core models/stores. + options.UseEntityFrameworkCore() + .UseDbContext(); + + // Enable Quartz.NET integration. + options.UseQuartz(); + }) + + .AddServer(options => + { + // Enable the authorization, token, introspection and userinfo endpoints. + options.SetAuthorizationEndpointUris(builder.Configuration["OpenIddict:Endpoints:Authorization"]!) + .SetTokenEndpointUris(builder.Configuration["OpenIddict:Endpoints:Token"]!) + .SetIntrospectionEndpointUris(builder.Configuration["OpenIddict:Endpoints:Introspection"]!) + .SetUserInfoEndpointUris(builder.Configuration["OpenIddict:Endpoints:Userinfo"]!) + .SetEndSessionEndpointUris(builder.Configuration["OpenIddict:Endpoints:Logout"]!); + + // Enable the authorization code, implicit, hybrid and the refresh token flows. + options.AllowAuthorizationCodeFlow() + .AllowImplicitFlow() + .AllowHybridFlow() + .AllowRefreshTokenFlow(); + + // Expose all the supported claims in the discovery document. + options.RegisterClaims(builder.Configuration.GetSection("OpenIddict:Claims").Get()!); + + // Expose all the supported scopes in the discovery document. + options.RegisterScopes(builder.Configuration.GetSection("OpenIddict:Scopes").Get()!); + + // Note: an ephemeral signing key is deliberately used to make the "OP-Rotation-OP-Sig" + // test easier to run as restarting the application is enough to rotate the keys. + options.AddEphemeralEncryptionKey() + .AddEphemeralSigningKey(); + + // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. + // + // Note: the pass-through mode is not enabled for the token endpoint + // so that token requests are automatically handled by OpenIddict. + options.UseAspNetCore() + .EnableAuthorizationEndpointPassthrough() + .EnableEndSessionEndpointPassthrough(); + + // Register the custom event handler responsible for populating userinfo responses. + options.AddEventHandler(options => options.UseInlineHandler(static context => + { + if (context.AccessTokenPrincipal.HasScope(Scopes.Profile)) + { + context.GivenName = context.AccessTokenPrincipal.GetClaim(Claims.GivenName); + context.FamilyName = context.AccessTokenPrincipal.GetClaim(Claims.FamilyName); + context.BirthDate = context.AccessTokenPrincipal.GetClaim(Claims.Birthdate); + context.Profile = context.AccessTokenPrincipal.GetClaim(Claims.Profile); + context.PreferredUsername = context.AccessTokenPrincipal.GetClaim(Claims.PreferredUsername); + context.Website = context.AccessTokenPrincipal.GetClaim(Claims.Website); - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(builder => builder.UseStartup()); + context.Claims[Claims.Name] = context.AccessTokenPrincipal.GetClaim(Claims.Name); + context.Claims[Claims.Gender] = context.AccessTokenPrincipal.GetClaim(Claims.Gender); + context.Claims[Claims.MiddleName] = context.AccessTokenPrincipal.GetClaim(Claims.MiddleName); + context.Claims[Claims.Nickname] = context.AccessTokenPrincipal.GetClaim(Claims.Nickname); + context.Claims[Claims.Picture] = context.AccessTokenPrincipal.GetClaim(Claims.Picture); + context.Claims[Claims.Locale] = context.AccessTokenPrincipal.GetClaim(Claims.Locale); + context.Claims[Claims.Zoneinfo] = context.AccessTokenPrincipal.GetClaim(Claims.Zoneinfo); + context.Claims[Claims.UpdatedAt] = long.Parse( + context.AccessTokenPrincipal.GetClaim(Claims.UpdatedAt)!, + NumberStyles.Number, CultureInfo.InvariantCulture); + } + + if (context.AccessTokenPrincipal.HasScope(Scopes.Email)) + { + context.Email = context.AccessTokenPrincipal.GetClaim(Claims.Email); + context.EmailVerified = false; + } + + if (context.AccessTokenPrincipal.HasScope(Scopes.Phone)) + { + context.PhoneNumber = context.AccessTokenPrincipal.GetClaim(Claims.PhoneNumber); + context.PhoneNumberVerified = false; + } + + if (context.AccessTokenPrincipal.HasScope(Scopes.Address)) + { + context.Address = JsonNode.Parse(context.AccessTokenPrincipal.GetClaim(Claims.Address)!)!.AsObject(); + } + + return default; + })); + }) + + .AddValidation(options => + { + // Import the configuration from the local OpenIddict server instance. + options.UseLocalServer(); + + // Register the ASP.NET Core host. + options.UseAspNetCore(); + + // Enable authorization entry validation, which is required to be able + // to reject access tokens retrieved from a revoked authorization code. + options.EnableAuthorizationEntryValidation(); + }); + +// Register the worker responsible for creating and seeding the SQL database. +// Note: in a real world application, this step should be part of a setup script. +builder.Services.AddHostedService(); + +var app = builder.Build(); + +if (builder.Environment.IsDevelopment()) +{ + app.UseDeveloperExceptionPage(); } + +app.UseHttpsRedirection(); +app.UseStaticFiles(); + +app.UseRouting(); + +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapRazorPages(); + +app.Run(); \ No newline at end of file diff --git a/samples/Contruum/Contruum.Server/Startup.cs b/samples/Contruum/Contruum.Server/Startup.cs deleted file mode 100644 index aa9ae08a..00000000 --- a/samples/Contruum/Contruum.Server/Startup.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System.Globalization; -using System.Text.Json; -using System.Text.Json.Nodes; -using Contruum.Server.Models; -using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.EntityFrameworkCore; -using OpenIddict.Abstractions; -using Quartz; -using static OpenIddict.Abstractions.OpenIddictConstants; -using static OpenIddict.Server.OpenIddictServerEvents; - -namespace Contruum.Server; - -public class Startup -{ - public IConfiguration Configuration { get; } - - public Startup(IConfiguration configuration) => Configuration = configuration; - - public void ConfigureServices(IServiceCollection services) - { - services.AddRazorPages(); - - services.AddDbContext(options => - { - // Configure the context to use an in-memory store. - options.UseSqlite($"Filename={Path.Combine(Path.GetTempPath(), "openiddict-contruum-server.sqlite3")}"); - - // Register the entity sets needed by OpenIddict. - options.UseOpenIddict(); - }); - - services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) - .AddCookie(options => - { - options.AccessDeniedPath = "/connect/signin"; - options.LoginPath = "/connect/signin"; - options.LogoutPath = "/connect/signout"; - }); - - // OpenIddict offers native integration with Quartz.NET to perform scheduled tasks - // (like pruning orphaned authorizations/tokens from the database) at regular intervals. - services.AddQuartz(options => - { - options.UseSimpleTypeLoader(); - options.UseInMemoryStore(); - }); - - // Register the Quartz.NET service and configure it to block shutdown until jobs are complete. - services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true); - - // Register the OpenIddict services. - services.AddOpenIddict() - .AddCore(options => - { - // Register the Entity Framework Core models/stores. - options.UseEntityFrameworkCore() - .UseDbContext(); - - // Enable Quartz.NET integration. - options.UseQuartz(); - }) - - .AddServer(options => - { - // Enable the authorization, token, introspection and userinfo endpoints. - options.SetAuthorizationEndpointUris(Configuration["OpenIddict:Endpoints:Authorization"]!) - .SetTokenEndpointUris(Configuration["OpenIddict:Endpoints:Token"]!) - .SetIntrospectionEndpointUris(Configuration["OpenIddict:Endpoints:Introspection"]!) - .SetUserInfoEndpointUris(Configuration["OpenIddict:Endpoints:Userinfo"]!) - .SetEndSessionEndpointUris(Configuration["OpenIddict:Endpoints:Logout"]!); - - // Enable the authorization code, implicit, hybrid and the refresh token flows. - options.AllowAuthorizationCodeFlow() - .AllowImplicitFlow() - .AllowHybridFlow() - .AllowRefreshTokenFlow(); - - // Expose all the supported claims in the discovery document. - options.RegisterClaims(Configuration.GetSection("OpenIddict:Claims").Get()!); - - // Expose all the supported scopes in the discovery document. - options.RegisterScopes(Configuration.GetSection("OpenIddict:Scopes").Get()!); - - // Note: an ephemeral signing key is deliberately used to make the "OP-Rotation-OP-Sig" - // test easier to run as restarting the application is enough to rotate the keys. - options.AddEphemeralEncryptionKey() - .AddEphemeralSigningKey(); - - // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. - // - // Note: the pass-through mode is not enabled for the token endpoint - // so that token requests are automatically handled by OpenIddict. - options.UseAspNetCore() - .EnableAuthorizationEndpointPassthrough() - .EnableEndSessionEndpointPassthrough(); - - // Register the custom event handler responsible for populating userinfo responses. - options.AddEventHandler(options => options.UseInlineHandler(static context => - { - if (context.AccessTokenPrincipal.HasScope(Scopes.Profile)) - { - context.GivenName = context.AccessTokenPrincipal.GetClaim(Claims.GivenName); - context.FamilyName = context.AccessTokenPrincipal.GetClaim(Claims.FamilyName); - context.BirthDate = context.AccessTokenPrincipal.GetClaim(Claims.Birthdate); - context.Profile = context.AccessTokenPrincipal.GetClaim(Claims.Profile); - context.PreferredUsername = context.AccessTokenPrincipal.GetClaim(Claims.PreferredUsername); - context.Website = context.AccessTokenPrincipal.GetClaim(Claims.Website); - - context.Claims[Claims.Name] = context.AccessTokenPrincipal.GetClaim(Claims.Name); - context.Claims[Claims.Gender] = context.AccessTokenPrincipal.GetClaim(Claims.Gender); - context.Claims[Claims.MiddleName] = context.AccessTokenPrincipal.GetClaim(Claims.MiddleName); - context.Claims[Claims.Nickname] = context.AccessTokenPrincipal.GetClaim(Claims.Nickname); - context.Claims[Claims.Picture] = context.AccessTokenPrincipal.GetClaim(Claims.Picture); - context.Claims[Claims.Locale] = context.AccessTokenPrincipal.GetClaim(Claims.Locale); - context.Claims[Claims.Zoneinfo] = context.AccessTokenPrincipal.GetClaim(Claims.Zoneinfo); - context.Claims[Claims.UpdatedAt] = long.Parse( - context.AccessTokenPrincipal.GetClaim(Claims.UpdatedAt)!, - NumberStyles.Number, CultureInfo.InvariantCulture); - } - - if (context.AccessTokenPrincipal.HasScope(Scopes.Email)) - { - context.Email = context.AccessTokenPrincipal.GetClaim(Claims.Email); - context.EmailVerified = false; - } - - if (context.AccessTokenPrincipal.HasScope(Scopes.Phone)) - { - context.PhoneNumber = context.AccessTokenPrincipal.GetClaim(Claims.PhoneNumber); - context.PhoneNumberVerified = false; - } - - if (context.AccessTokenPrincipal.HasScope(Scopes.Address)) - { - context.Address = JsonNode.Parse(context.AccessTokenPrincipal.GetClaim(Claims.Address)!)!.AsObject(); - } - - return default; - })); - }) - - .AddValidation(options => - { - // Import the configuration from the local OpenIddict server instance. - options.UseLocalServer(); - - // Register the ASP.NET Core host. - options.UseAspNetCore(); - - // Enable authorization entry validation, which is required to be able - // to reject access tokens retrieved from a revoked authorization code. - options.EnableAuthorizationEntryValidation(); - }); - - // Register the worker responsible for creating and seeding the SQL database. - // Note: in a real world application, this step should be part of a setup script. - services.AddHostedService(); - } - - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - app.UseHttpsRedirection(); - app.UseStaticFiles(); - - app.UseRouting(); - - app.UseAuthentication(); - app.UseAuthorization(); - - app.UseEndpoints(endpoints => endpoints.MapRazorPages()); - } -} diff --git a/samples/Dantooine/Dantooine.Server/Program.cs b/samples/Dantooine/Dantooine.Server/Program.cs index 60dd0a84..138c70a2 100644 --- a/samples/Dantooine/Dantooine.Server/Program.cs +++ b/samples/Dantooine/Dantooine.Server/Program.cs @@ -1,11 +1,131 @@ -namespace Dantooine.Server; +using Dantooine.Server; +using Dantooine.Server.Data; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Quartz; +using static OpenIddict.Abstractions.OpenIddictConstants; -public static class Program +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddControllersWithViews(); +builder.Services.AddRazorPages(); + +builder.Services.AddDbContext(options => +{ + // Configure the context to use sqlite. + options.UseSqlite($"Filename={Path.Combine(Path.GetTempPath(), "openiddict-dantooine-server.sqlite3")}"); + + // Register the entity sets needed by OpenIddict. + // Note: use the generic overload if you need + // to replace the default OpenIddict entities. + options.UseOpenIddict(); +}); + +builder.Services.AddDatabaseDeveloperPageExceptionFilter(); + +// Register the Identity services. +builder.Services.AddIdentity() + .AddEntityFrameworkStores() + .AddDefaultTokenProviders() + .AddDefaultUI(); + +// OpenIddict offers native integration with Quartz.NET to perform scheduled tasks +// (like pruning orphaned authorizations/tokens from the database) at regular intervals. +builder.Services.AddQuartz(options => +{ + options.UseSimpleTypeLoader(); + options.UseInMemoryStore(); +}); + +// Register the Quartz.NET service and configure it to block shutdown until jobs are complete. +builder.Services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true); + +builder.Services.AddOpenIddict() + + // Register the OpenIddict core components. + .AddCore(options => + { + // Configure OpenIddict to use the Entity Framework Core stores and models. + // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities. + options.UseEntityFrameworkCore() + .UseDbContext(); + + // Enable Quartz.NET integration. + options.UseQuartz(); + }) + + // Register the OpenIddict server components. + .AddServer(options => + { + // Enable the authorization, logout, token and userinfo endpoints. + options.SetAuthorizationEndpointUris("connect/authorize") + .SetEndSessionEndpointUris("connect/logout") + .SetIntrospectionEndpointUris("connect/introspect") + .SetTokenEndpointUris("connect/token") + .SetUserInfoEndpointUris("connect/userinfo") + .SetEndUserVerificationEndpointUris("connect/verify"); + + // Mark the "email", "profile" and "roles" scopes as supported scopes. + options.RegisterScopes(Scopes.Email, Scopes.Profile, Scopes.Roles); + + // Note: this sample only uses the authorization code and refresh token + // flows but you can enable the other flows if you need to support + // implicit, password or client credentials. + options.AllowAuthorizationCodeFlow() + .AllowRefreshTokenFlow(); + + // Register the signing and encryption credentials. + options.AddDevelopmentEncryptionCertificate() + .AddDevelopmentSigningCertificate(); + + // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. + options.UseAspNetCore() + .EnableAuthorizationEndpointPassthrough() + .EnableEndSessionEndpointPassthrough() + .EnableTokenEndpointPassthrough() + .EnableUserInfoEndpointPassthrough() + .EnableStatusCodePagesIntegration(); + }) + + // Register the OpenIddict validation components. + .AddValidation(options => + { + // Import the configuration from the local OpenIddict server instance. + options.UseLocalServer(); + + // Register the ASP.NET Core host. + options.UseAspNetCore(); + }); + +// Register the worker responsible for seeding the database. +// Note: in a real world application, this step should be part of a setup script. +builder.Services.AddHostedService(); + +var app = builder.Build(); + +if (builder.Environment.IsDevelopment()) +{ + app.UseDeveloperExceptionPage(); + app.UseMigrationsEndPoint(); +} +else { - public static void Main(string[] args) => - CreateHostBuilder(args).Build().Run(); + app.UseStatusCodePagesWithReExecute("~/error"); + //app.UseExceptionHandler("~/error"); - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(options => options.UseStartup()); + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + //app.UseHsts(); } +app.UseHttpsRedirection(); +app.UseStaticFiles(); + +app.UseRouting(); + +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapControllers(); +app.MapDefaultControllerRoute(); +app.MapRazorPages(); + +app.Run(); \ No newline at end of file diff --git a/samples/Dantooine/Dantooine.Server/Startup.cs b/samples/Dantooine/Dantooine.Server/Startup.cs deleted file mode 100644 index cad6a794..00000000 --- a/samples/Dantooine/Dantooine.Server/Startup.cs +++ /dev/null @@ -1,143 +0,0 @@ -using Microsoft.AspNetCore.Identity; -using Microsoft.EntityFrameworkCore; -using Quartz; -using Dantooine.Server.Data; -using static OpenIddict.Abstractions.OpenIddictConstants; - -namespace Dantooine.Server; - -public class Startup -{ - public Startup(IConfiguration configuration) - => Configuration = configuration; - - public IConfiguration Configuration { get; } - - public void ConfigureServices(IServiceCollection services) - { - services.AddControllersWithViews(); - services.AddRazorPages(); - - services.AddDbContext(options => - { - // Configure the context to use sqlite. - options.UseSqlite($"Filename={Path.Combine(Path.GetTempPath(), "openiddict-dantooine-server.sqlite3")}"); - - // Register the entity sets needed by OpenIddict. - // Note: use the generic overload if you need - // to replace the default OpenIddict entities. - options.UseOpenIddict(); - }); - - services.AddDatabaseDeveloperPageExceptionFilter(); - - // Register the Identity services. - services.AddIdentity() - .AddEntityFrameworkStores() - .AddDefaultTokenProviders() - .AddDefaultUI(); - - // OpenIddict offers native integration with Quartz.NET to perform scheduled tasks - // (like pruning orphaned authorizations/tokens from the database) at regular intervals. - services.AddQuartz(options => - { - options.UseSimpleTypeLoader(); - options.UseInMemoryStore(); - }); - - // Register the Quartz.NET service and configure it to block shutdown until jobs are complete. - services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true); - - services.AddOpenIddict() - - // Register the OpenIddict core components. - .AddCore(options => - { - // Configure OpenIddict to use the Entity Framework Core stores and models. - // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities. - options.UseEntityFrameworkCore() - .UseDbContext(); - - // Enable Quartz.NET integration. - options.UseQuartz(); - }) - - // Register the OpenIddict server components. - .AddServer(options => - { - // Enable the authorization, logout, token and userinfo endpoints. - options.SetAuthorizationEndpointUris("connect/authorize") - .SetEndSessionEndpointUris("connect/logout") - .SetIntrospectionEndpointUris("connect/introspect") - .SetTokenEndpointUris("connect/token") - .SetUserInfoEndpointUris("connect/userinfo") - .SetEndUserVerificationEndpointUris("connect/verify"); - - // Mark the "email", "profile" and "roles" scopes as supported scopes. - options.RegisterScopes(Scopes.Email, Scopes.Profile, Scopes.Roles); - - // Note: this sample only uses the authorization code and refresh token - // flows but you can enable the other flows if you need to support - // implicit, password or client credentials. - options.AllowAuthorizationCodeFlow() - .AllowRefreshTokenFlow(); - - // Register the signing and encryption credentials. - options.AddDevelopmentEncryptionCertificate() - .AddDevelopmentSigningCertificate(); - - // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. - options.UseAspNetCore() - .EnableAuthorizationEndpointPassthrough() - .EnableEndSessionEndpointPassthrough() - .EnableTokenEndpointPassthrough() - .EnableUserInfoEndpointPassthrough() - .EnableStatusCodePagesIntegration(); - }) - - // Register the OpenIddict validation components. - .AddValidation(options => - { - // Import the configuration from the local OpenIddict server instance. - options.UseLocalServer(); - - // Register the ASP.NET Core host. - options.UseAspNetCore(); - }); - - // Register the worker responsible for seeding the database. - // Note: in a real world application, this step should be part of a setup script. - services.AddHostedService(); - } - - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - app.UseMigrationsEndPoint(); - } - else - { - app.UseStatusCodePagesWithReExecute("~/error"); - //app.UseExceptionHandler("~/error"); - - // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. - //app.UseHsts(); - } - app.UseHttpsRedirection(); - app.UseStaticFiles(); - - app.UseRouting(); - - app.UseAuthentication(); - app.UseAuthorization(); - - app.UseEndpoints(endpoints => - { - endpoints.MapControllers(); - endpoints.MapDefaultControllerRoute(); - endpoints.MapRazorPages(); - }); - } -} diff --git a/samples/Dantooine/Dantooine.WebAssembly.Client/Program.cs b/samples/Dantooine/Dantooine.WebAssembly.Client/Program.cs index d0869b11..edc88dfd 100644 --- a/samples/Dantooine/Dantooine.WebAssembly.Client/Program.cs +++ b/samples/Dantooine/Dantooine.WebAssembly.Client/Program.cs @@ -1,39 +1,34 @@ using System.Net.Http.Headers; +using Dantooine.WebAssembly.Client; using Dantooine.WebAssembly.Client.Services; using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.Extensions.DependencyInjection.Extensions; -namespace Dantooine.WebAssembly.Client; +var builder = WebAssemblyHostBuilder.CreateDefault(args); -public static class Program +builder.Services.AddOptions(); +builder.Services.AddAuthorizationCore(); +builder.Services.TryAddSingleton(); +builder.Services.TryAddSingleton(provider => (HostAuthenticationStateProvider) provider.GetRequiredService()); +builder.Services.AddTransient(); + +builder.RootComponents.Add("#app"); + +builder.Services.AddHttpClient("default", client => +{ + client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress); + client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); +}); + +builder.Services.AddHttpClient("authorizedClient", client => { - public static async Task Main(string[] args) - { - var builder = WebAssemblyHostBuilder.CreateDefault(args); - builder.Services.AddOptions(); - builder.Services.AddAuthorizationCore(); - builder.Services.TryAddSingleton(); - builder.Services.TryAddSingleton(provider => (HostAuthenticationStateProvider) provider.GetRequiredService()); - builder.Services.AddTransient(); - - builder.RootComponents.Add("#app"); - - builder.Services.AddHttpClient("default", client => - { - client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress); - client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - }); - - builder.Services.AddHttpClient("authorizedClient", client => - { - client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress); - client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - }).AddHttpMessageHandler(); - - builder.Services.AddTransient(provider => provider.GetRequiredService().CreateClient("default")); - - var host = builder.Build(); - await host.RunAsync(); - } -} + client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress); + client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); +}).AddHttpMessageHandler(); + +builder.Services.AddTransient(provider => provider.GetRequiredService().CreateClient("default")); + +var host = builder.Build(); + +await host.RunAsync(); \ No newline at end of file diff --git a/samples/Dantooine/Dantooine.WebAssembly.Server/Program.cs b/samples/Dantooine/Dantooine.WebAssembly.Server/Program.cs index 9fca742a..d84be64d 100644 --- a/samples/Dantooine/Dantooine.WebAssembly.Server/Program.cs +++ b/samples/Dantooine/Dantooine.WebAssembly.Server/Program.cs @@ -1,11 +1,281 @@ -namespace Dantooine.WebAssembly.Server; +using System.Configuration; +using System.Globalization; +using Dantooine.WebAssembly.Server; +using Dantooine.WebAssembly.Server.Helpers; +using Dantooine.WebAssembly.Server.Models; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection.Extensions; +using OpenIddict.Client; +using Quartz; +using Yarp.ReverseProxy.Forwarder; +using Yarp.ReverseProxy.Transforms; +using static OpenIddict.Abstractions.OpenIddictConstants; +using static OpenIddict.Abstractions.OpenIddictExceptions; +using static OpenIddict.Client.AspNetCore.OpenIddictClientAspNetCoreConstants; -public static class Program +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddDbContext(options => +{ + // Configure the context to use sqlite. + options.UseSqlite($"Filename={Path.Combine(Path.GetTempPath(), "openiddict-dantooine-webassembly-server.sqlite3")}"); + + // Register the entity sets needed by OpenIddict. + // Note: use the generic overload if you need + // to replace the default OpenIddict entities. + options.UseOpenIddict(); +}); + +// Configure the antiforgery stack to allow extracting +// antiforgery tokens from the X-XSRF-TOKEN header. +builder.Services.AddAntiforgery(options => +{ + options.HeaderName = "X-XSRF-TOKEN"; + options.Cookie.Name = "__Host-X-XSRF-TOKEN"; + options.Cookie.SameSite = SameSiteMode.Strict; + options.Cookie.SecurePolicy = CookieSecurePolicy.Always; +}); + +builder.Services.AddAuthentication(options => +{ + options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; +}) + +.AddCookie(options => +{ + options.LoginPath = "/login"; + options.LogoutPath = "/logout"; + options.ExpireTimeSpan = TimeSpan.FromDays(7); +}); + +// OpenIddict offers native integration with Quartz.NET to perform scheduled tasks +// (like pruning orphaned authorizations from the database) at regular intervals. +builder.Services.AddQuartz(options => { - public static void Main(string[] args) => - CreateHostBuilder(args).Build().Run(); + options.UseSimpleTypeLoader(); + options.UseInMemoryStore(); +}); + +// Register the Quartz.NET service and configure it to block shutdown until jobs are complete. +builder.Services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true); + +builder.Services.AddOpenIddict() + + // Register the OpenIddict core components. + .AddCore(options => + { + // Configure OpenIddict to use the Entity Framework Core stores and models. + // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities. + options.UseEntityFrameworkCore() + .UseDbContext(); + + // Developers who prefer using MongoDB can remove the previous lines + // and configure OpenIddict to use the specified MongoDB database: + // options.UseMongoDb() + // .UseDatabase(new MongoClient().GetDatabase("openiddict")); + + // Enable Quartz.NET integration. + options.UseQuartz(); + }) + + // Register the OpenIddict client components. + .AddClient(options => + { + // Note: this sample uses the authorization code and refresh token + // flows, but you can enable the other flows if necessary. + options.AllowAuthorizationCodeFlow() + .AllowRefreshTokenFlow(); + + // Register the signing and encryption credentials used to protect + // sensitive data like the state tokens produced by OpenIddict. + options.AddDevelopmentEncryptionCertificate() + .AddDevelopmentSigningCertificate(); + + // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. + options.UseAspNetCore() + .EnableStatusCodePagesIntegration() + .EnableRedirectionEndpointPassthrough() + .EnablePostLogoutRedirectionEndpointPassthrough(); + + // Register the System.Net.Http integration and use the identity of the current + // assembly as a more specific user agent, which can be useful when dealing with + // providers that use the user agent as a way to throttle requests (e.g Reddit). + options.UseSystemNetHttp() + .SetProductInformation(typeof(Program).Assembly); + + // Add a client registration matching the client application definition in the server project. + options.AddRegistration(new OpenIddictClientRegistration + { + Issuer = new Uri("https://localhost:44319/", UriKind.Absolute), + + ClientId = "blazorcodeflowpkceclient", + ClientSecret = "codeflow_pkce_client_secret", + Scopes = { Scopes.OfflineAccess, Scopes.Profile, "api1" }, + + // Note: to mitigate mix-up attacks, it's recommended to use a unique redirection endpoint + // URI per provider, unless all the registered providers support returning a special "iss" + // parameter containing their URL as part of authorization responses. For more information, + // see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.4. + RedirectUri = new Uri("callback/login/local", UriKind.Relative), + PostLogoutRedirectUri = new Uri("callback/logout/local", UriKind.Relative) + }); + }); + +builder.Services.AddControllersWithViews(); +builder.Services.AddRazorPages(); + +// Create an authorization policy used by YARP when forwarding requests +// from the WASM application to the Dantooine.Api resource server. +builder.Services.AddAuthorizationBuilder() + .AddPolicy("CookieAuthenticationPolicy", builder => + { + builder.AddAuthenticationSchemes(CookieAuthenticationDefaults.AuthenticationScheme); + builder.RequireAuthenticatedUser(); + }); + +builder.Services.AddReverseProxy() + .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy")) + .AddTransforms(builder => + { + builder.AddRequestTransform(async context => + { + // Attach the access token, access token expiration date and refresh token resolved from the authentication + // cookie to the request options so they can later be resolved from the delegating handler and attached + // to the request message or used to refresh the tokens if the server returned a 401 error response. + // + // Alternatively, the user tokens could be stored in a database or a distributed cache. + + var result = await context.HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme); + if (result is not { Succeeded: true }) + { + return; + } + + context.ProxyRequest.Options.Set( + key: new(Tokens.BackchannelAccessToken), + value: result.Properties.GetTokenValue(Tokens.BackchannelAccessToken)); - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(builder => builder.UseStartup()); + context.ProxyRequest.Options.Set( + key: new(Tokens.BackchannelAccessTokenExpirationDate), + value: result.Properties.GetTokenValue(Tokens.BackchannelAccessTokenExpirationDate)); + + context.ProxyRequest.Options.Set( + key: new(Tokens.RefreshToken), + value: result.Properties.GetTokenValue(Tokens.RefreshToken)); + }); + + builder.AddResponseTransform(async context => + { + // If tokens were refreshed during the request handling (e.g due to the stored access token being + // expired or a 401 error response being returned by the resource server), extract and attach them + // to the authentication cookie that will be returned to the browser: doing that is essential as + // OpenIddict uses rolling refresh tokens: if the refresh token wasn't replaced, future refresh + // token requests would end up being rejected as they would be treated as replayed requests. + + if (context.ProxyResponse is not TokenRefreshingHttpResponseMessage response) + { + return; + } + + var result = await context.HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme); + if (result is not { Succeeded: true }) + { + return; + } + + // Override the tokens using the values returned in the token response. + var properties = result.Properties.Clone(); + properties.UpdateTokenValue(Tokens.BackchannelAccessToken, response.RefreshTokenAuthenticationResult.AccessToken); + + properties.UpdateTokenValue(Tokens.BackchannelAccessTokenExpirationDate, + response.RefreshTokenAuthenticationResult.AccessTokenExpirationDate?.ToString(CultureInfo.InvariantCulture) ?? string.Empty); + + // Note: if no refresh token was returned, preserve the refresh token initially returned. + if (!string.IsNullOrEmpty(response.RefreshTokenAuthenticationResult.RefreshToken)) + { + properties.UpdateTokenValue(Tokens.RefreshToken, response.RefreshTokenAuthenticationResult.RefreshToken); + } + + // Remove the redirect URI from the authentication properties + // to prevent the cookies handler from genering a 302 response. + properties.RedirectUri = null; + + // Replace the creation/expiration dates of the authentication ticket to extend the lifetime of the cookie. + // + // Note: doing that is not mandatory: if the expiration date is not replaced here, the resulting cookie + // will have the same expiration date as the authentication cookie present in the HTTP request headers. + // + // In any case, if the sliding expiration mechanism is enabled, the cookie (but not the data it contains) + // will be automatically renewed by the cookie handler upon reaching half of the cookie's lifespan. + properties.IssuedUtc = TimeProvider.System.GetUtcNow(); + properties.ExpiresUtc = properties.IssuedUtc + TimeSpan.FromDays(7); + + // Note: this event handler can be called concurrently for the same user if multiple HTTP + // responses are returned in parallel: in this case, the browser will always store the latest + // cookie received and the refresh tokens stored in the other cookies will be discarded. + await context.HttpContext.SignInAsync(result.Ticket.AuthenticationScheme, result.Principal, properties); + }); + }); + +// Replace the default HTTP client factory used by YARP by an instance able to inject the HTTP delegating +// handler that will be used to attach the access tokens to HTTP requests or refresh tokens if necessary. +builder.Services.Replace(ServiceDescriptor.Singleton()); + +// 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. +builder.Services.AddHostedService(); + +var app = builder.Build(); + +if (builder.Environment.IsDevelopment()) +{ + app.UseDeveloperExceptionPage(); + app.UseWebAssemblyDebugging(); +} +else +{ + app.UseExceptionHandler("/Error"); } + +app.UseHttpsRedirection(); +app.UseBlazorFrameworkFiles(); +app.UseStaticFiles(); + +app.UseRouting(); +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapRazorPages(); +app.MapControllers(); + +// Note: by default, the cookie authentication middleware automatically redirects +// the user agent to the login page configured in the cookie authentication options. +// In this case, this behavior is not desirable as an HTTP 401 response MUST be +// returned to the WASM client to automatically redirect the user agent to the +// login page. As such, this logic is disabled for all proxied requests. +app.MapReverseProxy(ConfigureProxyPipeline).DisableCookieRedirect(); + +app.MapFallbackToPage("/_Host"); + +app.Run(); + +static void ConfigureProxyPipeline(IReverseProxyApplicationBuilder app) +{ + app.Use(async (context, next) => + { + await next(); + + // Note: if an "access_denied" error occurred while trying to refresh tokens, + // trigger a cookie authentication challenge to let the WASM client know that + // the user should be redirected to the login page to re-authenticate. + var exception = context.GetForwarderErrorFeature()?.Exception; + if (exception is ProtocolException { Error: Errors.InvalidGrant }) + { + context.Response.Clear(); + + await context.ChallengeAsync(CookieAuthenticationDefaults.AuthenticationScheme); + } + }); +} \ No newline at end of file diff --git a/samples/Dantooine/Dantooine.WebAssembly.Server/Startup.cs b/samples/Dantooine/Dantooine.WebAssembly.Server/Startup.cs deleted file mode 100644 index ae28c342..00000000 --- a/samples/Dantooine/Dantooine.WebAssembly.Server/Startup.cs +++ /dev/null @@ -1,292 +0,0 @@ -using System.Globalization; -using Dantooine.WebAssembly.Server.Helpers; -using Dantooine.WebAssembly.Server.Models; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection.Extensions; -using OpenIddict.Client; -using Quartz; -using Yarp.ReverseProxy.Forwarder; -using Yarp.ReverseProxy.Transforms; -using static OpenIddict.Abstractions.OpenIddictConstants; -using static OpenIddict.Abstractions.OpenIddictExceptions; -using static OpenIddict.Client.AspNetCore.OpenIddictClientAspNetCoreConstants; - -namespace Dantooine.WebAssembly.Server; - -public class Startup -{ - public Startup(IConfiguration configuration) - => Configuration = configuration; - - public IConfiguration Configuration { get; } - - public void ConfigureServices(IServiceCollection services) - { - services.AddDbContext(options => - { - // Configure the context to use sqlite. - options.UseSqlite($"Filename={Path.Combine(Path.GetTempPath(), "openiddict-dantooine-webassembly-server.sqlite3")}"); - - // Register the entity sets needed by OpenIddict. - // Note: use the generic overload if you need - // to replace the default OpenIddict entities. - options.UseOpenIddict(); - }); - - // Configure the antiforgery stack to allow extracting - // antiforgery tokens from the X-XSRF-TOKEN header. - services.AddAntiforgery(options => - { - options.HeaderName = "X-XSRF-TOKEN"; - options.Cookie.Name = "__Host-X-XSRF-TOKEN"; - options.Cookie.SameSite = SameSiteMode.Strict; - options.Cookie.SecurePolicy = CookieSecurePolicy.Always; - }); - - services.AddAuthentication(options => - { - options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; - }) - - .AddCookie(options => - { - options.LoginPath = "/login"; - options.LogoutPath = "/logout"; - options.ExpireTimeSpan = TimeSpan.FromDays(7); - }); - - // OpenIddict offers native integration with Quartz.NET to perform scheduled tasks - // (like pruning orphaned authorizations from the database) at regular intervals. - services.AddQuartz(options => - { - options.UseSimpleTypeLoader(); - options.UseInMemoryStore(); - }); - - // Register the Quartz.NET service and configure it to block shutdown until jobs are complete. - services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true); - - services.AddOpenIddict() - - // Register the OpenIddict core components. - .AddCore(options => - { - // Configure OpenIddict to use the Entity Framework Core stores and models. - // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities. - options.UseEntityFrameworkCore() - .UseDbContext(); - - // Developers who prefer using MongoDB can remove the previous lines - // and configure OpenIddict to use the specified MongoDB database: - // options.UseMongoDb() - // .UseDatabase(new MongoClient().GetDatabase("openiddict")); - - // Enable Quartz.NET integration. - options.UseQuartz(); - }) - - // Register the OpenIddict client components. - .AddClient(options => - { - // Note: this sample uses the authorization code and refresh token - // flows, but you can enable the other flows if necessary. - options.AllowAuthorizationCodeFlow() - .AllowRefreshTokenFlow(); - - // Register the signing and encryption credentials used to protect - // sensitive data like the state tokens produced by OpenIddict. - options.AddDevelopmentEncryptionCertificate() - .AddDevelopmentSigningCertificate(); - - // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. - options.UseAspNetCore() - .EnableStatusCodePagesIntegration() - .EnableRedirectionEndpointPassthrough() - .EnablePostLogoutRedirectionEndpointPassthrough(); - - // Register the System.Net.Http integration and use the identity of the current - // assembly as a more specific user agent, which can be useful when dealing with - // providers that use the user agent as a way to throttle requests (e.g Reddit). - options.UseSystemNetHttp() - .SetProductInformation(typeof(Startup).Assembly); - - // Add a client registration matching the client application definition in the server project. - options.AddRegistration(new OpenIddictClientRegistration - { - Issuer = new Uri("https://localhost:44319/", UriKind.Absolute), - - ClientId = "blazorcodeflowpkceclient", - ClientSecret = "codeflow_pkce_client_secret", - Scopes = { Scopes.OfflineAccess, Scopes.Profile, "api1" }, - - // Note: to mitigate mix-up attacks, it's recommended to use a unique redirection endpoint - // URI per provider, unless all the registered providers support returning a special "iss" - // parameter containing their URL as part of authorization responses. For more information, - // see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.4. - RedirectUri = new Uri("callback/login/local", UriKind.Relative), - PostLogoutRedirectUri = new Uri("callback/logout/local", UriKind.Relative) - }); - }); - - services.AddControllersWithViews(); - services.AddRazorPages(); - - // Create an authorization policy used by YARP when forwarding requests - // from the WASM application to the Dantooine.Api resource server. - services.AddAuthorizationBuilder() - .AddPolicy("CookieAuthenticationPolicy", builder => - { - builder.AddAuthenticationSchemes(CookieAuthenticationDefaults.AuthenticationScheme); - builder.RequireAuthenticatedUser(); - }); - - services.AddReverseProxy() - .LoadFromConfig(Configuration.GetSection("ReverseProxy")) - .AddTransforms(builder => - { - builder.AddRequestTransform(async context => - { - // Attach the access token, access token expiration date and refresh token resolved from the authentication - // cookie to the request options so they can later be resolved from the delegating handler and attached - // to the request message or used to refresh the tokens if the server returned a 401 error response. - // - // Alternatively, the user tokens could be stored in a database or a distributed cache. - - var result = await context.HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme); - if (result is not { Succeeded: true }) - { - return; - } - - context.ProxyRequest.Options.Set( - key : new(Tokens.BackchannelAccessToken), - value: result.Properties.GetTokenValue(Tokens.BackchannelAccessToken)); - - context.ProxyRequest.Options.Set( - key : new(Tokens.BackchannelAccessTokenExpirationDate), - value: result.Properties.GetTokenValue(Tokens.BackchannelAccessTokenExpirationDate)); - - context.ProxyRequest.Options.Set( - key : new(Tokens.RefreshToken), - value: result.Properties.GetTokenValue(Tokens.RefreshToken)); - }); - - builder.AddResponseTransform(async context => - { - // If tokens were refreshed during the request handling (e.g due to the stored access token being - // expired or a 401 error response being returned by the resource server), extract and attach them - // to the authentication cookie that will be returned to the browser: doing that is essential as - // OpenIddict uses rolling refresh tokens: if the refresh token wasn't replaced, future refresh - // token requests would end up being rejected as they would be treated as replayed requests. - - if (context.ProxyResponse is not TokenRefreshingHttpResponseMessage response) - { - return; - } - - var result = await context.HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme); - if (result is not { Succeeded: true }) - { - return; - } - - // Override the tokens using the values returned in the token response. - var properties = result.Properties.Clone(); - properties.UpdateTokenValue(Tokens.BackchannelAccessToken, response.RefreshTokenAuthenticationResult.AccessToken); - - properties.UpdateTokenValue(Tokens.BackchannelAccessTokenExpirationDate, - response.RefreshTokenAuthenticationResult.AccessTokenExpirationDate?.ToString(CultureInfo.InvariantCulture) ?? string.Empty); - - // Note: if no refresh token was returned, preserve the refresh token initially returned. - if (!string.IsNullOrEmpty(response.RefreshTokenAuthenticationResult.RefreshToken)) - { - properties.UpdateTokenValue(Tokens.RefreshToken, response.RefreshTokenAuthenticationResult.RefreshToken); - } - - // Remove the redirect URI from the authentication properties - // to prevent the cookies handler from genering a 302 response. - properties.RedirectUri = null; - - // Replace the creation/expiration dates of the authentication ticket to extend the lifetime of the cookie. - // - // Note: doing that is not mandatory: if the expiration date is not replaced here, the resulting cookie - // will have the same expiration date as the authentication cookie present in the HTTP request headers. - // - // In any case, if the sliding expiration mechanism is enabled, the cookie (but not the data it contains) - // will be automatically renewed by the cookie handler upon reaching half of the cookie's lifespan. - properties.IssuedUtc = TimeProvider.System.GetUtcNow(); - properties.ExpiresUtc = properties.IssuedUtc + TimeSpan.FromDays(7); - - // Note: this event handler can be called concurrently for the same user if multiple HTTP - // responses are returned in parallel: in this case, the browser will always store the latest - // cookie received and the refresh tokens stored in the other cookies will be discarded. - await context.HttpContext.SignInAsync(result.Ticket.AuthenticationScheme, result.Principal, properties); - }); - }); - - // Replace the default HTTP client factory used by YARP by an instance able to inject the HTTP delegating - // handler that will be used to attach the access tokens to HTTP requests or refresh tokens if necessary. - services.Replace(ServiceDescriptor.Singleton()); - - // 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, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - app.UseWebAssemblyDebugging(); - } - else - { - app.UseExceptionHandler("/Error"); - } - - app.UseHttpsRedirection(); - app.UseBlazorFrameworkFiles(); - app.UseStaticFiles(); - - app.UseRouting(); - app.UseAuthentication(); - app.UseAuthorization(); - - app.UseEndpoints(endpoints => - { - endpoints.MapRazorPages(); - endpoints.MapControllers(); - - // Note: by default, the cookie authentication middleware automatically redirects - // the user agent to the login page configured in the cookie authentication options. - // In this case, this behavior is not desirable as an HTTP 401 response MUST be - // returned to the WASM client to automatically redirect the user agent to the - // login page. As such, this logic is disabled for all proxied requests. - endpoints.MapReverseProxy(ConfigureProxyPipeline).DisableCookieRedirect(); - - endpoints.MapFallbackToPage("/_Host"); - }); - - static void ConfigureProxyPipeline(IReverseProxyApplicationBuilder app) - { - app.Use(async (context, next) => - { - await next(); - - // Note: if an "access_denied" error occurred while trying to refresh tokens, - // trigger a cookie authentication challenge to let the WASM client know that - // the user should be redirected to the login page to re-authenticate. - var exception = context.GetForwarderErrorFeature()?.Exception; - if (exception is ProtocolException { Error: Errors.InvalidGrant }) - { - context.Response.Clear(); - - await context.ChallengeAsync(CookieAuthenticationDefaults.AuthenticationScheme); - } - }); - } - } -} diff --git a/samples/Hollastin/Hollastin.Server/Program.cs b/samples/Hollastin/Hollastin.Server/Program.cs index 2266622d..c70ad365 100644 --- a/samples/Hollastin/Hollastin.Server/Program.cs +++ b/samples/Hollastin/Hollastin.Server/Program.cs @@ -1,11 +1,101 @@ -namespace Hollastin.Server; +using Hollastin.Server; +using Hollastin.Server.Models; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Quartz; -public static class Program +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddControllersWithViews(); + +builder.Services.AddDbContext(options => +{ + // Configure the context to use sqlite. + options.UseSqlite($"Filename={Path.Combine(Path.GetTempPath(), "openiddict-hollastin-server.sqlite3")}"); + + // Register the entity sets needed by OpenIddict. + // Note: use the generic overload if you need + // to replace the default OpenIddict entities. + options.UseOpenIddict(); +}); + +// Register the Identity services. +builder.Services.AddIdentity() + .AddEntityFrameworkStores() + .AddDefaultTokenProviders(); + +// OpenIddict offers native integration with Quartz.NET to perform scheduled tasks +// (like pruning orphaned authorizations/tokens from the database) at regular intervals. +builder.Services.AddQuartz(options => { - public static void Main(string[] args) => - CreateHostBuilder(args).Build().Run(); + options.UseSimpleTypeLoader(); + options.UseInMemoryStore(); +}); + +// Register the Quartz.NET service and configure it to block shutdown until jobs are complete. +builder.Services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true); + +builder.Services.AddOpenIddict() + + // Register the OpenIddict core components. + .AddCore(options => + { + // Configure OpenIddict to use the Entity Framework Core stores and models. + // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities. + options.UseEntityFrameworkCore() + .UseDbContext(); + + // Enable Quartz.NET integration. + options.UseQuartz(); + }) + + // Register the OpenIddict server components. + .AddServer(options => + { + // Enable the token endpoint. + options.SetTokenEndpointUris("connect/token"); + + // Enable the password flow. + options.AllowPasswordFlow(); + + // Accept anonymous clients (i.e clients that don't send a client_id). + options.AcceptAnonymousClients(); + + // Register the signing and encryption credentials. + options.AddDevelopmentEncryptionCertificate() + .AddDevelopmentSigningCertificate(); + + // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. + options.UseAspNetCore() + .EnableTokenEndpointPassthrough(); + }) + + // Register the OpenIddict validation components. + .AddValidation(options => + { + // Import the configuration from the local OpenIddict server instance. + options.UseLocalServer(); + + // Register the ASP.NET Core host. + options.UseAspNetCore(); + }); + +// Register the worker responsible for creating and seeding the SQL database. +// Note: in a real world application, this step should be part of a setup script. +builder.Services.AddHostedService(); + +var app = builder.Build(); + +app.UseDeveloperExceptionPage(); + +app.UseRouting(); + +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapControllers(); +app.MapDefaultControllerRoute(); + +app.UseWelcomePage("/"); - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(builder => builder.UseStartup()); -} +app.Run(); \ No newline at end of file diff --git a/samples/Hollastin/Hollastin.Server/Startup.cs b/samples/Hollastin/Hollastin.Server/Startup.cs deleted file mode 100644 index 170fdf6f..00000000 --- a/samples/Hollastin/Hollastin.Server/Startup.cs +++ /dev/null @@ -1,113 +0,0 @@ -using Hollastin.Server.Models; -using Microsoft.AspNetCore.Identity; -using Microsoft.EntityFrameworkCore; -using Quartz; - -namespace Hollastin.Server; - -public class Startup -{ - public Startup(IConfiguration configuration) - => Configuration = configuration; - - public IConfiguration Configuration { get; } - - public void ConfigureServices(IServiceCollection services) - { - services.AddControllersWithViews(); - - services.AddDbContext(options => - { - // Configure the context to use sqlite. - options.UseSqlite($"Filename={Path.Combine(Path.GetTempPath(), "openiddict-hollastin-server.sqlite3")}"); - - // Register the entity sets needed by OpenIddict. - // Note: use the generic overload if you need - // to replace the default OpenIddict entities. - options.UseOpenIddict(); - }); - - // Register the Identity services. - services.AddIdentity() - .AddEntityFrameworkStores() - .AddDefaultTokenProviders(); - - // OpenIddict offers native integration with Quartz.NET to perform scheduled tasks - // (like pruning orphaned authorizations/tokens from the database) at regular intervals. - services.AddQuartz(options => - { - options.UseSimpleTypeLoader(); - options.UseInMemoryStore(); - }); - - // Register the Quartz.NET service and configure it to block shutdown until jobs are complete. - services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true); - - services.AddOpenIddict() - - // Register the OpenIddict core components. - .AddCore(options => - { - // Configure OpenIddict to use the Entity Framework Core stores and models. - // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities. - options.UseEntityFrameworkCore() - .UseDbContext(); - - // Enable Quartz.NET integration. - options.UseQuartz(); - }) - - // Register the OpenIddict server components. - .AddServer(options => - { - // Enable the token endpoint. - options.SetTokenEndpointUris("connect/token"); - - // Enable the password flow. - options.AllowPasswordFlow(); - - // Accept anonymous clients (i.e clients that don't send a client_id). - options.AcceptAnonymousClients(); - - // Register the signing and encryption credentials. - options.AddDevelopmentEncryptionCertificate() - .AddDevelopmentSigningCertificate(); - - // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. - options.UseAspNetCore() - .EnableTokenEndpointPassthrough(); - }) - - // Register the OpenIddict validation components. - .AddValidation(options => - { - // Import the configuration from the local OpenIddict server instance. - options.UseLocalServer(); - - // Register the ASP.NET Core host. - options.UseAspNetCore(); - }); - - // Register the worker responsible for creating and seeding the SQL database. - // Note: in a real world application, this step should be part of a setup script. - services.AddHostedService(); - } - - public void Configure(IApplicationBuilder app) - { - app.UseDeveloperExceptionPage(); - - app.UseRouting(); - - app.UseAuthentication(); - app.UseAuthorization(); - - app.UseEndpoints(endpoints => - { - endpoints.MapControllers(); - endpoints.MapDefaultControllerRoute(); - }); - - app.UseWelcomePage(); - } -} diff --git a/samples/Imynusoph/Imynusoph.Server/Program.cs b/samples/Imynusoph/Imynusoph.Server/Program.cs index b20e0fcb..25805d2b 100644 --- a/samples/Imynusoph/Imynusoph.Server/Program.cs +++ b/samples/Imynusoph/Imynusoph.Server/Program.cs @@ -1,11 +1,102 @@ -namespace Imynusoph.Server; +using Imynusoph.Server; +using Imynusoph.Server.Models; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Quartz; -public static class Program +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddControllersWithViews(); + +builder.Services.AddDbContext(options => +{ + // Configure the context to use sqlite. + options.UseSqlite($"Filename={Path.Combine(Path.GetTempPath(), "openiddict-imynusoph-server.sqlite3")}"); + + // Register the entity sets needed by OpenIddict. + // Note: use the generic overload if you need + // to replace the default OpenIddict entities. + options.UseOpenIddict(); +}); + +// Register the Identity services. +builder.Services.AddIdentity() + .AddEntityFrameworkStores() + .AddDefaultTokenProviders(); + +// OpenIddict offers native integration with Quartz.NET to perform scheduled tasks +// (like pruning orphaned authorizations/tokens from the database) at regular intervals. +builder.Services.AddQuartz(options => { - public static void Main(string[] args) => - CreateHostBuilder(args).Build().Run(); + options.UseSimpleTypeLoader(); + options.UseInMemoryStore(); +}); + +// Register the Quartz.NET service and configure it to block shutdown until jobs are complete. +builder.Services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true); + +builder.Services.AddOpenIddict() + + // Register the OpenIddict core components. + .AddCore(options => + { + // Configure OpenIddict to use the Entity Framework Core stores and models. + // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities. + options.UseEntityFrameworkCore() + .UseDbContext(); + + // Enable Quartz.NET integration. + options.UseQuartz(); + }) + + // Register the OpenIddict server components. + .AddServer(options => + { + // Enable the token endpoint. + options.SetTokenEndpointUris("connect/token"); + + // Enable the password and the refresh token flows. + options.AllowPasswordFlow() + .AllowRefreshTokenFlow(); + + // Accept anonymous clients (i.e clients that don't send a client_id). + options.AcceptAnonymousClients(); + + // Register the signing and encryption credentials. + options.AddDevelopmentEncryptionCertificate() + .AddDevelopmentSigningCertificate(); + + // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. + options.UseAspNetCore() + .EnableTokenEndpointPassthrough(); + }) + + // Register the OpenIddict validation components. + .AddValidation(options => + { + // Import the configuration from the local OpenIddict server instance. + options.UseLocalServer(); + + // Register the ASP.NET Core host. + options.UseAspNetCore(); + }); + +// Register the worker responsible for seeding the database. +// Note: in a real world application, this step should be part of a setup script. +builder.Services.AddHostedService(); + +var app = builder.Build(); + +app.UseDeveloperExceptionPage(); + +app.UseRouting(); + +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapControllers(); +app.MapDefaultControllerRoute(); + +app.UseWelcomePage("/"); - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(builder => builder.UseStartup()); -} +app.Run(); \ No newline at end of file diff --git a/samples/Imynusoph/Imynusoph.Server/Startup.cs b/samples/Imynusoph/Imynusoph.Server/Startup.cs deleted file mode 100644 index ef2510a9..00000000 --- a/samples/Imynusoph/Imynusoph.Server/Startup.cs +++ /dev/null @@ -1,116 +0,0 @@ -using Imynusoph.Server.Models; -using Microsoft.AspNetCore.Identity; -using Microsoft.EntityFrameworkCore; -using Quartz; - -namespace Imynusoph.Server; - -public class Startup -{ - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - public IConfiguration Configuration { get; } - - public void ConfigureServices(IServiceCollection services) - { - services.AddControllersWithViews(); - - services.AddDbContext(options => - { - // Configure the context to use sqlite. - options.UseSqlite($"Filename={Path.Combine(Path.GetTempPath(), "openiddict-imynusoph-server.sqlite3")}"); - - // Register the entity sets needed by OpenIddict. - // Note: use the generic overload if you need - // to replace the default OpenIddict entities. - options.UseOpenIddict(); - }); - - // Register the Identity services. - services.AddIdentity() - .AddEntityFrameworkStores() - .AddDefaultTokenProviders(); - - // OpenIddict offers native integration with Quartz.NET to perform scheduled tasks - // (like pruning orphaned authorizations/tokens from the database) at regular intervals. - services.AddQuartz(options => - { - options.UseSimpleTypeLoader(); - options.UseInMemoryStore(); - }); - - // Register the Quartz.NET service and configure it to block shutdown until jobs are complete. - services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true); - - services.AddOpenIddict() - - // Register the OpenIddict core components. - .AddCore(options => - { - // Configure OpenIddict to use the Entity Framework Core stores and models. - // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities. - options.UseEntityFrameworkCore() - .UseDbContext(); - - // Enable Quartz.NET integration. - options.UseQuartz(); - }) - - // Register the OpenIddict server components. - .AddServer(options => - { - // Enable the token endpoint. - options.SetTokenEndpointUris("connect/token"); - - // Enable the password and the refresh token flows. - options.AllowPasswordFlow() - .AllowRefreshTokenFlow(); - - // Accept anonymous clients (i.e clients that don't send a client_id). - options.AcceptAnonymousClients(); - - // Register the signing and encryption credentials. - options.AddDevelopmentEncryptionCertificate() - .AddDevelopmentSigningCertificate(); - - // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. - options.UseAspNetCore() - .EnableTokenEndpointPassthrough(); - }) - - // Register the OpenIddict validation components. - .AddValidation(options => - { - // Import the configuration from the local OpenIddict server instance. - options.UseLocalServer(); - - // Register the ASP.NET Core host. - options.UseAspNetCore(); - }); - - // Register the worker responsible for seeding the database. - // Note: in a real world application, this step should be part of a setup script. - services.AddHostedService(); - } - - public void Configure(IApplicationBuilder app) - { - app.UseDeveloperExceptionPage(); - - app.UseRouting(); - - app.UseAuthentication(); - app.UseAuthorization(); - - app.UseEndpoints(endpoints => - { - endpoints.MapControllers(); - endpoints.MapDefaultControllerRoute(); - }); - - app.UseWelcomePage(); - } -} diff --git a/samples/Matty/Matty.Server/Program.cs b/samples/Matty/Matty.Server/Program.cs index 352c1519..c1c05fb4 100644 --- a/samples/Matty/Matty.Server/Program.cs +++ b/samples/Matty/Matty.Server/Program.cs @@ -1,11 +1,129 @@ -namespace Matty.Server; +using Matty.Server; +using Matty.Server.Data; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Quartz; +using static OpenIddict.Abstractions.OpenIddictConstants; -public static class Program +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddControllersWithViews(); +builder.Services.AddRazorPages(); + +builder.Services.AddDbContext(options => +{ + // Configure the context to use sqlite. + options.UseSqlite($"Filename={Path.Combine(Path.GetTempPath(), "openiddict-matty-server.sqlite3")}"); + + // Register the entity sets needed by OpenIddict. + // Note: use the generic overload if you need + // to replace the default OpenIddict entities. + options.UseOpenIddict(); +}); + +builder.Services.AddDatabaseDeveloperPageExceptionFilter(); + +// Register the Identity services. +builder.Services.AddIdentity() + .AddEntityFrameworkStores() + .AddDefaultTokenProviders() + .AddDefaultUI(); + +// OpenIddict offers native integration with Quartz.NET to perform scheduled tasks +// (like pruning orphaned authorizations/tokens from the database) at regular intervals. +builder.Services.AddQuartz(options => +{ + options.UseSimpleTypeLoader(); + options.UseInMemoryStore(); +}); + +// Register the Quartz.NET service and configure it to block shutdown until jobs are complete. +builder.Services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true); + +builder.Services.AddOpenIddict() + + // Register the OpenIddict core components. + .AddCore(options => + { + // Configure OpenIddict to use the Entity Framework Core stores and models. + // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities. + options.UseEntityFrameworkCore() + .UseDbContext(); + + // Enable Quartz.NET integration. + options.UseQuartz(); + }) + + // Register the OpenIddict server components. + .AddServer(options => + { + // Enable the device, verification, token and userinfo endpoints. + options.SetDeviceAuthorizationEndpointUris("connect/device") + .SetEndUserVerificationEndpointUris("connect/verify") + .SetTokenEndpointUris("connect/token") + .SetUserInfoEndpointUris("connect/userinfo"); + + // Mark the "email", "profile" and "roles" scopes as supported scopes. + options.RegisterScopes(Scopes.Email, Scopes.Profile, Scopes.Roles); + + // Note: this sample uses the device code and refresh token flows but you can + // enable the other flows if you need to support implicit, password, etc. + options.AllowDeviceAuthorizationFlow() + .AllowRefreshTokenFlow(); + + // Register the signing and encryption credentials. + options.AddDevelopmentEncryptionCertificate() + .AddDevelopmentSigningCertificate(); + + // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. + options.UseAspNetCore() + .EnableTokenEndpointPassthrough() + .EnableUserInfoEndpointPassthrough() + .EnableEndUserVerificationEndpointPassthrough() + .EnableStatusCodePagesIntegration(); + }) + + // Register the OpenIddict validation components. + .AddValidation(options => + { + // Import the configuration from the local OpenIddict server instance. + options.UseLocalServer(); + + // Register the ASP.NET Core host. + options.UseAspNetCore(); + }); + +// Register the worker responsible for creating and seeding the SQL database. +// Note: in a real world application, this step should be part of a setup script. +builder.Services.AddHostedService(); + +var app = builder.Build(); + + +if (builder.Environment.IsDevelopment()) { - public static void Main(string[] args) => - CreateHostBuilder(args).Build().Run(); + app.UseDeveloperExceptionPage(); + app.UseMigrationsEndPoint(); +} +else +{ + app.UseStatusCodePagesWithReExecute("~/error"); + //app.UseExceptionHandler("~/error"); - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(options => options.UseStartup()); + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + //app.UseHsts(); } + +app.UseHttpsRedirection(); +app.UseStaticFiles(); + +app.UseRouting(); + +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapControllers(); +app.MapDefaultControllerRoute(); +app.MapRazorPages(); + +app.Run(); \ No newline at end of file diff --git a/samples/Matty/Matty.Server/Startup.cs b/samples/Matty/Matty.Server/Startup.cs deleted file mode 100644 index 41db9b58..00000000 --- a/samples/Matty/Matty.Server/Startup.cs +++ /dev/null @@ -1,139 +0,0 @@ -using Matty.Server.Data; -using Microsoft.AspNetCore.Identity; -using Microsoft.EntityFrameworkCore; -using Quartz; -using static OpenIddict.Abstractions.OpenIddictConstants; - -namespace Matty.Server; - -public class Startup -{ - public Startup(IConfiguration configuration) - => Configuration = configuration; - - public IConfiguration Configuration { get; } - - public void ConfigureServices(IServiceCollection services) - { - services.AddControllersWithViews(); - services.AddRazorPages(); - - services.AddDbContext(options => - { - // Configure the context to use sqlite. - options.UseSqlite($"Filename={Path.Combine(Path.GetTempPath(), "openiddict-matty-server.sqlite3")}"); - - // Register the entity sets needed by OpenIddict. - // Note: use the generic overload if you need - // to replace the default OpenIddict entities. - options.UseOpenIddict(); - }); - - services.AddDatabaseDeveloperPageExceptionFilter(); - - // Register the Identity services. - services.AddIdentity() - .AddEntityFrameworkStores() - .AddDefaultTokenProviders() - .AddDefaultUI(); - - // OpenIddict offers native integration with Quartz.NET to perform scheduled tasks - // (like pruning orphaned authorizations/tokens from the database) at regular intervals. - services.AddQuartz(options => - { - options.UseSimpleTypeLoader(); - options.UseInMemoryStore(); - }); - - // Register the Quartz.NET service and configure it to block shutdown until jobs are complete. - services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true); - - services.AddOpenIddict() - - // Register the OpenIddict core components. - .AddCore(options => - { - // Configure OpenIddict to use the Entity Framework Core stores and models. - // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities. - options.UseEntityFrameworkCore() - .UseDbContext(); - - // Enable Quartz.NET integration. - options.UseQuartz(); - }) - - // Register the OpenIddict server components. - .AddServer(options => - { - // Enable the device, verification, token and userinfo endpoints. - options.SetDeviceAuthorizationEndpointUris("connect/device") - .SetEndUserVerificationEndpointUris("connect/verify") - .SetTokenEndpointUris("connect/token") - .SetUserInfoEndpointUris("connect/userinfo"); - - // Mark the "email", "profile" and "roles" scopes as supported scopes. - options.RegisterScopes(Scopes.Email, Scopes.Profile, Scopes.Roles); - - // Note: this sample uses the device code and refresh token flows but you can - // enable the other flows if you need to support implicit, password, etc. - options.AllowDeviceAuthorizationFlow() - .AllowRefreshTokenFlow(); - - // Register the signing and encryption credentials. - options.AddDevelopmentEncryptionCertificate() - .AddDevelopmentSigningCertificate(); - - // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. - options.UseAspNetCore() - .EnableTokenEndpointPassthrough() - .EnableUserInfoEndpointPassthrough() - .EnableEndUserVerificationEndpointPassthrough() - .EnableStatusCodePagesIntegration(); - }) - - // Register the OpenIddict validation components. - .AddValidation(options => - { - // Import the configuration from the local OpenIddict server instance. - options.UseLocalServer(); - - // Register the ASP.NET Core host. - options.UseAspNetCore(); - }); - - // Register the worker responsible for creating and seeding the SQL database. - // Note: in a real world application, this step should be part of a setup script. - services.AddHostedService(); - } - - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - app.UseMigrationsEndPoint(); - } - else - { - app.UseStatusCodePagesWithReExecute("~/error"); - //app.UseExceptionHandler("~/error"); - - // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. - //app.UseHsts(); - } - app.UseHttpsRedirection(); - app.UseStaticFiles(); - - app.UseRouting(); - - app.UseAuthentication(); - app.UseAuthorization(); - - app.UseEndpoints(endpoints => - { - endpoints.MapControllers(); - endpoints.MapDefaultControllerRoute(); - endpoints.MapRazorPages(); - }); - } -} diff --git a/samples/Velusia/Velusia.Client/Program.cs b/samples/Velusia/Velusia.Client/Program.cs index 4589f9c2..489e7308 100644 --- a/samples/Velusia/Velusia.Client/Program.cs +++ b/samples/Velusia/Velusia.Client/Program.cs @@ -1,11 +1,128 @@ -namespace Velusia.Client; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.EntityFrameworkCore; +using OpenIddict.Client; +using Quartz; +using Velusia.Client; +using Velusia.Client.Models; +using static OpenIddict.Abstractions.OpenIddictConstants; -public static class Program +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddDbContext(options => +{ + // Configure the context to use sqlite. + options.UseSqlite($"Filename={Path.Combine(Path.GetTempPath(), "openiddict-velusia-client.sqlite3")}"); + + // Register the entity sets needed by OpenIddict. + // Note: use the generic overload if you need + // to replace the default OpenIddict entities. + options.UseOpenIddict(); +}); + +builder.Services.AddAuthentication(options => +{ + options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; +}) + +.AddCookie(options => +{ + options.LoginPath = "/login"; + options.LogoutPath = "/logout"; + options.ExpireTimeSpan = TimeSpan.FromMinutes(50); + options.SlidingExpiration = false; +}); + +// OpenIddict offers native integration with Quartz.NET to perform scheduled tasks +// (like pruning orphaned authorizations from the database) at regular intervals. +builder.Services.AddQuartz(options => { - public static void Main(string[] args) => - CreateHostBuilder(args).Build().Run(); + options.UseSimpleTypeLoader(); + options.UseInMemoryStore(); +}); + +// Register the Quartz.NET service and configure it to block shutdown until jobs are complete. +builder.Services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true); + +builder.Services.AddOpenIddict() + + // Register the OpenIddict core components. + .AddCore(options => + { + // Configure OpenIddict to use the Entity Framework Core stores and models. + // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities. + options.UseEntityFrameworkCore() + .UseDbContext(); + + // Developers who prefer using MongoDB can remove the previous lines + // and configure OpenIddict to use the specified MongoDB database: + // options.UseMongoDb() + // .UseDatabase(new MongoClient().GetDatabase("openiddict")); + + // Enable Quartz.NET integration. + options.UseQuartz(); + }) + + // Register the OpenIddict client components. + .AddClient(options => + { + // Note: this sample uses the code flow, but you can enable the other flows if necessary. + options.AllowAuthorizationCodeFlow(); + + // Register the signing and encryption credentials used to protect + // sensitive data like the state tokens produced by OpenIddict. + options.AddDevelopmentEncryptionCertificate() + .AddDevelopmentSigningCertificate(); + + // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. + options.UseAspNetCore() + .EnableStatusCodePagesIntegration() + .EnableRedirectionEndpointPassthrough() + .EnablePostLogoutRedirectionEndpointPassthrough(); + + // Register the System.Net.Http integration and use the identity of the current + // assembly as a more specific user agent, which can be useful when dealing with + // providers that use the user agent as a way to throttle requests (e.g Reddit). + options.UseSystemNetHttp() + .SetProductInformation(typeof(Program).Assembly); + + // Add a client registration matching the client application definition in the server project. + options.AddRegistration(new OpenIddictClientRegistration + { + Issuer = new Uri("https://localhost:44313/", UriKind.Absolute), + + ClientId = "mvc", + ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654", + Scopes = { Scopes.Email, Scopes.Profile }, + + // Note: to mitigate mix-up attacks, it's recommended to use a unique redirection endpoint + // URI per provider, unless all the registered providers support returning a special "iss" + // parameter containing their URL as part of authorization responses. For more information, + // see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.4. + RedirectUri = new Uri("callback/login/local", UriKind.Relative), + PostLogoutRedirectUri = new Uri("callback/logout/local", UriKind.Relative) + }); + }); + +builder.Services.AddHttpClient(); + +builder.Services.AddControllersWithViews(); + +// 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. +builder.Services.AddHostedService(); + +var app = builder.Build(); + +app.UseDeveloperExceptionPage(); + +app.UseStaticFiles(); + +app.UseRouting(); + +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapControllers(); +app.MapDefaultControllerRoute(); - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(builder => builder.UseStartup()); -} +app.Run(); \ No newline at end of file diff --git a/samples/Velusia/Velusia.Client/Startup.cs b/samples/Velusia/Velusia.Client/Startup.cs deleted file mode 100644 index 3802ea64..00000000 --- a/samples/Velusia/Velusia.Client/Startup.cs +++ /dev/null @@ -1,140 +0,0 @@ -using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.EntityFrameworkCore; -using OpenIddict.Client; -using Quartz; -using Velusia.Client.Models; -using static OpenIddict.Abstractions.OpenIddictConstants; - -namespace Velusia.Client; - -public class Startup -{ - public Startup(IConfiguration configuration) - => Configuration = configuration; - - public IConfiguration Configuration { get; } - - public void ConfigureServices(IServiceCollection services) - { - services.AddDbContext(options => - { - // Configure the context to use sqlite. - options.UseSqlite($"Filename={Path.Combine(Path.GetTempPath(), "openiddict-velusia-client.sqlite3")}"); - - // Register the entity sets needed by OpenIddict. - // Note: use the generic overload if you need - // to replace the default OpenIddict entities. - options.UseOpenIddict(); - }); - - services.AddAuthentication(options => - { - options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; - }) - - .AddCookie(options => - { - options.LoginPath = "/login"; - options.LogoutPath = "/logout"; - options.ExpireTimeSpan = TimeSpan.FromMinutes(50); - options.SlidingExpiration = false; - }); - - // OpenIddict offers native integration with Quartz.NET to perform scheduled tasks - // (like pruning orphaned authorizations from the database) at regular intervals. - services.AddQuartz(options => - { - options.UseSimpleTypeLoader(); - options.UseInMemoryStore(); - }); - - // Register the Quartz.NET service and configure it to block shutdown until jobs are complete. - services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true); - - services.AddOpenIddict() - - // Register the OpenIddict core components. - .AddCore(options => - { - // Configure OpenIddict to use the Entity Framework Core stores and models. - // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities. - options.UseEntityFrameworkCore() - .UseDbContext(); - - // Developers who prefer using MongoDB can remove the previous lines - // and configure OpenIddict to use the specified MongoDB database: - // options.UseMongoDb() - // .UseDatabase(new MongoClient().GetDatabase("openiddict")); - - // Enable Quartz.NET integration. - options.UseQuartz(); - }) - - // Register the OpenIddict client components. - .AddClient(options => - { - // Note: this sample uses the code flow, but you can enable the other flows if necessary. - options.AllowAuthorizationCodeFlow(); - - // Register the signing and encryption credentials used to protect - // sensitive data like the state tokens produced by OpenIddict. - options.AddDevelopmentEncryptionCertificate() - .AddDevelopmentSigningCertificate(); - - // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. - options.UseAspNetCore() - .EnableStatusCodePagesIntegration() - .EnableRedirectionEndpointPassthrough() - .EnablePostLogoutRedirectionEndpointPassthrough(); - - // Register the System.Net.Http integration and use the identity of the current - // assembly as a more specific user agent, which can be useful when dealing with - // providers that use the user agent as a way to throttle requests (e.g Reddit). - options.UseSystemNetHttp() - .SetProductInformation(typeof(Startup).Assembly); - - // Add a client registration matching the client application definition in the server project. - options.AddRegistration(new OpenIddictClientRegistration - { - Issuer = new Uri("https://localhost:44313/", UriKind.Absolute), - - ClientId = "mvc", - ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654", - Scopes = { Scopes.Email, Scopes.Profile }, - - // Note: to mitigate mix-up attacks, it's recommended to use a unique redirection endpoint - // URI per provider, unless all the registered providers support returning a special "iss" - // parameter containing their URL as part of authorization responses. For more information, - // see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.4. - RedirectUri = new Uri("callback/login/local", UriKind.Relative), - PostLogoutRedirectUri = new Uri("callback/logout/local", UriKind.Relative) - }); - }); - - services.AddHttpClient(); - - services.AddControllersWithViews(); - - // 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) - { - app.UseDeveloperExceptionPage(); - - app.UseStaticFiles(); - - app.UseRouting(); - - app.UseAuthentication(); - app.UseAuthorization(); - - app.UseEndpoints(endpoints => - { - endpoints.MapControllers(); - endpoints.MapDefaultControllerRoute(); - }); - } -} diff --git a/samples/Velusia/Velusia.Server/Program.cs b/samples/Velusia/Velusia.Server/Program.cs index 563dd982..23c74376 100644 --- a/samples/Velusia/Velusia.Server/Program.cs +++ b/samples/Velusia/Velusia.Server/Program.cs @@ -1,11 +1,164 @@ -namespace Velusia.Server; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Quartz; +using Velusia.Server; +using Velusia.Server.Data; +using static OpenIddict.Abstractions.OpenIddictConstants; -public static class Program +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddControllersWithViews(); +builder.Services.AddRazorPages(); + +builder.Services.AddDbContext(options => { - public static void Main(string[] args) => - CreateHostBuilder(args).Build().Run(); + // Configure the context to use sqlite. + options.UseSqlite($"Filename={Path.Combine(Path.GetTempPath(), "openiddict-velusia-server.sqlite3")}"); + + // Register the entity sets needed by OpenIddict. + // Note: use the generic overload if you need + // to replace the default OpenIddict entities. + options.UseOpenIddict(); +}); + +builder.Services.AddDatabaseDeveloperPageExceptionFilter(); + +// Register the Identity services. +builder.Services.AddIdentity() + .AddEntityFrameworkStores() + .AddDefaultTokenProviders() + .AddDefaultUI(); + +// OpenIddict offers native integration with Quartz.NET to perform scheduled tasks +// (like pruning orphaned authorizations/tokens from the database) at regular intervals. +builder.Services.AddQuartz(options => +{ + options.UseSimpleTypeLoader(); + options.UseInMemoryStore(); +}); + +// Register the Quartz.NET service and configure it to block shutdown until jobs are complete. +builder.Services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true); + +builder.Services.AddOpenIddict() + + // Register the OpenIddict core components. + .AddCore(options => + { + // Configure OpenIddict to use the Entity Framework Core stores and models. + // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities. + options.UseEntityFrameworkCore() + .UseDbContext(); + + // Enable Quartz.NET integration. + options.UseQuartz(); + }) + + // Register the OpenIddict client components. + .AddClient(options => + { + // Note: this sample uses the code flow, but you can enable the other flows if necessary. + options.AllowAuthorizationCodeFlow(); + + // Register the signing and encryption credentials used to protect + // sensitive data like the state tokens produced by OpenIddict. + options.AddDevelopmentEncryptionCertificate() + .AddDevelopmentSigningCertificate(); + + // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. + options.UseAspNetCore() + .EnableStatusCodePagesIntegration() + .EnableRedirectionEndpointPassthrough(); + + // Register the System.Net.Http integration and use the identity of the current + // assembly as a more specific user agent, which can be useful when dealing with + // providers that use the user agent as a way to throttle requests (e.g Reddit). + options.UseSystemNetHttp() + .SetProductInformation(typeof(Program).Assembly); + + // Register the Web providers integrations. + // + // Note: to mitigate mix-up attacks, it's recommended to use a unique redirection endpoint + // URI per provider, unless all the registered providers support returning a special "iss" + // parameter containing their URL as part of authorization responses. For more information, + // see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.4. + options.UseWebProviders() + .AddGitHub(options => + { + options.SetClientId("c4ade52327b01ddacff3") + .SetClientSecret("da6bed851b75e317bf6b2cb67013679d9467c122") + .SetRedirectUri("callback/login/github"); + }); + }) + + // Register the OpenIddict server components. + .AddServer(options => + { + // Enable the authorization, logout, token and userinfo endpoints. + options.SetAuthorizationEndpointUris("connect/authorize") + .SetEndSessionEndpointUris("connect/logout") + .SetTokenEndpointUris("connect/token") + .SetUserInfoEndpointUris("connect/userinfo"); - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(options => options.UseStartup()); + // Mark the "email", "profile" and "roles" scopes as supported scopes. + options.RegisterScopes(Scopes.Email, Scopes.Profile, Scopes.Roles); + + // Note: this sample only uses the authorization code flow but you can enable + // the other flows if you need to support implicit, password or client credentials. + options.AllowAuthorizationCodeFlow(); + + // Register the signing and encryption credentials. + options.AddDevelopmentEncryptionCertificate() + .AddDevelopmentSigningCertificate(); + + // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. + options.UseAspNetCore() + .EnableAuthorizationEndpointPassthrough() + .EnableEndSessionEndpointPassthrough() + .EnableTokenEndpointPassthrough() + .EnableUserInfoEndpointPassthrough() + .EnableStatusCodePagesIntegration(); + }) + + // Register the OpenIddict validation components. + .AddValidation(options => + { + // Import the configuration from the local OpenIddict server instance. + options.UseLocalServer(); + + // Register the ASP.NET Core host. + options.UseAspNetCore(); + }); + +// Register the worker responsible for seeding the database. +// Note: in a real world application, this step should be part of a setup script. +builder.Services.AddHostedService(); + +var app = builder.Build(); + +if (builder.Environment.IsDevelopment()) +{ + app.UseDeveloperExceptionPage(); + app.UseMigrationsEndPoint(); +} +else +{ + app.UseStatusCodePagesWithReExecute("~/error"); + //app.UseExceptionHandler("~/error"); + + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + //app.UseHsts(); } +app.UseHttpsRedirection(); +app.UseStaticFiles(); + +app.UseRouting(); + +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapControllers(); +app.MapDefaultControllerRoute(); +app.MapRazorPages(); + +app.Run(); \ No newline at end of file diff --git a/samples/Velusia/Velusia.Server/Startup.cs b/samples/Velusia/Velusia.Server/Startup.cs deleted file mode 100644 index ca67c87f..00000000 --- a/samples/Velusia/Velusia.Server/Startup.cs +++ /dev/null @@ -1,176 +0,0 @@ -using Microsoft.AspNetCore.Identity; -using Microsoft.EntityFrameworkCore; -using Quartz; -using Velusia.Server.Data; -using static OpenIddict.Abstractions.OpenIddictConstants; - -namespace Velusia.Server; - -public class Startup -{ - public Startup(IConfiguration configuration) - => Configuration = configuration; - - public IConfiguration Configuration { get; } - - public void ConfigureServices(IServiceCollection services) - { - services.AddControllersWithViews(); - services.AddRazorPages(); - - services.AddDbContext(options => - { - // Configure the context to use sqlite. - options.UseSqlite($"Filename={Path.Combine(Path.GetTempPath(), "openiddict-velusia-server.sqlite3")}"); - - // Register the entity sets needed by OpenIddict. - // Note: use the generic overload if you need - // to replace the default OpenIddict entities. - options.UseOpenIddict(); - }); - - services.AddDatabaseDeveloperPageExceptionFilter(); - - // Register the Identity services. - services.AddIdentity() - .AddEntityFrameworkStores() - .AddDefaultTokenProviders() - .AddDefaultUI(); - - // OpenIddict offers native integration with Quartz.NET to perform scheduled tasks - // (like pruning orphaned authorizations/tokens from the database) at regular intervals. - services.AddQuartz(options => - { - options.UseSimpleTypeLoader(); - options.UseInMemoryStore(); - }); - - // Register the Quartz.NET service and configure it to block shutdown until jobs are complete. - services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true); - - services.AddOpenIddict() - - // Register the OpenIddict core components. - .AddCore(options => - { - // Configure OpenIddict to use the Entity Framework Core stores and models. - // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities. - options.UseEntityFrameworkCore() - .UseDbContext(); - - // Enable Quartz.NET integration. - options.UseQuartz(); - }) - - // Register the OpenIddict client components. - .AddClient(options => - { - // Note: this sample uses the code flow, but you can enable the other flows if necessary. - options.AllowAuthorizationCodeFlow(); - - // Register the signing and encryption credentials used to protect - // sensitive data like the state tokens produced by OpenIddict. - options.AddDevelopmentEncryptionCertificate() - .AddDevelopmentSigningCertificate(); - - // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. - options.UseAspNetCore() - .EnableStatusCodePagesIntegration() - .EnableRedirectionEndpointPassthrough(); - - // Register the System.Net.Http integration and use the identity of the current - // assembly as a more specific user agent, which can be useful when dealing with - // providers that use the user agent as a way to throttle requests (e.g Reddit). - options.UseSystemNetHttp() - .SetProductInformation(typeof(Startup).Assembly); - - // Register the Web providers integrations. - // - // Note: to mitigate mix-up attacks, it's recommended to use a unique redirection endpoint - // URI per provider, unless all the registered providers support returning a special "iss" - // parameter containing their URL as part of authorization responses. For more information, - // see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.4. - options.UseWebProviders() - .AddGitHub(options => - { - options.SetClientId("c4ade52327b01ddacff3") - .SetClientSecret("da6bed851b75e317bf6b2cb67013679d9467c122") - .SetRedirectUri("callback/login/github"); - }); - }) - - // Register the OpenIddict server components. - .AddServer(options => - { - // Enable the authorization, logout, token and userinfo endpoints. - options.SetAuthorizationEndpointUris("connect/authorize") - .SetEndSessionEndpointUris("connect/logout") - .SetTokenEndpointUris("connect/token") - .SetUserInfoEndpointUris("connect/userinfo"); - - // Mark the "email", "profile" and "roles" scopes as supported scopes. - options.RegisterScopes(Scopes.Email, Scopes.Profile, Scopes.Roles); - - // Note: this sample only uses the authorization code flow but you can enable - // the other flows if you need to support implicit, password or client credentials. - options.AllowAuthorizationCodeFlow(); - - // Register the signing and encryption credentials. - options.AddDevelopmentEncryptionCertificate() - .AddDevelopmentSigningCertificate(); - - // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. - options.UseAspNetCore() - .EnableAuthorizationEndpointPassthrough() - .EnableEndSessionEndpointPassthrough() - .EnableTokenEndpointPassthrough() - .EnableUserInfoEndpointPassthrough() - .EnableStatusCodePagesIntegration(); - }) - - // Register the OpenIddict validation components. - .AddValidation(options => - { - // Import the configuration from the local OpenIddict server instance. - options.UseLocalServer(); - - // Register the ASP.NET Core host. - options.UseAspNetCore(); - }); - - // Register the worker responsible for seeding the database. - // Note: in a real world application, this step should be part of a setup script. - services.AddHostedService(); - } - - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - app.UseMigrationsEndPoint(); - } - else - { - app.UseStatusCodePagesWithReExecute("~/error"); - //app.UseExceptionHandler("~/error"); - - // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. - //app.UseHsts(); - } - app.UseHttpsRedirection(); - app.UseStaticFiles(); - - app.UseRouting(); - - app.UseAuthentication(); - app.UseAuthorization(); - - app.UseEndpoints(endpoints => - { - endpoints.MapControllers(); - endpoints.MapDefaultControllerRoute(); - endpoints.MapRazorPages(); - }); - } -} diff --git a/samples/Weytta/Weytta.Server/Program.cs b/samples/Weytta/Weytta.Server/Program.cs index 2d1e7609..7009b54b 100644 --- a/samples/Weytta/Weytta.Server/Program.cs +++ b/samples/Weytta/Weytta.Server/Program.cs @@ -1,11 +1,112 @@ -namespace Weytta.Server; +using Microsoft.EntityFrameworkCore; +using Quartz; +using Weytta.Server; +using static OpenIddict.Abstractions.OpenIddictConstants; -public static class Program +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddControllersWithViews(); + +builder.Services.AddDbContext(options => { - public static void Main(string[] args) => - CreateHostBuilder(args).Build().Run(); + // Configure the context to use sqlite. + options.UseSqlite($"Filename={Path.Combine(Path.GetTempPath(), "openiddict-weytta-server.sqlite3")}"); + + // Register the entity sets needed by OpenIddict. + // Note: use the generic overload if you need + // to replace the default OpenIddict entities. + options.UseOpenIddict(); +}); + +// OpenIddict offers native integration with Quartz.NET to perform scheduled tasks +// (like pruning orphaned authorizations/tokens from the database) at regular intervals. +builder.Services.AddQuartz(options => +{ + options.UseSimpleTypeLoader(); + options.UseInMemoryStore(); +}); + +// Register the Quartz.NET service and configure it to block shutdown until jobs are complete. +builder.Services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true); + +// Register the Negotiate handler (when running on IIS, it will automatically +// delegate the actual Integrated Windows Authentication process to IIS). +builder.Services.AddAuthentication() + .AddNegotiate(); + +builder.Services.AddOpenIddict() + + // Register the OpenIddict core components. + .AddCore(options => + { + // Configure OpenIddict to use the Entity Framework Core stores and models. + // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities. + options.UseEntityFrameworkCore() + .UseDbContext(); + + // Enable Quartz.NET integration. + options.UseQuartz(); + }) + + // Register the OpenIddict server components. + .AddServer(options => + { + // Enable the authorization and token endpoints. + options.SetAuthorizationEndpointUris("connect/authorize") + .SetTokenEndpointUris("connect/token"); + + // Mark the "email", "profile" and "roles" scopes as supported scopes. + options.RegisterScopes(Scopes.Email, Scopes.Profile, Scopes.Roles); + + // Note: this sample only uses the authorization code flow but you can enable + // the other flows if you need to support implicit, password or client credentials. + options.AllowAuthorizationCodeFlow(); - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(builder => builder.UseStartup()); + // Register the signing and encryption credentials. + options.AddDevelopmentEncryptionCertificate() + .AddDevelopmentSigningCertificate(); + + // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. + // + // Note: unlike other samples, this sample doesn't use token endpoint pass-through + // to handle token requests in a custom MVC action. As such, the token requests + // will be automatically handled by OpenIddict, that will reuse the identity + // resolved from the authorization code to produce access and identity tokens. + // + options.UseAspNetCore() + .EnableAuthorizationEndpointPassthrough() + .EnableStatusCodePagesIntegration(); + }) + + // Register the OpenIddict validation components. + .AddValidation(options => + { + // Import the configuration from the local OpenIddict server instance. + options.UseLocalServer(); + + // Register the ASP.NET Core host. + options.UseAspNetCore(); + }); + +// Register the worker responsible for seeding the database. +// Note: in a real world application, this step should be part of a setup script. +builder.Services.AddHostedService(); + +var app = builder.Build(); + +if (builder.Environment.IsDevelopment()) +{ + app.UseDeveloperExceptionPage(); } + +app.UseRouting(); + +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapControllers(); +app.MapDefaultControllerRoute(); + +app.UseWelcomePage("/"); + +app.Run(); \ No newline at end of file diff --git a/samples/Weytta/Weytta.Server/Startup.cs b/samples/Weytta/Weytta.Server/Startup.cs deleted file mode 100644 index 21541ab2..00000000 --- a/samples/Weytta/Weytta.Server/Startup.cs +++ /dev/null @@ -1,119 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Quartz; -using static OpenIddict.Abstractions.OpenIddictConstants; - -namespace Weytta.Server; - -public class Startup -{ - public void ConfigureServices(IServiceCollection services) - { - services.AddControllersWithViews(); - - services.AddDbContext(options => - { - // Configure the context to use sqlite. - options.UseSqlite($"Filename={Path.Combine(Path.GetTempPath(), "openiddict-weytta-server.sqlite3")}"); - - // Register the entity sets needed by OpenIddict. - // Note: use the generic overload if you need - // to replace the default OpenIddict entities. - options.UseOpenIddict(); - }); - - // OpenIddict offers native integration with Quartz.NET to perform scheduled tasks - // (like pruning orphaned authorizations/tokens from the database) at regular intervals. - services.AddQuartz(options => - { - options.UseSimpleTypeLoader(); - options.UseInMemoryStore(); - }); - - // Register the Quartz.NET service and configure it to block shutdown until jobs are complete. - services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true); - - // Register the Negotiate handler (when running on IIS, it will automatically - // delegate the actual Integrated Windows Authentication process to IIS). - services.AddAuthentication() - .AddNegotiate(); - - services.AddOpenIddict() - - // Register the OpenIddict core components. - .AddCore(options => - { - // Configure OpenIddict to use the Entity Framework Core stores and models. - // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities. - options.UseEntityFrameworkCore() - .UseDbContext(); - - // Enable Quartz.NET integration. - options.UseQuartz(); - }) - - // Register the OpenIddict server components. - .AddServer(options => - { - // Enable the authorization and token endpoints. - options.SetAuthorizationEndpointUris("connect/authorize") - .SetTokenEndpointUris("connect/token"); - - // Mark the "email", "profile" and "roles" scopes as supported scopes. - options.RegisterScopes(Scopes.Email, Scopes.Profile, Scopes.Roles); - - // Note: this sample only uses the authorization code flow but you can enable - // the other flows if you need to support implicit, password or client credentials. - options.AllowAuthorizationCodeFlow(); - - // Register the signing and encryption credentials. - options.AddDevelopmentEncryptionCertificate() - .AddDevelopmentSigningCertificate(); - - // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. - // - // Note: unlike other samples, this sample doesn't use token endpoint pass-through - // to handle token requests in a custom MVC action. As such, the token requests - // will be automatically handled by OpenIddict, that will reuse the identity - // resolved from the authorization code to produce access and identity tokens. - // - options.UseAspNetCore() - .EnableAuthorizationEndpointPassthrough() - .EnableStatusCodePagesIntegration(); - }) - - // Register the OpenIddict validation components. - .AddValidation(options => - { - // Import the configuration from the local OpenIddict server instance. - options.UseLocalServer(); - - // Register the ASP.NET Core host. - options.UseAspNetCore(); - }); - - // Register the worker responsible for seeding the database. - // Note: in a real world application, this step should be part of a setup script. - services.AddHostedService(); - } - - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - app.UseRouting(); - - app.UseAuthentication(); - app.UseAuthorization(); - - app.UseEndpoints(endpoints => - { - endpoints.MapControllers(); - endpoints.MapDefaultControllerRoute(); - }); - - app.UseWelcomePage(); - } -} diff --git a/samples/Zirku/Zirku.Api1/Program.cs b/samples/Zirku/Zirku.Api1/Program.cs index bb8ea894..21fd3589 100644 --- a/samples/Zirku/Zirku.Api1/Program.cs +++ b/samples/Zirku/Zirku.Api1/Program.cs @@ -44,6 +44,6 @@ app.MapGet("api", [Authorize] (ClaimsPrincipal user) => $"{user.Identity!.Name} is allowed to access Api1."); -app.UseWelcomePage(); +app.UseWelcomePage("/"); app.Run(); diff --git a/samples/Zirku/Zirku.Api2/Program.cs b/samples/Zirku/Zirku.Api2/Program.cs index f04690bc..90098852 100644 --- a/samples/Zirku/Zirku.Api2/Program.cs +++ b/samples/Zirku/Zirku.Api2/Program.cs @@ -48,6 +48,6 @@ app.MapGet("api", [Authorize] (ClaimsPrincipal user) => $"{user.Identity!.Name} is allowed to access Api2."); -app.UseWelcomePage(); +app.UseWelcomePage("/"); app.Run(); diff --git a/samples/Zirku/Zirku.Server/Program.cs b/samples/Zirku/Zirku.Server/Program.cs index 00452ed8..52a747c0 100644 --- a/samples/Zirku/Zirku.Server/Program.cs +++ b/samples/Zirku/Zirku.Server/Program.cs @@ -287,6 +287,6 @@ await manager.CreateAsync(new OpenIddictScopeDescriptor return Results.SignIn(new ClaimsPrincipal(identity), properties: null, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); }); -app.UseWelcomePage(); +app.UseWelcomePage("/"); app.Run();