using System.Security.Cryptography; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using OpenIddict.Client; using OpenIddict.Sandbox.AspNetCore.Client.Models; using Quartz; using static OpenIddict.Abstractions.OpenIddictConstants; namespace OpenIddict.Sandbox.AspNetCore.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 Microsoft SQL Server. options.UseSqlite($"Filename={Path.Combine(Path.GetTempPath(), "openiddict-sandbox-aspnetcore-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.UseMicrosoftDependencyInjectionJobFactory(); 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:44395/", UriKind.Absolute), ProviderName = "Local", ProviderDisplayName = "Local OIDC server", ClientId = "mvc", Scopes = { Scopes.Email, Scopes.Profile, Scopes.OfflineAccess, "demo_api" }, RedirectUri = new Uri("callback/login/local", UriKind.Relative), PostLogoutRedirectUri = new Uri("callback/logout/local", UriKind.Relative), #if SUPPORTS_PEM_ENCODED_KEY_IMPORT // On supported platforms, this application authenticates by generating JWT client // assertions that are signed using a signing key instead of using a client secret. // // As such, no client secret is set, but an ECDSA key is registered and used by // the OpenIddict client to automatically generate client assertions when needed. // // Note: while the server only needs access to the public key, the client needs // to know the private key to be able to generate and sign the client assertions. SigningCredentials = { new SigningCredentials(GetECDsaSigningKey($""" -----BEGIN EC PRIVATE KEY----- MHcCAQEEIMGxf/eMzKuW2F8KKWPJo3bwlrO68rK5+xCeO1atwja2oAoGCCqGSM49 AwEHoUQDQgAEI23kaVsRRAWIez/pqEZOByJFmlXda6iSQ4QqcH23Ir8aYPPX5lsV nBsExNsl7SOYOiIhgTaX6+PTS7yxTnmvSw== -----END EC PRIVATE KEY----- """), SecurityAlgorithms.EcdsaSha256, SecurityAlgorithms.Sha256) } #else ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654" #endif }); // 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"); }) .AddGoogle(options => { options.SetClientId("1016114395689-kgtgq2p6dj27d7v6e2kjkoj54dgrrckh.apps.googleusercontent.com") .SetClientSecret("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") .SetRedirectUri("callback/login/google") .SetAccessType("offline") .AddScopes(Scopes.Profile); }) .AddReddit(options => { options.SetClientId("vDLNqhrkwrvqHgnoBWF3og") .SetClientSecret("Tpab28Dz0upyZLqn7AN3GFD1O-zaAw") .SetRedirectUri("callback/login/reddit") .SetDuration("permanent"); }); #if SUPPORTS_PEM_ENCODED_KEY_IMPORT static ECDsaSecurityKey GetECDsaSigningKey(ReadOnlySpan key) { var algorithm = ECDsa.Create(); algorithm.ImportFromPem(key); return new ECDsaSecurityKey(algorithm); } #endif }); services.AddHttpClient(); services.AddMvc(); // Register the worker responsible for creating the database used to store tokens. // Note: in a real world application, this step should be part of a setup script. services.AddHostedService(); } public void Configure(IApplicationBuilder app) { app.UseDeveloperExceptionPage(); app.UseStaticFiles(); app.UseStatusCodePagesWithReExecute("/error"); #if SUPPORTS_ENDPOINT_ROUTING app.UseRouting(); #endif app.UseAuthentication(); #if SUPPORTS_AUTHORIZATION_MIDDLEWARE app.UseAuthorization(); #endif #if SUPPORTS_ENDPOINT_ROUTING app.UseEndpoints(options => { options.MapControllers(); options.MapDefaultControllerRoute(); }); #else app.UseMvcWithDefaultRoute(); #endif } }