You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
269 lines
12 KiB
269 lines
12 KiB
using System;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.AspNetCore.Builder;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Identity;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Mvc.Server.Models;
|
|
using Mvc.Server.Services;
|
|
using OpenIddict.Abstractions;
|
|
using OpenIddict.Core;
|
|
using OpenIddict.EntityFrameworkCore.Models;
|
|
using static OpenIddict.Abstractions.OpenIddictConstants;
|
|
|
|
namespace Mvc.Server
|
|
{
|
|
public class Startup
|
|
{
|
|
public Startup(IConfiguration configuration)
|
|
=> Configuration = configuration;
|
|
|
|
public IConfiguration Configuration { get; }
|
|
|
|
public void ConfigureServices(IServiceCollection services)
|
|
{
|
|
services.AddMvc();
|
|
|
|
services.AddDbContext<ApplicationDbContext>(options =>
|
|
{
|
|
// Configure the context to use Microsoft SQL Server.
|
|
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
|
|
|
|
// 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<ApplicationUser, IdentityRole>()
|
|
.AddEntityFrameworkStores<ApplicationDbContext>()
|
|
.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<IdentityOptions>(options =>
|
|
{
|
|
options.ClaimsIdentity.UserNameClaimType = Claims.Name;
|
|
options.ClaimsIdentity.UserIdClaimType = Claims.Subject;
|
|
options.ClaimsIdentity.RoleClaimType = Claims.Role;
|
|
});
|
|
|
|
services.AddOpenIddict()
|
|
|
|
// Register the OpenIddict core components.
|
|
.AddCore(options =>
|
|
{
|
|
// Configure OpenIddict to use the Entity Framework Core stores and models.
|
|
options.UseEntityFrameworkCore()
|
|
.UseDbContext<ApplicationDbContext>();
|
|
})
|
|
|
|
// Register the OpenIddict server components.
|
|
.AddServer(options =>
|
|
{
|
|
// Enable the authorization, logout, token and userinfo endpoints.
|
|
options.SetAuthorizationEndpointUris("/connect/authorize")
|
|
.SetDeviceEndpointUris("/connect/device")
|
|
.SetLogoutEndpointUris("/connect/logout")
|
|
.SetTokenEndpointUris("/connect/token")
|
|
.SetUserinfoEndpointUris("/connect/userinfo")
|
|
.SetVerificationEndpointUris("/connect/verify");
|
|
|
|
// 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()
|
|
.AllowDeviceCodeFlow()
|
|
.AllowPasswordFlow()
|
|
.AllowRefreshTokenFlow();
|
|
|
|
// Mark the "email", "profile", "roles" and "demo_api" scopes as supported scopes.
|
|
options.RegisterScopes(Scopes.Email, Scopes.Profile, Scopes.Roles, "demo_api");
|
|
|
|
// 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()
|
|
.EnableStatusCodePagesIntegration()
|
|
.EnableAuthorizationEndpointPassthrough()
|
|
.EnableLogoutEndpointPassthrough()
|
|
.EnableTokenEndpointPassthrough()
|
|
.EnableUserinfoEndpointPassthrough()
|
|
.EnableVerificationEndpointPassthrough()
|
|
.DisableTransportSecurityRequirement(); // During development, you can disable the HTTPS requirement.
|
|
|
|
// Note: if you don't want to specify a client_id when sending
|
|
// a token or revocation request, uncomment the following line:
|
|
//
|
|
// options.AcceptAnonymousClients();
|
|
|
|
// Note: if you want to process authorization and token requests
|
|
// that specify non-registered scopes, uncomment the following line:
|
|
//
|
|
// options.DisableScopeValidation();
|
|
|
|
// Note: if you don't want to use permissions, you can disable
|
|
// permission enforcement by uncommenting the following lines:
|
|
//
|
|
// options.IgnoreEndpointPermissions()
|
|
// .IgnoreGrantTypePermissions()
|
|
// .IgnoreScopePermissions();
|
|
|
|
// Note: when issuing access tokens used by third-party APIs
|
|
// you don't own, you can disable access token encryption:
|
|
//
|
|
// options.DisableAccessTokenEncryption();
|
|
})
|
|
|
|
// Register the OpenIddict validation components.
|
|
.AddValidation(options =>
|
|
{
|
|
// Configure the audience accepted by this resource server.
|
|
// The value MUST match the audience associated with the
|
|
// "demo_api" scope, which is used by ResourceController.
|
|
options.AddAudiences("resource_server");
|
|
|
|
// Import the configuration from the local OpenIddict server instance.
|
|
options.UseLocalServer();
|
|
|
|
// Register the ASP.NET Core host.
|
|
options.UseAspNetCore();
|
|
});
|
|
|
|
services.AddTransient<IEmailSender, AuthMessageSender>();
|
|
services.AddTransient<ISmsSender, AuthMessageSender>();
|
|
}
|
|
|
|
public void Configure(IApplicationBuilder app)
|
|
{
|
|
app.UseDeveloperExceptionPage();
|
|
|
|
app.UseStaticFiles();
|
|
|
|
app.UseStatusCodePagesWithReExecute("/error");
|
|
|
|
// Note: ASP.NET Core is impacted by a bug that prevents the status code pages
|
|
// from working correctly with endpoint routing. For more information, visit
|
|
// https://github.com/aspnet/AspNetCore/issues/13715#issuecomment-528929683.
|
|
app.Use((context, next) =>
|
|
{
|
|
context.SetEndpoint(null);
|
|
|
|
return next();
|
|
});
|
|
|
|
app.UseRouting();
|
|
|
|
app.UseAuthentication();
|
|
|
|
app.UseAuthorization();
|
|
|
|
app.UseEndpoints(options => options.MapControllerRoute(
|
|
name: "default",
|
|
pattern: "{controller=Home}/{action=Index}/{id?}"));
|
|
|
|
// 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).GetAwaiter().GetResult();
|
|
}
|
|
|
|
private async Task InitializeAsync(IServiceProvider services)
|
|
{
|
|
// Create a new service scope to ensure the database context is correctly disposed when this methods returns.
|
|
using var scope = services.GetRequiredService<IServiceScopeFactory>().CreateScope();
|
|
|
|
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
|
await context.Database.EnsureCreatedAsync();
|
|
|
|
await RegisterApplicationsAsync(scope.ServiceProvider);
|
|
await RegisterScopesAsync(scope.ServiceProvider);
|
|
|
|
static async Task RegisterApplicationsAsync(IServiceProvider provider)
|
|
{
|
|
var manager = provider.GetRequiredService<OpenIddictApplicationManager<OpenIddictApplication>>();
|
|
|
|
if (await manager.FindByClientIdAsync("mvc") == null)
|
|
{
|
|
await manager.CreateAsync(new OpenIddictApplicationDescriptor
|
|
{
|
|
ClientId = "mvc",
|
|
ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654",
|
|
ConsentType = ConsentTypes.Explicit,
|
|
DisplayName = "MVC client application",
|
|
PostLogoutRedirectUris = { new Uri("http://localhost:53507/signout-callback-oidc") },
|
|
RedirectUris = { new Uri("http://localhost:53507/signin-oidc") },
|
|
Permissions =
|
|
{
|
|
Permissions.Endpoints.Authorization,
|
|
Permissions.Endpoints.Logout,
|
|
Permissions.Endpoints.Token,
|
|
Permissions.GrantTypes.AuthorizationCode,
|
|
Permissions.GrantTypes.RefreshToken,
|
|
Permissions.Scopes.Email,
|
|
Permissions.Scopes.Profile,
|
|
Permissions.Scopes.Roles,
|
|
Permissions.Prefixes.Scope + "demo_api"
|
|
},
|
|
Requirements =
|
|
{
|
|
Requirements.Features.ProofKeyForCodeExchange
|
|
}
|
|
});
|
|
}
|
|
|
|
// 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") == null)
|
|
{
|
|
await manager.CreateAsync(new OpenIddictApplicationDescriptor
|
|
{
|
|
ClientId = "postman",
|
|
ConsentType = ConsentTypes.Systematic,
|
|
DisplayName = "Postman",
|
|
RedirectUris = { new Uri("urn:postman") },
|
|
Permissions =
|
|
{
|
|
Permissions.Endpoints.Authorization,
|
|
Permissions.Endpoints.Device,
|
|
Permissions.Endpoints.Token,
|
|
Permissions.GrantTypes.AuthorizationCode,
|
|
Permissions.GrantTypes.DeviceCode,
|
|
Permissions.GrantTypes.Password,
|
|
Permissions.GrantTypes.RefreshToken,
|
|
Permissions.Scopes.Email,
|
|
Permissions.Scopes.Profile,
|
|
Permissions.Scopes.Roles
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
static async Task RegisterScopesAsync(IServiceProvider provider)
|
|
{
|
|
var manager = provider.GetRequiredService<OpenIddictScopeManager<OpenIddictScope>>();
|
|
|
|
if (await manager.FindByNameAsync("demo_api") == null)
|
|
{
|
|
await manager.CreateAsync(new OpenIddictScopeDescriptor
|
|
{
|
|
DisplayName = "Demo API access",
|
|
Name = "demo_api",
|
|
Resources = { "resource_server" }
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|