Browse Source

Rename OpenIddictBuilder to OpenIddictServices and introduce a new OpenIddictBuilder to make options easier to configure

pull/24/head
Kévin Chalet 10 years ago
parent
commit
1ee94270bc
  1. 4
      samples/Mvc.Server/Startup.cs
  2. 45
      src/OpenIddict.Core/OpenIddictBuilder.cs
  3. 9
      src/OpenIddict.Core/OpenIddictController.cs
  4. 122
      src/OpenIddict.Core/OpenIddictExtensions.cs
  5. 77
      src/OpenIddict.Core/OpenIddictOptions.cs
  6. 36
      src/OpenIddict.Core/OpenIddictServices.cs
  7. 28
      src/OpenIddict.EF/OpenIddictExtensions.cs
  8. 5
      src/OpenIddict/OpenIddictExtensions.cs

4
samples/Mvc.Server/Startup.cs

@ -76,9 +76,7 @@ namespace Mvc.Server {
// Note: OpenIddict must be added after
// ASP.NET Identity and the external providers.
app.UseOpenIddict(options => {
options.AllowInsecureHttp = true;
});
app.UseOpenIddict();
app.UseMvcWithDefaultRoute();

45
src/OpenIddict.Core/OpenIddictBuilder.cs

@ -1,36 +1,21 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/core for more information concerning
* the license and the contributors participating to this project.
*/
using OpenIddict;
using System;
using Microsoft.Extensions.DependencyInjection;
namespace OpenIddict {
public class OpenIddictBuilder {
internal OpenIddictBuilder(IServiceCollection services) {
Services = services;
namespace Microsoft.AspNet.Builder {
/// <summary>
/// Holds various properties allowing to configure OpenIddct.
/// </summary>
public class OpenIddictBuilder : OpenIdConnectServerBuilder {
public OpenIddictBuilder(IApplicationBuilder builder)
: base(builder) {
Options = new OpenIddictOptions();
}
/// <summary>
/// Gets or sets the type corresponding to the Application entity.
/// </summary>
public Type ApplicationType { get; set; }
/// <summary>
/// Gets or sets the type corresponding to the Role entity.
/// Gets or sets the options used by OpenIddict.
/// </summary>
public Type RoleType { get; set; }
/// <summary>
/// Gets or sets the type corresponding to the User entity.
/// </summary>
public Type UserType { get; set; }
/// <summary>
/// Gets the services used by OpenIddict.
/// </summary>
public IServiceCollection Services { get; }
public new OpenIddictOptions Options {
get { return base.Options as OpenIddictOptions; }
set { base.Options = value; }
}
}
}
}

9
src/OpenIddict.Core/OpenIddictController.cs

@ -10,8 +10,8 @@ using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Server;
using Microsoft.AspNet.Authorization;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http.Authentication;
using Microsoft.AspNet.Mvc;
using Microsoft.Extensions.Internal;
@ -20,8 +20,11 @@ using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace OpenIddict {
// Note: this controller is generic and doesn't need to be marked as internal to prevent MVC from discovering it.
public class OpenIddictController<TUser, TApplication> : Controller where TUser : class where TApplication : class {
public OpenIddictController([NotNull] OpenIddictManager<TUser, TApplication> manager) {
public OpenIddictController(
[NotNull] OpenIddictManager<TUser, TApplication> manager,
[NotNull] OpenIddictOptions options) {
Manager = manager;
Options = options;
}
/// <summary>
@ -32,7 +35,7 @@ namespace OpenIddict {
/// <summary>
/// Gets the OpenIddict options used by the server.
/// </summary>
protected virtual OpenIddictOptions Options => (OpenIddictOptions) HttpContext.Items[typeof(OpenIddictOptions)];
protected virtual OpenIddictOptions Options { get; }
[HttpGet, HttpPost]
public async Task<IActionResult> Authorize() {

122
src/OpenIddict.Core/OpenIddictExtensions.cs

@ -9,6 +9,7 @@ using System.Diagnostics;
using System.Reflection;
using AspNet.Security.OpenIdConnect.Server;
using Microsoft.AspNet.FileProviders;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.StaticFiles;
@ -22,8 +23,8 @@ using NWebsec.Owin;
namespace Microsoft.AspNet.Builder {
public static class OpenIddictExtensions {
public static OpenIddictBuilder AddOpenIddictCore<TApplication>(
[NotNull] this IdentityBuilder builder) where TApplication : class {
public static OpenIddictServices AddOpenIddictCore<TApplication>([NotNull] this IdentityBuilder builder)
where TApplication : class {
builder.Services.AddAuthentication();
builder.Services.AddCaching();
@ -36,7 +37,7 @@ namespace Microsoft.AspNet.Builder {
typeof(OpenIddictManager<,>).MakeGenericType(
builder.UserType, typeof(TApplication)));
var services = new OpenIddictBuilder(builder.Services) {
var services = new OpenIddictServices(builder.Services) {
ApplicationType = typeof(TApplication),
RoleType = builder.RoleType,
UserType = builder.UserType
@ -47,19 +48,22 @@ namespace Microsoft.AspNet.Builder {
return services;
}
public static IApplicationBuilder UseOpenIddict([NotNull] this IApplicationBuilder app) {
return app.UseOpenIddict(options => { });
}
public static IApplicationBuilder UseOpenIddict(
[NotNull] this IApplicationBuilder app,
[NotNull] Action<OpenIddictOptions> configuration) {
var instance = new OpenIddictOptions();
[NotNull] Action<OpenIddictBuilder> configuration) {
var builder = new OpenIddictBuilder(app);
// Turn ApplicationCanDisplayErrors on to ensure ASP.NET MVC 6
// handles errored requests and returns an appropriate error page.
instance.ApplicationCanDisplayErrors = true;
// By default, enable AllowInsecureHttp in development/testing environments.
var environment = app.ApplicationServices.GetRequiredService<IHostingEnvironment>();
builder.Options.AllowInsecureHttp = environment.IsDevelopment() || environment.IsEnvironment("Testing");
// Call the configuration delegate defined by the user.
configuration(instance);
configuration(builder);
if (!instance.UseCustomViews) {
if (!builder.Options.UseCustomViews) {
app.UseStaticFiles(new StaticFileOptions {
FileProvider = new EmbeddedFileProvider(
assembly: Assembly.Load(new AssemblyName("OpenIddict.Assets")),
@ -76,24 +80,8 @@ namespace Microsoft.AspNet.Builder {
// Add OpenIdConnectServerMiddleware to the ASP.NET 5 pipeline.
app.UseOpenIdConnectServer(options => {
// Resolve the OpenIddict provider from the global services container.
options.Options = builder.Options;
options.Provider = app.ApplicationServices.GetRequiredService<OpenIdConnectServerProvider>();
// Copy the OpenIddict options to the ASOS configuration.
options.Options.AuthenticationScheme = instance.AuthenticationScheme;
options.Options.Issuer = instance.Issuer;
options.Options.AuthorizationEndpointPath = instance.AuthorizationEndpointPath;
options.Options.LogoutEndpointPath = instance.LogoutEndpointPath;
options.Options.AccessTokenLifetime = instance.AccessTokenLifetime;
options.Options.AuthorizationCodeLifetime = instance.AuthorizationCodeLifetime;
options.Options.IdentityTokenLifetime = instance.IdentityTokenLifetime;
options.Options.RefreshTokenLifetime = instance.RefreshTokenLifetime;
options.Options.ApplicationCanDisplayErrors = instance.ApplicationCanDisplayErrors;
options.Options.AllowInsecureHttp = instance.AllowInsecureHttp;
});
#if DNX451
@ -120,57 +108,45 @@ namespace Microsoft.AspNet.Builder {
#endif
// Run the rest of the pipeline in an isolated environment.
return app.Isolate(builder => {
// Add the options to the ASP.NET context
// before executing the rest of the pipeline.
builder.Use(next => context => {
context.Items[typeof(OpenIddictOptions)] = instance;
return app.Isolate(container => container.UseMvc(routes => {
// Register the actions corresponding to the authorization endpoint.
if (builder.Options.AuthorizationEndpointPath.HasValue) {
routes.MapRoute("{D97891B4}", builder.Options.AuthorizationEndpointPath.Value.Substring(1), new {
controller = typeof(OpenIddictController<,>).Name,
action = nameof(OpenIddictController<dynamic, dynamic>.Authorize)
});
return next(context);
});
routes.MapRoute("{7148DB83}", builder.Options.AuthorizationEndpointPath.Value.Substring(1) + "/accept", new {
controller = typeof(OpenIddictController<,>).Name,
action = nameof(OpenIddictController<dynamic, dynamic>.Accept)
});
// Register ASP.NET MVC 6 and the actions
// associated to the OpenIddict controller.
builder.UseMvc(routes => {
// Register the actions corresponding to the authorization endpoint.
if (instance.AuthorizationEndpointPath.HasValue) {
routes.MapRoute("{D97891B4}", instance.AuthorizationEndpointPath.Value.Substring(1), new {
controller = typeof(OpenIddictController<,>).Name,
action = nameof(OpenIddictController<dynamic, dynamic>.Authorize)
});
routes.MapRoute("{7148DB83}", instance.AuthorizationEndpointPath.Value.Substring(1) + "/accept", new {
controller = typeof(OpenIddictController<,>).Name,
action = nameof(OpenIddictController<dynamic, dynamic>.Accept)
});
routes.MapRoute("{23438BCC}", instance.AuthorizationEndpointPath.Value.Substring(1) + "/deny", new {
controller = typeof(OpenIddictController<,>).Name,
action = nameof(OpenIddictController<dynamic, dynamic>.Deny)
});
}
// Register the action corresponding to the logout endpoint.
if (instance.LogoutEndpointPath.HasValue) {
routes.MapRoute("{C7DB102A}", instance.LogoutEndpointPath.Value.Substring(1), new {
controller = typeof(OpenIddictController<,>).Name,
action = nameof(OpenIddictController<dynamic, dynamic>.Logout)
});
}
});
}, services => {
var builder = app.ApplicationServices.GetRequiredService<OpenIddictBuilder>();
routes.MapRoute("{23438BCC}", builder.Options.AuthorizationEndpointPath.Value.Substring(1) + "/deny", new {
controller = typeof(OpenIddictController<,>).Name,
action = nameof(OpenIddictController<dynamic, dynamic>.Deny)
});
}
// Register the action corresponding to the logout endpoint.
if (builder.Options.LogoutEndpointPath.HasValue) {
routes.MapRoute("{C7DB102A}", builder.Options.LogoutEndpointPath.Value.Substring(1), new {
controller = typeof(OpenIddictController<,>).Name,
action = nameof(OpenIddictController<dynamic, dynamic>.Logout)
});
}
}), services => {
var instance = app.ApplicationServices.GetRequiredService<OpenIddictServices>();
services.AddMvc()
// Register the OpenIddict controller.
.AddControllersAsServices(new[] {
typeof(OpenIddictController<,>).MakeGenericType(builder.UserType, builder.ApplicationType)
typeof(OpenIddictController<,>).MakeGenericType(instance.UserType, instance.ApplicationType)
})
// Update the Razor options to use an embedded provider
// extracting its views from the current assembly.
.AddRazorOptions(options => {
if (!instance.UseCustomViews) {
if (!builder.Options.UseCustomViews) {
options.FileProvider = new EmbeddedFileProvider(
assembly: typeof(OpenIddictOptions).GetTypeInfo().Assembly,
baseNamespace: "OpenIddict.Core");
@ -178,24 +154,26 @@ namespace Microsoft.AspNet.Builder {
});
// Register the sign-in manager in the isolated container.
services.AddScoped(typeof(SignInManager<>).MakeGenericType(builder.UserType), provider => {
services.AddScoped(typeof(SignInManager<>).MakeGenericType(instance.UserType), provider => {
var accessor = provider.GetRequiredService<IHttpContextAccessor>();
var container = (IServiceProvider) accessor.HttpContext.Items[typeof(IServiceProvider)];
Debug.Assert(container != null);
// Resolve the sign-in manager from the parent container.
return container.GetRequiredService(typeof(SignInManager<>).MakeGenericType(builder.UserType));
return container.GetRequiredService(typeof(SignInManager<>).MakeGenericType(instance.UserType));
});
// Register the user manager in the isolated container.
services.AddScoped(typeof(OpenIddictManager<,>).MakeGenericType(builder.UserType, builder.ApplicationType), provider => {
services.AddScoped(typeof(OpenIddictManager<,>).MakeGenericType(instance.UserType, instance.ApplicationType), provider => {
var accessor = provider.GetRequiredService<IHttpContextAccessor>();
var container = (IServiceProvider) accessor.HttpContext.Items[typeof(IServiceProvider)];
Debug.Assert(container != null);
// Resolve the user manager from the parent container.
return container.GetRequiredService(typeof(OpenIddictManager<,>).MakeGenericType(builder.UserType, builder.ApplicationType));
return container.GetRequiredService(typeof(OpenIddictManager<,>).MakeGenericType(instance.UserType, instance.ApplicationType));
});
services.AddScoped(provider => builder.Options);
});
}
}

77
src/OpenIddict.Core/OpenIddictOptions.cs

@ -4,81 +4,14 @@
* the license and the contributors participating to this project.
*/
using System;
using AspNet.Security.OpenIdConnect.Server;
using Microsoft.AspNet.Http;
namespace OpenIddict {
public class OpenIddictOptions {
public string AuthenticationScheme { get; set; } = OpenIddictDefaults.AuthenticationScheme;
/// <summary>
/// The base address used to uniquely identify the authorization server.
/// The URI must be absolute and may contain a path, but no query string or fragment part.
/// Unless AllowInsecureHttp has been set to true, an HTTPS address must be provided.
/// </summary>
public Uri Issuer { get; set; }
/// <summary>
/// The request path where client applications will redirect the user-agent in order to
/// obtain user consent to issue a token. Must begin with a leading slash, like "/connect/authorize".
/// This setting can be set to <see cref="PathString.Empty"/> to disable the authorization endpoint.
/// </summary>
public PathString AuthorizationEndpointPath { get; set; } = new PathString(OpenIdConnectServerDefaults.AuthorizationEndpointPath);
/// <summary>
/// The request path client applications communicate with to log out.
/// Must begin with a leading slash, like "/connect/logout".
/// You can set it to <see cref="PathString.Empty"/> to disable the logout endpoint.
/// </summary>
public PathString LogoutEndpointPath { get; set; } = new PathString(OpenIdConnectServerDefaults.LogoutEndpointPath);
/// <summary>
/// The period of time the authorization code remains valid after being issued. The default is 5 minutes.
/// This time span must also take into account clock synchronization between servers in a web farm, so a very
/// brief value could result in unexpectedly expired tokens.
/// </summary>
public TimeSpan AuthorizationCodeLifetime { get; set; } = TimeSpan.FromMinutes(5);
/// <summary>
/// The period of time the access token remains valid after being issued. The default is 1 hour.
/// The client application is expected to refresh or acquire a new access token after the token has expired.
/// </summary>
public TimeSpan AccessTokenLifetime { get; set; } = TimeSpan.FromHours(1);
/// <summary>
/// The period of time the identity token remains valid after being issued. The default is 20 minutes.
/// The client application is expected to refresh or acquire a new identity token after the token has expired.
/// </summary>
public TimeSpan IdentityTokenLifetime { get; set; } = TimeSpan.FromMinutes(20);
/// <summary>
/// The period of time the refresh token remains valid after being issued. The default is 6 hours.
/// The client application is expected to start a whole new authentication flow after the refresh token has expired.
/// </summary>
public TimeSpan RefreshTokenLifetime { get; set; } = TimeSpan.FromHours(6);
/// <summary>
/// Determines whether refresh tokens issued during a grant_type=refresh_token request should be generated
/// with a new expiration date or should re-use the same expiration date as the original refresh token.
/// Set this property to <c>true</c> to assign a new expiration date each time a refresh token is issued,
/// <c>false</c> to use the expiration date of the original refresh token. When set to <c>false</c>,
/// access and identity tokens' lifetime cannot exceed the expiration date of the refresh token.
/// </summary>
public bool UseSlidingExpiration { get; set; } = true;
/// <summary>
/// Set to true if the web application is able to render error messages on the authorization endpoint. This is only needed for cases where
/// the browser is not redirected back to the client application, for example, when the client_id or redirect_uri are incorrect. The
/// authorization endpoint should expect to see the OpenID Connect response added to the ASP.NET 5 environment.
/// </summary>
public bool ApplicationCanDisplayErrors { get; set; }
/// <summary>
/// True to allow incoming requests to arrive on HTTP and to allow redirect_uri parameters to have HTTP URI addresses.
/// Setting this option to false in production is strongly encouraged to mitigate man-in-the-middle attacks.
/// </summary>
public bool AllowInsecureHttp { get; set; }
public class OpenIddictOptions : OpenIdConnectServerOptions {
public OpenIddictOptions() {
AuthenticationScheme = OpenIddictDefaults.AuthenticationScheme;
ApplicationCanDisplayErrors = true;
}
/// <summary>
/// Set to <c>true</c> to allow you to use your own views/styles/scripts in your server.

36
src/OpenIddict.Core/OpenIddictServices.cs

@ -0,0 +1,36 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using Microsoft.Extensions.DependencyInjection;
namespace OpenIddict {
public class OpenIddictServices {
public OpenIddictServices(IServiceCollection services) {
Services = services;
}
/// <summary>
/// Gets or sets the type corresponding to the Application entity.
/// </summary>
public Type ApplicationType { get; set; }
/// <summary>
/// Gets or sets the type corresponding to the Role entity.
/// </summary>
public Type RoleType { get; set; }
/// <summary>
/// Gets or sets the type corresponding to the User entity.
/// </summary>
public Type UserType { get; set; }
/// <summary>
/// Gets the services used by OpenIddict.
/// </summary>
public IServiceCollection Services { get; }
}
}

28
src/OpenIddict.EF/OpenIddictExtensions.cs

@ -16,21 +16,21 @@ using OpenIddict;
namespace Microsoft.AspNet.Builder {
public static class OpenIddictExtensions {
public static OpenIddictBuilder AddEntityFrameworkStore([NotNull] this OpenIddictBuilder builder) {
builder.Services.AddScoped(
typeof(IOpenIddictStore<,>).MakeGenericType(builder.UserType, builder.ApplicationType),
public static OpenIddictServices AddEntityFrameworkStore([NotNull] this OpenIddictServices services) {
services.Services.AddScoped(
typeof(IOpenIddictStore<,>).MakeGenericType(services.UserType, services.ApplicationType),
typeof(OpenIddictStore<,,,,>).MakeGenericType(
/* TUser: */ builder.UserType,
/* TApplication: */ builder.ApplicationType,
/* TRole: */ builder.RoleType,
/* TContext: */ ResolveContextType(builder),
/* TKey: */ ResolveKeyType(builder)));
/* TUser: */ services.UserType,
/* TApplication: */ services.ApplicationType,
/* TRole: */ services.RoleType,
/* TContext: */ ResolveContextType(services),
/* TKey: */ ResolveKeyType(services)));
return builder;
return services;
}
private static Type ResolveContextType([NotNull] OpenIddictBuilder builder) {
var service = (from registration in builder.Services
private static Type ResolveContextType([NotNull] OpenIddictServices services) {
var service = (from registration in services.Services
where registration.ServiceType.IsConstructedGenericType
let definition = registration.ServiceType.GetGenericTypeDefinition()
where definition == typeof(IUserStore<>)
@ -65,9 +65,9 @@ namespace Microsoft.AspNet.Builder {
throw new InvalidOperationException("The type of the database context cannot be automatically inferred.");
}
private static Type ResolveKeyType([NotNull] OpenIddictBuilder builder) {
private static Type ResolveKeyType([NotNull] OpenIddictServices services) {
TypeInfo type;
for (type = builder.UserType.GetTypeInfo(); type != null; type = type.BaseType?.GetTypeInfo()) {
for (type = services.UserType.GetTypeInfo(); type != null; type = type.BaseType?.GetTypeInfo()) {
if (!type.IsGenericType) {
continue;
}
@ -86,7 +86,7 @@ namespace Microsoft.AspNet.Builder {
throw new InvalidOperationException(
"The type of the key identifier used by the user " +
$"entity '{builder.UserType}' cannot be automatically inferred.");
$"entity '{services.UserType}' cannot be automatically inferred.");
}
}
}

5
src/OpenIddict/OpenIddictExtensions.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project.
*/
using System;
using Microsoft.AspNet.Identity;
using Microsoft.Extensions.Internal;
using OpenIddict;
@ -11,12 +12,12 @@ using OpenIddict.Models;
namespace Microsoft.AspNet.Builder {
public static class OpenIddictExtensions {
public static OpenIddictBuilder AddOpenIddict([NotNull] this IdentityBuilder builder) {
public static OpenIddictServices AddOpenIddict([NotNull] this IdentityBuilder builder) {
return builder.AddOpenIddictCore<Application>()
.AddEntityFrameworkStore();
}
public static OpenIddictBuilder AddOpenIddict<TApplication>([NotNull] this IdentityBuilder builder)
public static OpenIddictServices AddOpenIddict<TApplication>([NotNull] this IdentityBuilder builder)
where TApplication : Application {
return builder.AddOpenIddictCore<TApplication>()
.AddEntityFrameworkStore();

Loading…
Cancel
Save