Versatile OpenID Connect stack for ASP.NET Core and Microsoft.Owin (compatible with ASP.NET 4.6.1)
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.
 
 
 
 
 
 

220 lines
9.8 KiB

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<ApplicationDbContext>(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<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 = 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<ApplicationDbContext>();
// 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();
// 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<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
}
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<IServiceScopeFactory>().CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
await context.Database.EnsureCreatedAsync();
var manager = scope.ServiceProvider.GetRequiredService<OpenIddictApplicationManager<OpenIddictApplication>>();
if (await manager.FindByClientIdAsync("mvc", cancellationToken) == null)
{
var application = new OpenIddictApplication
{
ClientId = "mvc",
DisplayName = "MVC client application",
LogoutRedirectUri = "http://localhost:53507/",
RedirectUri = "http://localhost:53507/signin-oidc"
};
await manager.CreateAsync(application, "901564A5-E7FE-42CB-B10D-61EF6A8F3654", 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 application = new OpenIddictApplication
{
ClientId = "postman",
DisplayName = "Postman",
RedirectUri = "https://www.getpostman.com/oauth2/callback"
};
await manager.CreateAsync(application, cancellationToken);
}
}
}
}
}