using System; using System.Threading; using System.Threading.Tasks; using AspNet.Security.OpenIdConnect.Primitives; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Mvc.Server.Extensions; using Mvc.Server.Models; using Mvc.Server.Services; using OpenIddict.Core; using OpenIddict.Models; namespace Mvc.Server { public class Startup { public void ConfigureServices(IServiceCollection services) { var configuration = new ConfigurationBuilder() .AddJsonFile("config.json") .AddEnvironmentVariables() .Build(); services.AddMvc(); services.AddDbContext(options => { // Configure the context to use Microsoft SQL Server. options.UseSqlServer(configuration["Data:DefaultConnection:ConnectionString"]); // 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(); // Configure Identity to use the same JWT claims as OpenIddict instead // of the legacy WS-Federation claims it uses by default (ClaimTypes), // which saves you from doing the mapping in your authorization controller. services.Configure(options => { options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name; options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject; options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role; }); // Register the OpenIddict services. services.AddOpenIddict(options => { // Register the Entity Framework stores. options.AddEntityFrameworkCoreStores(); // Register the ASP.NET Core MVC binder used by OpenIddict. // Note: if you don't call this method, you won't be able to // bind OpenIdConnectRequest or OpenIdConnectResponse parameters. options.AddMvcBinders(); // Enable the authorization, logout, token and userinfo endpoints. options.EnableAuthorizationEndpoint("/connect/authorize") .EnableLogoutEndpoint("/connect/logout") .EnableTokenEndpoint("/connect/token") .EnableUserinfoEndpoint("/api/userinfo"); // Note: the Mvc.Client sample only uses the code flow and the password flow, but you // can enable the other flows if you need to support implicit or client credentials. options.AllowAuthorizationCodeFlow() .AllowPasswordFlow() .AllowRefreshTokenFlow(); // Mark the "profile" scope as a supported scope in the discovery document. options.RegisterScopes(OpenIdConnectConstants.Scopes.Profile); // Make the "client_id" parameter mandatory when sending a token request. options.RequireClientIdentification(); // When request caching is enabled, authorization and logout requests // are stored in the distributed cache by OpenIddict and the user agent // is redirected to the same page with a single parameter (request_id). // This allows flowing large OpenID Connect requests even when using // an external authentication provider like Google, Facebook or Twitter. options.EnableRequestCaching(); // During development, you can disable the HTTPS requirement. options.DisableHttpsRequirement(); // Note: to use JWT access tokens instead of the default // encrypted format, the following lines are required: // // options.UseJsonWebTokens(); // options.AddEphemeralSigningKey(); }); services.AddTransient(); services.AddTransient(); } public void Configure(IApplicationBuilder app) { app.UseDeveloperExceptionPage(); app.UseStaticFiles(); app.UseWhen(context => context.Request.Path.StartsWithSegments("/api"), branch => { // Add a middleware used to validate access // tokens and protect the API endpoints. branch.UseOAuthValidation(); // If you prefer using JWT, don't forget to disable the automatic // JWT -> WS-Federation claims mapping used by the JWT middleware: // // JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); // JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear(); // // branch.UseJwtBearerAuthentication(new JwtBearerOptions // { // Authority = "http://localhost:54540/", // Audience = "resource_server", // RequireHttpsMetadata = false, // TokenValidationParameters = new TokenValidationParameters // { // NameClaimType = OpenIdConnectConstants.Claims.Subject, // RoleClaimType = OpenIdConnectConstants.Claims.Role // } // }); // Alternatively, you can also use the introspection middleware. // Using it is recommended if your resource server is in a // different application/separated from the authorization server. // // branch.UseOAuthIntrospection(options => // { // options.Authority = new Uri("http://localhost:54540/"); // options.Audiences.Add("resource_server"); // options.ClientId = "resource_server"; // options.ClientSecret = "875sqd4s5d748z78z7ds1ff8zz8814ff88ed8ea4z4zzd"; // options.RequireHttpsMetadata = false; // }); }); app.UseWhen(context => !context.Request.Path.StartsWithSegments("/api"), branch => { branch.UseStatusCodePagesWithReExecute("/error"); branch.UseIdentity(); branch.UseGoogleAuthentication(new GoogleOptions { ClientId = "560027070069-37ldt4kfuohhu3m495hk2j4pjp92d382.apps.googleusercontent.com", ClientSecret = "n2Q-GEw9RQjzcRbU3qhfTj8f" }); branch.UseTwitterAuthentication(new TwitterOptions { ConsumerKey = "6XaCTaLbMqfj6ww3zvZ5g", ConsumerSecret = "Il2eFzGIrYhz6BWjYhVXBPQSfZuS4xoHpSSyD9PI" }); }); app.UseOpenIddict(); app.UseMvcWithDefaultRoute(); // Seed the database with the sample applications. // Note: in a real world application, this step should be part of a setup script. InitializeAsync(app.ApplicationServices, CancellationToken.None).GetAwaiter().GetResult(); } private async Task InitializeAsync(IServiceProvider services, CancellationToken cancellationToken) { // Create a new service scope to ensure the database context is correctly disposed when this methods returns. using (var scope = services.GetRequiredService().CreateScope()) { var context = scope.ServiceProvider.GetRequiredService(); await context.Database.EnsureCreatedAsync(); var manager = scope.ServiceProvider.GetRequiredService>(); if (await manager.FindByClientIdAsync("mvc", cancellationToken) == null) { var descriptor = new OpenIddictApplicationDescriptor { ClientId = "mvc", ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654", DisplayName = "MVC client application", PostLogoutRedirectUris = { new Uri("http://localhost:53507/") }, RedirectUris = { new Uri("http://localhost:53507/signin-oidc") } }; await manager.CreateAsync(descriptor, cancellationToken); } // To test this sample with Postman, use the following settings: // // * Authorization URL: http://localhost:54540/connect/authorize // * Access token URL: http://localhost:54540/connect/token // * Client ID: postman // * Client secret: [blank] (not used with public clients) // * Scope: openid email profile roles // * Grant type: authorization code // * Request access token locally: yes if (await manager.FindByClientIdAsync("postman", cancellationToken) == null) { var descriptor = new OpenIddictApplicationDescriptor { ClientId = "postman", DisplayName = "Postman", RedirectUris = { new Uri("https://www.getpostman.com/oauth2/callback") } }; await manager.CreateAsync(descriptor, cancellationToken); } } } } }