Browse Source

Refactor OpenIddict/OpenIddict.Core, introduce new entities and add built-in refresh token revocation

pull/127/head
Kévin Chalet 10 years ago
parent
commit
d0dce5b33a
  1. 9
      OpenIddict.sln
  2. 1
      samples/Mvc.Client/project.json
  3. 4
      samples/Mvc.Server/Models/ApplicationUser.cs
  4. 112
      samples/Mvc.Server/Startup.cs
  5. 6
      samples/Mvc.Server/project.json
  6. 7
      src/OpenIddict.Assets/OpenIddictExtensions.cs
  7. 13
      src/OpenIddict.Core/IOpenIddictStore.cs
  8. 37
      src/OpenIddict.Core/Infrastructure/OpenIddictHelpers.cs
  9. 22
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Authentication.cs
  10. 71
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Exchange.cs
  11. 37
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Introspection.cs
  12. 120
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Revocation.cs
  13. 30
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Serialization.cs
  14. 11
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Session.cs
  15. 7
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Userinfo.cs
  16. 5
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.cs
  17. 41
      src/OpenIddict.Core/Infrastructure/OpenIddictServices.cs
  18. 173
      src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs
  19. 49
      src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
  20. 49
      src/OpenIddict.Core/Managers/OpenIddictScopeManager.cs
  21. 189
      src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs
  22. 426
      src/OpenIddict.Core/OpenIddictBuilder.cs
  23. 36
      src/OpenIddict.Core/OpenIddictConfiguration.cs
  24. 18
      src/OpenIddict.Core/OpenIddictConstants.cs
  25. 317
      src/OpenIddict.Core/OpenIddictExtensions.cs
  26. 68
      src/OpenIddict.Core/OpenIddictHelpers.cs
  27. 195
      src/OpenIddict.Core/OpenIddictManager.cs
  28. 47
      src/OpenIddict.Core/OpenIddictModule.cs
  29. 18
      src/OpenIddict.Core/OpenIddictOptions.cs
  30. 72
      src/OpenIddict.Core/Stores/IOpenIddictApplicationStore.cs
  31. 13
      src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs
  32. 13
      src/OpenIddict.Core/Stores/IOpenIddictScopeStore.cs
  33. 41
      src/OpenIddict.Core/Stores/IOpenIddictTokenStore.cs
  34. 1
      src/OpenIddict.Core/project.json
  35. 39
      src/OpenIddict.EF/OpenIddictContext.cs
  36. 117
      src/OpenIddict.EF/OpenIddictExtensions.cs
  37. 78
      src/OpenIddict.EF/OpenIddictStore.cs
  38. 60
      src/OpenIddict.EntityFramework/Models/OpenIddictApplication.cs
  39. 54
      src/OpenIddict.EntityFramework/Models/OpenIddictAuthorization.cs
  40. 31
      src/OpenIddict.EntityFramework/Models/OpenIddictScope.cs
  41. 48
      src/OpenIddict.EntityFramework/Models/OpenIddictToken.cs
  42. 41
      src/OpenIddict.EntityFramework/Models/OpenIddictUser.cs
  43. 0
      src/OpenIddict.EntityFramework/OpenIddict.EntityFramework.xproj
  44. 152
      src/OpenIddict.EntityFramework/OpenIddictContext.cs
  45. 79
      src/OpenIddict.EntityFramework/OpenIddictExtensions.cs
  46. 140
      src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs
  47. 37
      src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs
  48. 35
      src/OpenIddict.EntityFramework/Stores/OpenIddictScopeStore.cs
  49. 97
      src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs
  50. 3
      src/OpenIddict.EntityFramework/project.json
  51. 20
      src/OpenIddict.Models/Application.cs
  52. 18
      src/OpenIddict.Models/OpenIddict.Models.xproj
  53. 49
      src/OpenIddict.Models/project.json
  54. 51
      src/OpenIddict.Mvc/OpenIddictController.cs
  55. 131
      src/OpenIddict.Mvc/OpenIddictExtensions.cs
  56. 45
      src/OpenIddict.Security/OpenIddictExtensions.cs
  57. 114
      src/OpenIddict/OpenIddictExtensions.cs
  58. 9
      src/OpenIddict/project.json

9
OpenIddict.sln

@ -15,9 +15,7 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Mvc.Server", "samples\Mvc.S
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "OpenIddict.Assets", "src\OpenIddict.Assets\OpenIddict.Assets.xproj", "{86293E11-DD31-4D54-BCAD-8788B5C9972F}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "OpenIddict.Models", "src\OpenIddict.Models\OpenIddict.Models.xproj", "{79AE02C3-2AB4-4495-BEDD-685A714EA51C}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "OpenIddict.EF", "src\OpenIddict.EF\OpenIddict.EF.xproj", "{D2450929-ED0E-420D-B475-327924F9701C}"
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "OpenIddict.EntityFramework", "src\OpenIddict.EntityFramework\OpenIddict.EntityFramework.xproj", "{D2450929-ED0E-420D-B475-327924F9701C}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "OpenIddict.Core", "src\OpenIddict.Core\OpenIddict.Core.xproj", "{E60CF8CA-6313-4359-BE43-AFCBB927EA30}"
EndProject
@ -47,10 +45,6 @@ Global
{86293E11-DD31-4D54-BCAD-8788B5C9972F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{86293E11-DD31-4D54-BCAD-8788B5C9972F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{86293E11-DD31-4D54-BCAD-8788B5C9972F}.Release|Any CPU.Build.0 = Release|Any CPU
{79AE02C3-2AB4-4495-BEDD-685A714EA51C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{79AE02C3-2AB4-4495-BEDD-685A714EA51C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{79AE02C3-2AB4-4495-BEDD-685A714EA51C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{79AE02C3-2AB4-4495-BEDD-685A714EA51C}.Release|Any CPU.Build.0 = Release|Any CPU
{D2450929-ED0E-420D-B475-327924F9701C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D2450929-ED0E-420D-B475-327924F9701C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D2450929-ED0E-420D-B475-327924F9701C}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -76,7 +70,6 @@ Global
{96B22EB9-771A-4DCA-B828-E6EA2774CF1B} = {F47D1283-0EE9-4728-8026-58405C29B786}
{7CBEAFD2-E3D0-4424-9B78-E87AB52327A6} = {F47D1283-0EE9-4728-8026-58405C29B786}
{86293E11-DD31-4D54-BCAD-8788B5C9972F} = {D544447C-D701-46BB-9A5B-C76C612A596B}
{79AE02C3-2AB4-4495-BEDD-685A714EA51C} = {D544447C-D701-46BB-9A5B-C76C612A596B}
{D2450929-ED0E-420D-B475-327924F9701C} = {D544447C-D701-46BB-9A5B-C76C612A596B}
{E60CF8CA-6313-4359-BE43-AFCBB927EA30} = {D544447C-D701-46BB-9A5B-C76C612A596B}
{7AE46E2F-E93B-4FF9-B941-6CD7A3E1BF84} = {D544447C-D701-46BB-9A5B-C76C612A596B}

1
samples/Mvc.Client/project.json

@ -1,6 +1,5 @@
{
"buildOptions": {
"debugType": "portable",
"emitEntryPoint": true,
"warningsAsErrors": true,
"preserveCompilationContext": true,

4
samples/Mvc.Server/Models/ApplicationUser.cs

@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using OpenIddict;
namespace Mvc.Server.Models {
// Add profile data for application users by adding properties to the ApplicationUser class
public class ApplicationUser : IdentityUser { }
public class ApplicationUser : OpenIddictUser { }
}

112
samples/Mvc.Server/Startup.cs

@ -1,9 +1,7 @@
using System.Linq;
using System.Reflection;
using AspNet.Security.OAuth.GitHub;
using CryptoHelper;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
@ -13,7 +11,6 @@ using Mvc.Server.Models;
using Mvc.Server.Services;
using NWebsec.AspNetCore.Middleware;
using OpenIddict;
using OpenIddict.Models;
namespace Mvc.Server {
public class Startup {
@ -30,10 +27,60 @@ namespace Mvc.Server {
.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(configuration["Data:DefaultConnection:ConnectionString"]));
// Register the Identity services.
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders()
.AddOpenIddict();
.AddDefaultTokenProviders();
// Register the OpenIddict services, including the default Entity Framework stores.
services.AddOpenIddict<ApplicationUser, ApplicationDbContext>()
// Register the HTML/CSS assets and MVC modules to handle the interactive flows.
// Note: these modules are not necessary when using your own authorization controller
// or when using non-interactive flows-only like the resource owner password credentials grant.
.AddAssets()
.AddMvc()
// Register the NWebsec module. Note: you can replace the default Content Security Policy (CSP)
// by calling UseNWebsec with a custom delegate instead of using the parameterless extension.
// This can be useful to allow your HTML views to reference remote scripts/images/styles.
.AddNWebsec(options => options.DefaultSources(directive => directive.Self())
.ImageSources(directive => directive.Self()
.CustomSources("*"))
.ScriptSources(directive => directive.Self()
.UnsafeInline()
.CustomSources("https://my.custom.url/"))
.StyleSources(directive => directive.Self()
.UnsafeInline()))
// During development, you can disable the HTTPS requirement.
.DisableHttpsRequirement();
// When using your own authorization controller instead of using the
// MVC module, you need to configure the authorization/logout paths:
// services.AddOpenIddict<ApplicationUser, ApplicationDbContext>()
// .SetAuthorizationEndpointPath("/connect/authorize")
// .SetLogoutEndpointPath("/connect/logout");
// Note: if you don't explicitly register a signing key, one is automatically generated and
// persisted on the disk. If the key cannot be persisted, an in-memory key is used instead:
// when the application shuts down, the key is definitely lost and the access/identity tokens
// will be considered as invalid by client applications/resource servers when validating them.
//
// On production, using a X.509 certificate stored in the machine store is recommended.
// You can generate a self-signed certificate using Pluralsight's self-cert utility:
// https://s3.amazonaws.com/pluralsight-free/keith-brown/samples/SelfCert.zip
//
// services.AddOpenIddict<ApplicationUser, ApplicationDbContext>()
// .AddSigningCertificate("7D2A741FE34CC2C7369237A5F2078988E17A6A75");
//
// Alternatively, you can also store the certificate as an embedded .pfx resource
// directly in this assembly or in a file published alongside this project:
//
// services.AddOpenIddict<ApplicationUser, ApplicationDbContext>()
// .AddSigningCertificate(
// assembly: typeof(Startup).GetTypeInfo().Assembly,
// resource: "Mvc.Server.Certificate.pfx",
// password: "OpenIddict");
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
@ -79,46 +126,12 @@ namespace Mvc.Server {
Scope = { "user:email" }
});
// Note: OpenIddict must be added after
// ASP.NET Identity and the external providers.
app.UseOpenIddict(builder => {
builder.Options.AllowInsecureHttp = true;
// Note: if you don't explicitly register a signing key, one is automatically generated and
// persisted on the disk. If the key cannot be persisted, an in-memory key is used instead:
// when the application shuts down, the key is definitely lost and the access/identity tokens
// will be considered as invalid by client applications/resource servers when validating them.
//
// On production, using a X.509 certificate stored in the machine store is recommended.
// You can generate a self-signed certificate using Pluralsight's self-cert utility:
// https://s3.amazonaws.com/pluralsight-free/keith-brown/samples/SelfCert.zip
//
// builder.UseSigningCertificate("7D2A741FE34CC2C7369237A5F2078988E17A6A75");
//
// Alternatively, you can also store the certificate as an embedded .pfx resource
// directly in this assembly or in a file published alongside this project:
//
// builder.UseSigningCertificate(
// assembly: typeof(Startup).GetTypeInfo().Assembly,
// resource: "Nancy.Server.Certificate.pfx",
// password: "Owin.Security.OpenIdConnect.Server");
// You can customize the default Content Security Policy (CSP) by calling UseNWebsec explicitly.
// This can be useful to allow your HTML views to reference remote scripts/images/styles.
builder.UseNWebsec(directives => {
directives.DefaultSources(directive => directive.Self())
.ImageSources(directive => directive.Self().CustomSources("*"))
.ScriptSources(directive => directive
.Self()
.UnsafeInline()
.CustomSources("https://my.custom.url"))
.StyleSources(directive => directive.Self().UnsafeInline());
});
});
app.UseOpenIddict();
app.UseMvcWithDefaultRoute();
using (var context = app.ApplicationServices.GetRequiredService<ApplicationDbContext>()) {
using (var context = new ApplicationDbContext(
app.ApplicationServices.GetRequiredService<DbContextOptions<ApplicationDbContext>>())) {
context.Database.EnsureCreated();
// Add Mvc.Client to the known applications.
@ -126,21 +139,20 @@ namespace Mvc.Server {
// Note: when using the introspection middleware, your resource server
// MUST be registered as an OAuth2 client and have valid credentials.
//
// context.Applications.Add(new Application {
// context.Applications.Add(new OpenIddictApplication {
// Id = "resource_server",
// DisplayName = "Main resource server",
// Secret = "875sqd4s5d748z78z7ds1ff8zz8814ff88ed8ea4z4zzd"
// Secret = "875sqd4s5d748z78z7ds1ff8zz8814ff88ed8ea4z4zzd",
// Type = OpenIddictConstants.ClientTypes.Confidential
// });
var hasher = new PasswordHasher<Application>();
context.Applications.Add(new Application {
context.Applications.Add(new OpenIddictApplication {
Id = "myClient",
DisplayName = "My client application",
RedirectUri = "http://localhost:53507/signin-oidc",
LogoutRedirectUri = "http://localhost:53507/",
Secret = Crypto.HashPassword("secret_secret_secret"),
Type = OpenIddictConstants.ApplicationTypes.Confidential
Type = OpenIddictConstants.ClientTypes.Confidential
});
// To test this sample with Postman, use the following settings:
@ -152,11 +164,11 @@ namespace Mvc.Server {
// * Scope: openid email profile roles
// * Grant type: authorization code
// * Request access token locally: yes
context.Applications.Add(new Application {
context.Applications.Add(new OpenIddictApplication {
Id = "postman",
DisplayName = "Postman",
RedirectUri = "https://www.getpostman.com/oauth2/callback",
Type = OpenIddictConstants.ApplicationTypes.Public
Type = OpenIddictConstants.ClientTypes.Public
});
context.SaveChanges();

6
samples/Mvc.Server/project.json

@ -1,6 +1,5 @@
{
"buildOptions": {
"debugType": "portable",
"emitEntryPoint": true,
"warningsAsErrors": true,
"preserveCompilationContext": true,
@ -36,7 +35,10 @@
"Microsoft.Extensions.Configuration.Json": "1.0.0-rc2-final",
"Microsoft.Extensions.Logging.Console": "1.0.0-rc2-final",
"Microsoft.Extensions.Logging.Debug": "1.0.0-rc2-final",
"OpenIddict": { "target": "project" }
"OpenIddict": { "target": "project" },
"OpenIddict.Assets": { "target": "project" },
"OpenIddict.Mvc": { "target": "project" },
"OpenIddict.Security": { "target": "project" }
},
"frameworks": {

7
src/OpenIddict.Assets/OpenIddictExtensions.cs

@ -11,7 +11,12 @@ using Microsoft.Extensions.FileProviders;
namespace Microsoft.AspNetCore.Builder {
public static class OpenIddictExtensions {
public static OpenIddictBuilder UseAssets([NotNull] this OpenIddictBuilder builder) {
/// <summary>
/// Registers the assets module, including the default HTML/CSS files used by the MVC module.
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddAssets([NotNull] this OpenIddictBuilder builder) {
if (builder == null) {
throw new ArgumentNullException(nameof(builder));
}

13
src/OpenIddict.Core/IOpenIddictStore.cs

@ -1,13 +0,0 @@
using System.Threading;
using System.Threading.Tasks;
namespace OpenIddict {
public interface IOpenIddictStore<TUser, TApplication> where TApplication : class {
Task<TApplication> FindApplicationByIdAsync(string identifier, CancellationToken cancellationToken);
Task<TApplication> FindApplicationByLogoutRedirectUri(string url, CancellationToken cancellationToken);
Task<string> GetApplicationTypeAsync(TApplication application, CancellationToken cancellationToken);
Task<string> GetDisplayNameAsync(TApplication application, CancellationToken cancellationToken);
Task<string> GetRedirectUriAsync(TApplication application, CancellationToken cancellationToken);
Task<string> GetHashedSecretAsync(TApplication application, CancellationToken cancellationToken);
}
}

37
src/OpenIddict.Core/Infrastructure/OpenIddictHelpers.cs

@ -0,0 +1,37 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Linq;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Identity;
namespace OpenIddict.Infrastructure {
public static class OpenIddictHelpers {
public static async Task<string> FindClaimAsync<TUser>(
[NotNull] this UserManager<TUser> manager,
[NotNull] TUser user, [NotNull] string type) where TUser : class {
if (manager == null) {
throw new ArgumentNullException(nameof(manager));
}
if (user == null) {
throw new ArgumentNullException(nameof(user));
}
if (string.IsNullOrEmpty(type)) {
throw new ArgumentNullException(nameof(type));
}
// Note: GetClaimsAsync will automatically throw an exception
// if the underlying store doesn't support custom claims.
return (from claim in await manager.GetClaimsAsync(user)
where string.Equals(claim.Type, type, StringComparison.OrdinalIgnoreCase)
select claim.Value).FirstOrDefault();
}
}
}

22
src/OpenIddict.Core/OpenIddictProvider.Authentication.cs → src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Authentication.cs

@ -6,7 +6,6 @@
using System;
using System.Diagnostics;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
@ -17,10 +16,11 @@ using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.Extensions.DependencyInjection;
namespace OpenIddict {
public partial class OpenIddictProvider<TUser, TApplication> : OpenIdConnectServerProvider where TUser : class where TApplication : class {
namespace OpenIddict.Infrastructure {
public partial class OpenIddictProvider<TUser, TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
where TUser : class where TApplication : class where TAuthorization : class where TScope : class where TToken : class {
public override async Task ValidateAuthorizationRequest([NotNull] ValidateAuthorizationRequestContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication>>();
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
// Note: redirect_uri is not required for pure OAuth2 requests
// but this provider uses a stricter policy making it mandatory,
@ -35,7 +35,7 @@ namespace OpenIddict {
}
// Retrieve the application details corresponding to the requested client_id.
var application = await services.Applications.FindApplicationByIdAsync(context.ClientId);
var application = await services.Applications.FindByIdAsync(context.ClientId);
if (application == null) {
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidClient,
@ -54,9 +54,11 @@ namespace OpenIddict {
// To prevent downgrade attacks, ensure that authorization requests using the hybrid/implicit
// flow are rejected if the client identifier corresponds to a confidential application.
// Note: when using the authorization code grant, ValidateClientAuthentication is responsible of
// Note: when using the authorization code grant, ValidateTokenRequest is responsible of
// rejecting the token request if the client_id corresponds to an unauthenticated confidential client.
if (await services.Applications.IsConfidentialApplicationAsync(application) && !context.Request.IsAuthorizationCodeFlow()) {
var type = await services.Applications.GetClientTypeAsync(application);
if (!string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase) &&
!context.Request.IsAuthorizationCodeFlow()) {
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidRequest,
description: "Confidential clients can only use response_type=code.");
@ -144,13 +146,13 @@ namespace OpenIddict {
}
public override async Task HandleAuthorizationRequest([NotNull] HandleAuthorizationRequestContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
// Only handle prompt=none requests at this stage.
if (!string.Equals(context.Request.Prompt, "none", StringComparison.Ordinal)) {
return;
}
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication>>();
// Note: principal is guaranteed to be non-null since ValidateAuthorizationRequest
// rejects prompt=none requests missing or having an invalid id_token_hint.
var principal = await context.HttpContext.Authentication.AuthenticateAsync(context.Options.AuthenticationScheme);
@ -168,7 +170,7 @@ namespace OpenIddict {
// Note: filtering the username is not needed at this stage as OpenIddictController.Accept
// and OpenIddictProvider.GrantResourceOwnerCredentials are expected to reject requests that
// don't include the "email" scope if the username corresponds to the registed email address.
var identity = await services.Applications.CreateIdentityAsync(user, context.Request.GetScopes());
var identity = await services.Tokens.CreateIdentityAsync(user, context.Request.GetScopes());
Debug.Assert(identity != null);
// Create a new authentication ticket holding the user identity.

71
src/OpenIddict.Core/OpenIddictProvider.Exchange.cs → src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Exchange.cs

@ -12,15 +12,14 @@ using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace OpenIddict {
public partial class OpenIddictProvider<TUser, TApplication> : OpenIdConnectServerProvider where TUser : class where TApplication : class {
namespace OpenIddict.Infrastructure {
public partial class OpenIddictProvider<TUser, TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
where TUser : class where TApplication : class where TAuthorization : class where TScope : class where TToken : class {
public override async Task ValidateTokenRequest([NotNull] ValidateTokenRequestContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication>>();
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
// Note: OpenIdConnectServerHandler supports authorization code, refresh token,
// client credentials, resource owner password credentials and custom grants
@ -53,7 +52,7 @@ namespace OpenIddict {
}
// Retrieve the application details corresponding to the requested client_id.
var application = await services.Applications.FindApplicationByIdAsync(context.ClientId);
var application = await services.Applications.FindByIdAsync(context.ClientId);
if (application == null) {
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidClient,
@ -63,7 +62,9 @@ namespace OpenIddict {
}
// Reject tokens requests containing a client_secret if the client application is not confidential.
if (await services.Applications.IsPublicApplicationAsync(application) && !string.IsNullOrEmpty(context.ClientSecret)) {
var type = await services.Applications.GetClientTypeAsync(application);
if (!string.Equals(type, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase) &&
!string.IsNullOrEmpty(context.ClientSecret)) {
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidRequest,
description: "Public clients are not allowed to send a client_secret.");
@ -71,9 +72,8 @@ namespace OpenIddict {
return;
}
// Confidential applications MUST authenticate
// to protect them from impersonation attacks.
else if (await services.Applications.IsConfidentialApplicationAsync(application)) {
// Confidential applications MUST authenticate to protect them from impersonation attacks.
else if (!string.Equals(type, OpenIddictConstants.ClientTypes.Public)) {
if (string.IsNullOrEmpty(context.ClientSecret)) {
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidClient,
@ -95,10 +95,10 @@ namespace OpenIddict {
}
public override async Task GrantClientCredentials([NotNull] GrantClientCredentialsContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication>>();
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
// Retrieve the application details corresponding to the requested client_id.
var application = await services.Applications.FindApplicationByIdAsync(context.ClientId);
var application = await services.Applications.FindByIdAsync(context.ClientId);
Debug.Assert(application != null);
var identity = new ClaimsIdentity(context.Options.AuthenticationScheme);
@ -125,8 +125,7 @@ namespace OpenIddict {
}
public override async Task GrantRefreshToken([NotNull] GrantRefreshTokenContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication>>();
var options = context.HttpContext.RequestServices.GetRequiredService<IOptions<IdentityOptions>>();
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
var principal = context.Ticket?.Principal;
Debug.Assert(principal != null);
@ -140,23 +139,37 @@ namespace OpenIddict {
return;
}
// If the user manager supports security stamps,
// ensure that the refresh token is still valid.
if (services.Users.SupportsUserSecurityStamp) {
var identifier = principal.GetClaim(options.Value.ClaimsIdentity.SecurityStampClaimType);
if (!string.IsNullOrEmpty(identifier) &&
!string.Equals(identifier, await services.Users.GetSecurityStampAsync(user), StringComparison.Ordinal)) {
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidGrant,
description: "The refresh token is no longer valid.");
// Try to extract the token identifier from the authentication ticket.
// If the identifier cannot be found, the revocation check is skipped.
var identifier = context.Ticket.GetTicketId();
if (string.IsNullOrEmpty(identifier)) {
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidGrant,
description: "The refresh token is no longer valid.");
return;
}
return;
}
// Retrieve the token from the database and ensure it is still valid.
var token = await services.Tokens.FindByIdAsync(identifier);
if (token == null) {
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidGrant,
description: "The refresh token is no longer valid.");
return;
}
// When sliding expiration is enabled, immediately
// revoke the refresh token to prevent future reuse.
// See https://tools.ietf.org/html/rfc6749#section-6.
if (context.Options.UseSlidingExpiration) {
await services.Tokens.RevokeAsync(token);
}
// Note: the "scopes" property stored in context.AuthenticationTicket is automatically
// updated by ASOS when the client application requests a restricted scopes collection.
var identity = await services.Applications.CreateIdentityAsync(user, context.Ticket.GetScopes());
var identity = await services.Tokens.CreateIdentityAsync(user, context.Ticket.GetScopes());
Debug.Assert(identity != null);
// Create a new authentication ticket holding the user identity but
@ -170,7 +183,7 @@ namespace OpenIddict {
}
public override async Task GrantResourceOwnerCredentials([NotNull] GrantResourceOwnerCredentialsContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication>>();
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
var user = await services.Users.FindByNameAsync(context.UserName);
if (user == null) {
@ -198,7 +211,7 @@ namespace OpenIddict {
return;
}
// Ensure the password is valid.
if (!await services.Users.CheckPasswordAsync(user, context.Password)) {
context.Reject(
@ -249,7 +262,7 @@ namespace OpenIddict {
}
}
var identity = await services.Applications.CreateIdentityAsync(user, context.Request.GetScopes());
var identity = await services.Tokens.CreateIdentityAsync(user, context.Request.GetScopes());
Debug.Assert(identity != null);
// Create a new authentication ticket holding the user identity.

37
src/OpenIddict.Core/OpenIddictProvider.Introspection.cs → src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Introspection.cs

@ -10,14 +10,13 @@ using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace OpenIddict {
public partial class OpenIddictProvider<TUser, TApplication> : OpenIdConnectServerProvider where TUser : class where TApplication : class {
namespace OpenIddict.Infrastructure {
public partial class OpenIddictProvider<TUser, TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
where TUser : class where TApplication : class where TAuthorization : class where TScope : class where TToken : class {
public override async Task ValidateIntrospectionRequest([NotNull] ValidateIntrospectionRequestContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication>>();
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
// Note: ASOS supports both GET and POST introspection requests but OpenIddict only accepts POST requests.
if (!string.Equals(context.HttpContext.Request.Method, "POST", StringComparison.OrdinalIgnoreCase)) {
@ -41,7 +40,7 @@ namespace OpenIddict {
}
// Retrieve the application details corresponding to the requested client_id.
var application = await services.Applications.FindApplicationByIdAsync(context.ClientId);
var application = await services.Applications.FindByIdAsync(context.ClientId);
if (application == null) {
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidClient,
@ -51,7 +50,8 @@ namespace OpenIddict {
}
// Reject non-confidential applications.
if (await services.Applications.IsPublicApplicationAsync(application)) {
var type = await services.Applications.GetClientTypeAsync(application);
if (!string.Equals(type, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase)) {
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidClient,
description: "Public applications are not allowed to use the introspection endpoint.");
@ -72,14 +72,7 @@ namespace OpenIddict {
}
public override async Task HandleIntrospectionRequest([NotNull] HandleIntrospectionRequestContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication>>();
var options = context.HttpContext.RequestServices.GetRequiredService<IOptions<IdentityOptions>>();
// If the user manager doesn't support security
// stamps, skip the additional validation logic.
if (!services.Users.SupportsUserSecurityStamp) {
return;
}
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
var principal = context.Ticket?.Principal;
Debug.Assert(principal != null);
@ -91,12 +84,16 @@ namespace OpenIddict {
return;
}
var identifier = principal.GetClaim(options.Value.ClaimsIdentity.SecurityStampClaimType);
if (!string.IsNullOrEmpty(identifier) &&
!string.Equals(identifier, await services.Users.GetSecurityStampAsync(user), StringComparison.Ordinal)) {
context.Active = false;
// When the received ticket is a refresh token, ensure it is still valid.
if (context.Ticket.IsRefreshToken()) {
// Retrieve the token from the database using the unique identifier stored in the refresh token:
// if the corresponding entry cannot be found, return Active = false to indicate that is is no longer valid.
var token = await services.Tokens.FindByIdAsync(context.Ticket.GetTicketId());
if (token == null) {
context.Active = false;
return;
return;
}
}
}
}

120
src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Revocation.cs

@ -0,0 +1,120 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
namespace OpenIddict.Infrastructure {
public partial class OpenIddictProvider<TUser, TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
where TUser : class where TApplication : class where TAuthorization : class where TScope : class where TToken : class {
public override async Task ValidateRevocationRequest([NotNull] ValidateRevocationRequestContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
// When token_type_hint is specified, reject the request
// if token_type_hint is not equal to "refresh_token".
if (!string.IsNullOrEmpty(context.Request.GetTokenTypeHint()) &&
!string.Equals(context.Request.GetTokenTypeHint(), OpenIdConnectConstants.TokenTypeHints.RefreshToken)) {
context.Reject(
error: OpenIdConnectConstants.Errors.UnsupportedTokenType,
description: "Only refresh tokens can be revoked. When specifying a token_type_hint " +
"parameter, its value must be equal to 'refresh_token'.");
return;
}
// Skip client authentication if the client identifier is missing.
// Note: ASOS will automatically ensure that the calling application
// cannot revoke a refresh token if it's not the intended audience,
// even if client authentication was skipped.
if (string.IsNullOrEmpty(context.ClientId)) {
context.Skip();
return;
}
// Retrieve the application details corresponding to the requested client_id.
var application = await services.Applications.FindByIdAsync(context.ClientId);
if (application == null) {
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidClient,
description: "Application not found in the database: ensure that your client_id is correct.");
return;
}
// Reject tokens requests containing a client_secret if the client application is not confidential.
var type = await services.Applications.GetClientTypeAsync(application);
if (!string.Equals(type, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase) &&
!string.IsNullOrEmpty(context.ClientSecret)) {
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidRequest,
description: "Public clients are not allowed to send a client_secret.");
return;
}
// Confidential applications MUST authenticate to protect them from impersonation attacks.
else if (!string.Equals(type, OpenIddictConstants.ClientTypes.Public)) {
if (string.IsNullOrEmpty(context.ClientSecret)) {
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidClient,
description: "Missing credentials: ensure that you specified a client_secret.");
return;
}
if (!await services.Applications.ValidateSecretAsync(application, context.ClientSecret)) {
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidClient,
description: "Invalid credentials: ensure that you specified a correct client_secret.");
return;
}
}
context.Validate();
}
public override async Task HandleRevocationRequest([NotNull] HandleRevocationRequestContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
// If the received token is not a refresh token, set Revoked
// to false to indicate that the token cannot be revoked.
if (!context.Ticket.IsRefreshToken()) {
context.Revoked = false;
return;
}
// Extract the token identifier from the authentication ticket.
// If the identifier cannot be extracted, abort the revocation.
var identifier = context.Ticket.GetTicketId();
if (string.IsNullOrEmpty(identifier)) {
context.Revoked = true;
return;
}
// Retrieve the token from the database. If the token cannot be found,
// assume it is invalid and consider the revocation as successful.
var token = await services.Tokens.FindByIdAsync(identifier);
if (token == null) {
context.Revoked = true;
return;
}
// Revoke the refresh token.
await services.Tokens.RevokeAsync(token);
context.Revoked = true;
}
}
}

30
src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Serialization.cs

@ -0,0 +1,30 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System.Diagnostics;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
namespace OpenIddict.Infrastructure {
public partial class OpenIddictProvider<TUser, TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
where TUser : class where TApplication : class where TAuthorization : class where TScope : class where TToken : class {
public override async Task SerializeRefreshToken([NotNull] SerializeRefreshTokenContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
// Note: the identifier cannot be null as it's always
// generated by the OpenID Connect server middleware
// before invoking the SerializeRefreshToken event.
Debug.Assert(!string.IsNullOrEmpty(context.Ticket.GetTicketId()),
"The unique identifier associated with the refresh token was missing or empty.");
// Only persist the refresh token identifier in the database.
await services.Tokens.CreateAsync(context.Ticket.GetTicketId(), OpenIdConnectConstants.TokenTypeHints.RefreshToken);
}
}
}

11
src/OpenIddict.Core/OpenIddictProvider.Session.cs → src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Session.cs

@ -12,10 +12,11 @@ using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
namespace OpenIddict {
public partial class OpenIddictProvider<TUser, TApplication> : OpenIdConnectServerProvider where TUser : class where TApplication : class {
namespace OpenIddict.Infrastructure {
public partial class OpenIddictProvider<TUser, TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
where TUser : class where TApplication : class where TAuthorization : class where TScope : class where TToken : class {
public override async Task ValidateLogoutRequest([NotNull] ValidateLogoutRequestContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication>>();
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
// Skip validation if the optional post_logout_redirect_uri
// parameter was missing from the logout request.
@ -25,7 +26,7 @@ namespace OpenIddict {
return;
}
var application = await services.Applications.FindApplicationByLogoutRedirectUri(context.PostLogoutRedirectUri);
var application = await services.Applications.FindByLogoutRedirectUri(context.PostLogoutRedirectUri);
if (application == null) {
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidClient,
@ -38,7 +39,7 @@ namespace OpenIddict {
}
public override async Task HandleLogoutRequest([NotNull] HandleLogoutRequestContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication>>();
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
// Only validate the id_token_hint if the user is still logged in.
// If the authentication cookie doesn't exist or is no longer valid,

7
src/OpenIddict.Core/OpenIddictProvider.Userinfo.cs → src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Userinfo.cs

@ -13,10 +13,11 @@ using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json.Linq;
namespace OpenIddict {
public partial class OpenIddictProvider<TUser, TApplication> : OpenIdConnectServerProvider where TUser : class where TApplication : class {
namespace OpenIddict.Infrastructure {
public partial class OpenIddictProvider<TUser, TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
where TUser : class where TApplication : class where TAuthorization : class where TScope : class where TToken : class {
public override async Task HandleUserinfoRequest([NotNull] HandleUserinfoRequestContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication>>();
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
var principal = context.Ticket?.Principal;
Debug.Assert(principal != null);

5
src/OpenIddict.Core/OpenIddictProvider.cs → src/OpenIddict.Core/Infrastructure/OpenIddictProvider.cs

@ -8,8 +8,9 @@ using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations;
namespace OpenIddict {
public partial class OpenIddictProvider<TUser, TApplication> : OpenIdConnectServerProvider where TUser : class where TApplication : class {
namespace OpenIddict.Infrastructure {
public partial class OpenIddictProvider<TUser, TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
where TUser : class where TApplication : class where TAuthorization : class where TScope : class where TToken : class {
public override Task MatchEndpoint([NotNull] MatchEndpointContext context) {
// Note: by default, OpenIdConnectServerHandler only handles authorization requests made to AuthorizationEndpointPath.
// This context handler uses a more relaxed policy that allows extracting authorization requests received at

41
src/OpenIddict.Core/OpenIddictServices.cs → src/OpenIddict.Core/Infrastructure/OpenIddictServices.cs

@ -12,42 +12,38 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace OpenIddict {
namespace OpenIddict.Infrastructure {
/// <summary>
/// Exposes the common services used by OpenIddict.
/// </summary>
public class OpenIddictServices<TUser, TApplication> where TUser : class where TApplication : class {
public class OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>
where TUser : class where TApplication : class where TAuthorization : class
where TScope : class where TToken : class {
public OpenIddictServices([NotNull] IServiceProvider services) {
Services = services;
}
/// <summary>
/// Gets the <see cref="OpenIddictManager{TUser, TApplication}"/>.
/// Gets the <see cref="OpenIddictApplicationManager{TApplication}"/>.
/// </summary>
public virtual OpenIddictManager<TUser, TApplication> Applications {
get { return Services.GetRequiredService<OpenIddictManager<TUser, TApplication>>(); }
}
public virtual OpenIddictApplicationManager<TApplication> Applications =>
Services.GetRequiredService<OpenIddictApplicationManager<TApplication>>();
/// <summary>
/// Gets the optional <see cref="HttpContext"/>.
/// </summary>
public virtual HttpContext Context {
get { return Services.GetService<IHttpContextAccessor>()?.HttpContext; }
}
public virtual HttpContext Context => Services.GetService<IHttpContextAccessor>()?.HttpContext;
/// <summary>
/// Gets the <see cref="ILogger"/>.
/// </summary>
public virtual ILogger Logger {
get { return Services.GetRequiredService<ILogger<OpenIddictManager<TUser, TApplication>>>(); }
}
public virtual ILogger Logger =>
Services.GetRequiredService<ILogger<OpenIddictProvider<TUser, TApplication, TAuthorization, TScope, TToken>>>();
/// <summary>
/// Gets the <see cref="OpenIddictOptions"/>.
/// </summary>
public virtual OpenIddictOptions Options {
get { return Services.GetRequiredService<IOptions<OpenIddictOptions>>().Value; }
}
public virtual OpenIddictOptions Options => Services.GetRequiredService<IOptions<OpenIddictOptions>>().Value;
/// <summary>
/// Gets the <see cref="IServiceProvider"/> used to resolve services.
@ -57,22 +53,17 @@ namespace OpenIddict {
/// <summary>
/// Gets the <see cref="SignInManager{TUser}"/>.
/// </summary>
public virtual SignInManager<TUser> SignIn {
get { return Services.GetRequiredService<SignInManager<TUser>>(); }
}
public virtual SignInManager<TUser> SignIn => Services.GetRequiredService<SignInManager<TUser>>();
/// <summary>
/// Gets the <see cref="IOpenIddictStore{TUser, TApplication}"/>.
/// Gets the <see cref="OpenIddictTokenManager{TToken, TUser}"/>.
/// </summary>
public virtual IOpenIddictStore<TUser, TApplication> Store {
get { return Services.GetRequiredService<IOpenIddictStore<TUser, TApplication>>(); }
}
public virtual OpenIddictTokenManager<TToken, TUser> Tokens =>
Services.GetRequiredService<OpenIddictTokenManager<TToken, TUser>>();
/// <summary>
/// Gets the <see cref="UserManager{TUser}"/>.
/// </summary>
public virtual UserManager<TUser> Users {
get { return Services.GetRequiredService<UserManager<TUser>>(); }
}
public virtual UserManager<TUser> Users => Services.GetRequiredService<UserManager<TUser>>();
}
}

173
src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs

@ -0,0 +1,173 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Threading;
using System.Threading.Tasks;
using CryptoHelper;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace OpenIddict {
/// <summary>
/// Provides methods allowing to manage the applications stored in the store.
/// </summary>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
public class OpenIddictApplicationManager<TApplication> where TApplication : class {
public OpenIddictApplicationManager(
[NotNull] IServiceProvider services,
[NotNull] IOpenIddictApplicationStore<TApplication> store,
[NotNull] ILogger<OpenIddictApplicationManager<TApplication>> logger) {
Context = services?.GetRequiredService<IHttpContextAccessor>()?.HttpContext;
Store = store;
Logger = logger;
}
/// <summary>
/// Gets the HTTP context associated with the current manager.
/// </summary>
protected HttpContext Context { get; }
/// <summary>
/// Gets the cancellation token used to abort async operations.
/// </summary>
protected CancellationToken CancellationToken => Context?.RequestAborted ?? CancellationToken.None;
/// <summary>
/// Gets the logger associated with the current manager.
/// </summary>
protected ILogger Logger { get; }
/// <summary>
/// Gets the store associated with the current manager.
/// </summary>
protected IOpenIddictApplicationStore<TApplication> Store { get; }
/// <summary>
/// Creates a new application.
/// </summary>
/// <param name="application">The application to create.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public virtual Task CreateAsync(TApplication application) {
if (application == null) {
throw new ArgumentNullException(nameof(application));
}
return Store.CreateAsync(application, CancellationToken);
}
/// <summary>
/// Retrieves an application using its unique identifier.
/// </summary>
/// <param name="identifier">The unique identifier associated with the application.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public virtual Task<TApplication> FindByIdAsync(string identifier) {
return Store.FindByIdAsync(identifier, CancellationToken);
}
/// <summary>
/// Retrieves an application using its post_logout_redirect_uri.
/// </summary>
/// <param name="url">The post_logout_redirect_uri associated with the application.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public virtual Task<TApplication> FindByLogoutRedirectUri(string url) {
return Store.FindByLogoutRedirectUri(url, CancellationToken);
}
/// <summary>
/// Retrieves the client type associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public virtual async Task<string> GetClientTypeAsync(TApplication application) {
if (application == null) {
throw new ArgumentNullException(nameof(application));
}
var type = await Store.GetClientTypeAsync(application, CancellationToken);
// Ensure the application type returned by the store is supported by the manager.
if (!string.Equals(type, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase) &&
!string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) {
throw new InvalidOperationException("Only 'confidential' or 'public' applications are " +
"supported by the default application manager.");
}
return type;
}
/// <summary>
/// Retrieves the display name associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public virtual Task<string> GetDisplayNameAsync(TApplication application) {
if (application == null) {
throw new ArgumentNullException(nameof(application));
}
return Store.GetDisplayNameAsync(application, CancellationToken);
}
/// <summary>
/// Validates the redirect_uri associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="address">The address that should be compared to the redirect_uri stored in the database.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public virtual async Task<bool> ValidateRedirectUriAsync(TApplication application, string address) {
if (application == null) {
throw new ArgumentNullException(nameof(application));
}
if (!string.Equals(address, await Store.GetRedirectUriAsync(application, CancellationToken), StringComparison.Ordinal)) {
Logger.LogWarning("Client validation failed because {RedirectUri} was not a valid redirect_uri " +
"for {Client}", address, await GetDisplayNameAsync(application));
return false;
}
return true;
}
/// <summary>
/// Validates the client_secret associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="secret">The secret that should be compared to the client_secret stored in the database.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public virtual async Task<bool> ValidateSecretAsync(TApplication application, string secret) {
if (application == null) {
throw new ArgumentNullException(nameof(application));
}
var type = await GetClientTypeAsync(application);
if (type != OpenIddictConstants.ClientTypes.Confidential) {
Logger.LogWarning("Client authentication cannot be enforced for non-confidential applications.");
return false;
}
var hash = await Store.GetHashedSecretAsync(application, CancellationToken);
if (string.IsNullOrEmpty(hash)) {
Logger.LogError("Client authentication failed for {Client} because " +
"no client secret was associated with the application.");
return false;
}
if (!Crypto.VerifyHashedPassword(hash, secret)) {
Logger.LogWarning("Client authentication failed for {Client}.", await GetDisplayNameAsync(application));
return false;
}
return true;
}
}
}

49
src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs

@ -0,0 +1,49 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Threading;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace OpenIddict {
/// <summary>
/// Provides methods allowing to manage the authorizations stored in the store.
/// </summary>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
public class OpenIddictAuthorizationManager<TAuthorization> where TAuthorization : class {
public OpenIddictAuthorizationManager(
[NotNull] IServiceProvider services,
[NotNull] IOpenIddictAuthorizationStore<TAuthorization> store,
[NotNull] ILogger<OpenIddictAuthorizationManager<TAuthorization>> logger) {
Context = services?.GetRequiredService<IHttpContextAccessor>()?.HttpContext;
Logger = logger;
Store = store;
}
/// <summary>
/// Gets the HTTP context associated with the current manager.
/// </summary>
protected HttpContext Context { get; }
/// <summary>
/// Gets the cancellation token used to abort async operations.
/// </summary>
protected CancellationToken CancellationToken => Context?.RequestAborted ?? CancellationToken.None;
/// <summary>
/// Gets the logger associated with the current manager.
/// </summary>
protected ILogger Logger { get; }
/// <summary>
/// Gets the store associated with the current manager.
/// </summary>
protected IOpenIddictAuthorizationStore<TAuthorization> Store { get; }
}
}

49
src/OpenIddict.Core/Managers/OpenIddictScopeManager.cs

@ -0,0 +1,49 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Threading;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace OpenIddict {
/// <summary>
/// Provides methods allowing to manage the scopes stored in the store.
/// </summary>
/// <typeparam name="TScope">The type of the Scope entity.</typeparam>
public class OpenIddictScopeManager<TScope> where TScope : class {
public OpenIddictScopeManager(
[NotNull] IServiceProvider services,
[NotNull] IOpenIddictAuthorizationStore<TScope> store,
[NotNull] ILogger<OpenIddictAuthorizationManager<TScope>> logger) {
Context = services?.GetRequiredService<IHttpContextAccessor>()?.HttpContext;
Logger = logger;
Store = store;
}
/// <summary>
/// Gets the HTTP context associated with the current manager.
/// </summary>
protected HttpContext Context { get; }
/// <summary>
/// Gets the cancellation token used to abort async operations.
/// </summary>
protected CancellationToken CancellationToken => Context?.RequestAborted ?? CancellationToken.None;
/// <summary>
/// Gets the logger associated with the current manager.
/// </summary>
protected ILogger Logger { get; }
/// <summary>
/// Gets the store associated with the current manager.
/// </summary>
protected IOpenIddictAuthorizationStore<TScope> Store { get; }
}
}

189
src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs

@ -0,0 +1,189 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace OpenIddict {
/// <summary>
/// Provides methods allowing to manage the tokens stored in the store.
/// </summary>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <typeparam name="TUser">The type of the User entity.</typeparam>
public class OpenIddictTokenManager<TToken, TUser> where TToken : class where TUser : class {
public OpenIddictTokenManager(
[NotNull] IServiceProvider services,
[NotNull] IOpenIddictTokenStore<TToken> store,
[NotNull] UserManager<TUser> users,
[NotNull] IOptions<IdentityOptions> options,
[NotNull] ILogger<OpenIddictTokenManager<TToken, TUser>> logger) {
Context = services?.GetRequiredService<IHttpContextAccessor>()?.HttpContext;
Logger = logger;
Options = options.Value;
Store = store;
Users = users;
}
/// <summary>
/// Gets the cancellation token used to abort async operations.
/// </summary>
protected CancellationToken CancellationToken => Context?.RequestAborted ?? CancellationToken.None;
/// <summary>
/// Gets the HTTP context associated with the current manager.
/// </summary>
protected HttpContext Context { get; }
/// <summary>
/// Gets the logger associated with the current manager.
/// </summary>
protected ILogger Logger { get; }
/// <summary>
/// Gets the identity options.
/// </summary>
protected IdentityOptions Options { get; }
/// <summary>
/// Gets the store associated with the current manager.
/// </summary>
protected IOpenIddictTokenStore<TToken> Store { get; }
/// <summary>
/// Gets the user manager.
/// </summary>
protected UserManager<TUser> Users { get; }
/// <summary>
/// Creates a new <see cref="ClaimsIdentity"/> used to create new tokens.
/// </summary>
/// <param name="user">The user corresponding to the identity.</param>
/// <param name="scopes">The scopes granted by the resource owner.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public virtual async Task<ClaimsIdentity> CreateIdentityAsync(TUser user, IEnumerable<string> scopes) {
if (user == null) {
throw new ArgumentNullException(nameof(user));
}
if (scopes == null) {
throw new ArgumentNullException(nameof(scopes));
}
var identity = new ClaimsIdentity(
OpenIdConnectServerDefaults.AuthenticationScheme,
Options.ClaimsIdentity.UserNameClaimType,
Options.ClaimsIdentity.RoleClaimType);
// Note: the name identifier is always included in both identity and
// access tokens, even if an explicit destination is not specified.
identity.AddClaim(ClaimTypes.NameIdentifier, await Users.GetUserIdAsync(user));
// Resolve the email address associated with the user if the underlying store supports it.
var email = Users.SupportsUserEmail ? await Users.GetEmailAsync(user) : null;
// Only add the name claim if the "profile" scope was granted.
if (scopes.Contains(OpenIdConnectConstants.Scopes.Profile)) {
var username = await Users.GetUserNameAsync(user);
// Throw an exception if the username corresponds to the registered
// email address and if the "email" scope has not been requested.
if (!scopes.Contains(OpenIdConnectConstants.Scopes.Email) &&
!string.IsNullOrEmpty(email) &&
string.Equals(username, email, StringComparison.OrdinalIgnoreCase)) {
throw new InvalidOperationException("The 'email' scope is required.");
}
identity.AddClaim(ClaimTypes.Name, username,
OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
}
// Only add the email address if the "email" scope was granted.
if (!string.IsNullOrEmpty(email) && scopes.Contains(OpenIdConnectConstants.Scopes.Email)) {
identity.AddClaim(ClaimTypes.Email, email,
OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
}
if (Users.SupportsUserRole && scopes.Contains(OpenIddictConstants.Scopes.Roles)) {
foreach (var role in await Users.GetRolesAsync(user)) {
identity.AddClaim(identity.RoleClaimType, role,
OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
}
}
if (Users.SupportsUserSecurityStamp) {
var stamp = await Users.GetSecurityStampAsync(user);
if (!string.IsNullOrEmpty(stamp)) {
identity.AddClaim(Options.ClaimsIdentity.SecurityStampClaimType, stamp,
OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
}
}
return identity;
}
/// <summary>
/// Creates a new token, defined by a unique identifier and a token type.
/// </summary>
/// <param name="identifier">The unique identifier associated with the token to create.</param>
/// <param name="type">The token type.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public virtual Task CreateAsync(string identifier, string type) {
if (string.IsNullOrEmpty(identifier)) {
throw new ArgumentException("The identifier cannot be null or empty", nameof(identifier));
}
if (string.IsNullOrEmpty(type)) {
throw new ArgumentException("The token type cannot be null or empty", nameof(type));
}
return Store.CreateAsync(identifier, type, CancellationToken);
}
/// <summary>
/// Retrieves an token using its unique identifier.
/// </summary>
/// <param name="identifier">The unique identifier associated with the token.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public virtual Task<TToken> FindByIdAsync(string identifier) {
if (string.IsNullOrEmpty(identifier)) {
throw new ArgumentException("The identifier cannot be null or empty", nameof(identifier));
}
return Store.FindByIdAsync(identifier, CancellationToken);
}
/// <summary>
/// Revokes a token.
/// </summary>
/// <param name="token">The token to revoke.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public virtual Task RevokeAsync(TToken token) {
if (token == null) {
throw new ArgumentNullException(nameof(token));
}
return Store.RevokeAsync(token, CancellationToken);
}
}
}

426
src/OpenIddict.Core/OpenIddictBuilder.cs

@ -1,19 +1,431 @@
using System.Collections.Generic;
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using OpenIddict;
namespace Microsoft.AspNetCore.Builder {
/// <summary>
/// Holds various properties allowing to configure OpenIddict.
/// Exposes the necessary methods required to configure OpenIddict.
/// </summary>
public class OpenIddictBuilder {
/// <summary>
/// Gets the list of the OpenIddict modules.
/// Initializes a new instance of <see cref="OpenIddictBuilder"/>.
/// </summary>
/// <param name="services">The services collection.</param>
public OpenIddictBuilder(IServiceCollection services) {
Services = services;
}
/// <summary>
/// Gets or sets the type corresponding to the Application entity.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public Type ApplicationType { get; set; }
/// <summary>
/// Gets or sets the type corresponding to the Authorization entity.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public Type AuthorizationType { get; set; }
/// <summary>
/// Gets or sets the type corresponding to the Scope entity.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public Type ScopeType { get; set; }
/// <summary>
/// Gets or sets the type corresponding to the Token entity.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public Type TokenType { get; set; }
/// <summary>
/// Gets or sets the type corresponding to the User entity.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public Type UserType { get; set; }
/// <summary>
/// Gets the services collection.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public IServiceCollection Services { get; }
/// <summary>
/// Amends the default OpenIddict configuration.
/// </summary>
/// <param name="configuration">The delegate used to configure the OpenIddict options.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder Configure([NotNull] Action<OpenIddictOptions> configuration) {
if (configuration == null) {
throw new ArgumentNullException(nameof(configuration));
}
Services.Configure(configuration);
return this;
}
/// <summary>
/// Adds a custom application manager.
/// </summary>
/// <typeparam name="TManager">The type of the custom manager.</typeparam>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AddApplicationManager<TManager>() {
var contract = typeof(OpenIddictApplicationManager<>).MakeGenericType(ApplicationType);
if (!contract.IsAssignableFrom(typeof(TManager))) {
throw new InvalidOperationException("Custom managers must be derived from OpenIddictApplicationManager.");
}
Services.AddScoped(contract, typeof(TManager));
return this;
}
/// <summary>
/// Adds a custom application store.
/// </summary>
/// <typeparam name="TStore">The type of the custom store.</typeparam>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AddApplicationStore<TStore>() {
var contract = typeof(IOpenIddictApplicationStore<>).MakeGenericType(ApplicationType);
if (!contract.IsAssignableFrom(typeof(TStore))) {
throw new InvalidOperationException("Custom stores must implement IOpenIddictApplicationStore.");
}
Services.AddScoped(contract, typeof(TStore));
return this;
}
/// <summary>
/// Adds a custom authorization manager.
/// </summary>
/// <typeparam name="TManager">The type of the custom manager.</typeparam>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AddAuthorizationManager<TManager>() {
var contract = typeof(OpenIddictAuthorizationManager<>).MakeGenericType(AuthorizationType);
if (!contract.IsAssignableFrom(typeof(TManager))) {
throw new InvalidOperationException("Custom managers must be derived from OpenIddictAuthorizationManager.");
}
Services.AddScoped(contract, typeof(TManager));
return this;
}
/// <summary>
/// Adds a custom authorization store.
/// </summary>
/// <typeparam name="TStore">The type of the custom store.</typeparam>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AddAuthorizationStore<TStore>() {
var contract = typeof(IOpenIddictAuthorizationStore<>).MakeGenericType(AuthorizationType);
if (!contract.IsAssignableFrom(typeof(TStore))) {
throw new InvalidOperationException("Custom stores must implement IOpenIddictAuthorizationStore.");
}
Services.AddScoped(contract, typeof(TStore));
return this;
}
/// <summary>
/// Adds a custom scope manager.
/// </summary>
/// <typeparam name="TManager">The type of the custom manager.</typeparam>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AddScopeManager<TManager>() {
var contract = typeof(OpenIddictScopeManager<>).MakeGenericType(ScopeType);
if (!contract.IsAssignableFrom(typeof(TManager))) {
throw new InvalidOperationException("Custom managers must be derived from OpenIddictScopeManager.");
}
Services.AddScoped(contract, typeof(TManager));
return this;
}
/// <summary>
/// Adds a custom scope store.
/// </summary>
/// <typeparam name="TStore">The type of the custom store.</typeparam>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AddScopeStore<TStore>() {
var contract = typeof(IOpenIddictScopeStore<>).MakeGenericType(ScopeType);
if (!contract.IsAssignableFrom(typeof(TStore))) {
throw new InvalidOperationException("Custom stores must implement IOpenIddictScopeStore.");
}
Services.AddScoped(contract, typeof(TStore));
return this;
}
/// <summary>
/// Adds a custom token manager.
/// </summary>
/// <typeparam name="TManager">The type of the custom manager.</typeparam>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AddTokenManager<TManager>() {
var contract = typeof(OpenIddictTokenManager<,>).MakeGenericType(TokenType, UserType);
if (!contract.IsAssignableFrom(typeof(TManager))) {
throw new InvalidOperationException("Custom managers must be derived from OpenIddictTokenManager.");
}
Services.AddScoped(contract, typeof(TManager));
return this;
}
/// <summary>
/// Adds a custom token store.
/// </summary>
/// <typeparam name="TStore">The type of the custom store.</typeparam>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AddTokenStore<TStore>() {
var contract = typeof(IOpenIddictTokenStore<>).MakeGenericType(TokenType);
if (!contract.IsAssignableFrom(typeof(TStore))) {
throw new InvalidOperationException("Custom stores must implement IOpenIddictTokenStore.");
}
Services.AddScoped(contract, typeof(TStore));
return this;
}
/// <summary>
/// Registers a new OpenIddict module. If a module with the same name already
/// exists, the new instance is ignored and this extension has no effect.
/// </summary>
/// <param name="name">The name of the OpenIddict module.</param>
/// <param name="position">The relative position of the OpenIddict module in the ASP.NET Core pipeline.</param>
/// <param name="registration">The delegate used to register the module in the ASP.NET Core pipeline.</param>
/// <returns>The<see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AddModule(
[NotNull] string name, int position,
[NotNull] Action<IApplicationBuilder> registration) {
if (string.IsNullOrEmpty(name)) {
throw new ArgumentNullException(nameof(name));
}
if (registration == null) {
throw new ArgumentNullException(nameof(registration));
}
return Configure(options => {
if (options.Modules.Any(module => module.Name == name)) {
return;
}
options.Modules.Add(new OpenIddictModule(name, position, registration));
});
}
/// <summary>
/// Registers a <see cref="X509Certificate2"/> used to sign the tokens issued by OpenIddict.
/// </summary>
/// <param name="certificate">The certificate used to sign the security tokens issued by the server.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AddSigningCertificate([NotNull] X509Certificate2 certificate) {
if (certificate == null) {
throw new ArgumentNullException(nameof(certificate));
}
if (!certificate.HasPrivateKey) {
throw new InvalidOperationException("The certificate doesn't contain the required private key.");
}
return Configure(options => options.SigningCredentials.AddCertificate(certificate));
}
/// <summary>
/// Registers a <see cref="X509Certificate2"/> retrieved from
/// an embedded resource to sign the tokens issued by OpenIddict.
/// </summary>
/// <param name="assembly">The assembly containing the certificate.</param>
/// <param name="resource">The name of the embedded resource.</param>
/// <param name="password">The password used to open the certificate.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AddSigningCertificate(
[NotNull] Assembly assembly, [NotNull] string resource, [NotNull] string password) {
if (assembly == null) {
throw new ArgumentNullException(nameof(assembly));
}
if (string.IsNullOrEmpty(resource)) {
throw new ArgumentNullException(nameof(resource));
}
if (string.IsNullOrEmpty(password)) {
throw new ArgumentNullException(nameof(password));
}
return Configure(options => options.SigningCredentials.AddCertificate(assembly, resource, password));
}
/// <summary>
/// Registers a <see cref="X509Certificate2"/> extracted
/// from a stream to sign the tokens issued by OpenIddict.
/// </summary>
/// <param name="stream">The stream containing the certificate.</param>
/// <param name="password">The password used to open the certificate.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AddSigningCertificate([NotNull] Stream stream, [NotNull] string password) {
if (stream == null) {
throw new ArgumentNullException(nameof(stream));
}
if (string.IsNullOrEmpty(password)) {
throw new ArgumentNullException(nameof(password));
}
return Configure(options => options.SigningCredentials.AddCertificate(stream, password));
}
/// <summary>
/// Registers a <see cref="X509Certificate2"/> extracted
/// from a stream to sign the tokens issued by OpenIddict.
/// </summary>
/// <param name="stream">The stream containing the certificate.</param>
/// <param name="password">The password used to open the certificate.</param>
/// <param name="flags">
/// An enumeration of flags indicating how and where
/// to store the private key of the certificate.
/// </param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AddSigningCertificate(
[NotNull] Stream stream, [NotNull] string password, X509KeyStorageFlags flags) {
if (stream == null) {
throw new ArgumentNullException(nameof(stream));
}
if (string.IsNullOrEmpty(password)) {
throw new ArgumentNullException(nameof(password));
}
return Configure(options => options.SigningCredentials.AddCertificate(stream, password, flags));
}
/// <summary>
/// Registers a <see cref="X509Certificate2"/> retrieved from the
/// X.509 machine store to sign the tokens issued by OpenIddict.
/// </summary>
/// <param name="thumbprint">The thumbprint of the certificate used to identify it in the X.509 store.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AddSigningCertificate([NotNull] string thumbprint) {
if (string.IsNullOrEmpty(thumbprint)) {
throw new ArgumentNullException(nameof(thumbprint));
}
return Configure(options => options.SigningCredentials.AddCertificate(thumbprint));
}
/// <summary>
/// Registers a <see cref="X509Certificate2"/> retrieved from the
/// given X.509 store to sign the tokens issued by OpenIddict.
/// </summary>
/// <param name="thumbprint">The thumbprint of the certificate used to identify it in the X.509 store.</param>
/// <param name="name">The name of the X.509 store.</param>
/// <param name="location">The location of the X.509 store.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AddSigningCertificate(
[NotNull] string thumbprint, StoreName name, StoreLocation location) {
if (string.IsNullOrEmpty(thumbprint)) {
throw new ArgumentNullException(nameof(thumbprint));
}
return Configure(options => options.SigningCredentials.AddCertificate(thumbprint, name, location));
}
/// <summary>
/// Disables the HTTPS requirement during development.
/// </summary>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder DisableHttpsRequirement() {
return Configure(options => options.AllowInsecureHttp = true);
}
/// <summary>
/// Disables sliding expiration, which prevents OpenIddict from issuing a new
/// refresh token when receiving a grant_type=refresh_token token request.
/// </summary>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder DisableSlidingExpiration() {
return Configure(options => options.UseSlidingExpiration = false);
}
/// <summary>
/// Sets the relative path corresponding to the authorization endpoint.
/// </summary>
/// <param name="path">The relative path of the authorization endpoint.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder SetAuthorizationEndpointPath(PathString path) {
return Configure(options => options.AuthorizationEndpointPath = path);
}
/// <summary>
/// Sets the relative path corresponding to the logout endpoint.
/// </summary>
/// <param name="path">The relative path of the logout endpoint.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder SetLogoutEndpointPath(PathString path) {
return Configure(options => options.LogoutEndpointPath = path);
}
/// <summary>
/// Sets the relative path corresponding to the token endpoint.
/// </summary>
/// <param name="path">The relative path of the token endpoint.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder SetTokenEndpointPath(PathString path) {
return Configure(options => options.TokenEndpointPath = path);
}
/// <summary>
/// Sets the access token lifetime, after which client applications must retrieve
/// a new access token by making a grant_type=refresh_token token request
/// or a prompt=none authorization request, depending on the selected flow.
/// Using long-lived access tokens or tokens that never expire is not recommended.
/// </summary>
/// <param name="lifetime">The access token lifetime.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder SetAccessTokenLifetime(TimeSpan lifetime) {
return Configure(options => options.AccessTokenLifetime = lifetime);
}
/// <summary>
/// Sets the refresh token lifetime, after which client applications must get
/// a new authorization from the user. When sliding expiration is enabled,
/// a new refresh token is always issued to the client application,
/// which prolongs the validity period of the refresh token.
/// </summary>
public ICollection<OpenIddictModule> Modules { get; } = new List<OpenIddictModule>();
/// <param name="lifetime">The refresh token lifetime.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder SetRefreshTokenLifetime(TimeSpan lifetime) {
return Configure(options => options.RefreshTokenLifetime = lifetime);
}
/// <summary>
/// Gets or sets the options used by OpenIddict.
/// Sets JWT as the default token format for access tokens.
/// </summary>
public OpenIddictOptions Options { get; set; } = new OpenIddictOptions();
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder UseJsonWebTokens() {
return Configure(options => options.UseJwtTokens());
}
}
}
}

36
src/OpenIddict.Core/OpenIddictConfiguration.cs

@ -1,36 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/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 OpenIddictConfiguration {
public OpenIddictConfiguration(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; }
}
}

18
src/OpenIddict.Core/OpenIddictConstants.cs

@ -1,14 +1,20 @@
namespace OpenIddict {
public static class OpenIddictConstants {
public static class ApplicationTypes {
public const string Confidential = "confidential";
public const string Public = "public";
}
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
namespace OpenIddict {
public static class OpenIddictConstants {
public static class Claims {
public const string Roles = "roles";
}
public static class ClientTypes {
public const string Confidential = "confidential";
public const string Public = "public";
}
public static class Scopes {
public const string Roles = "roles";
}

317
src/OpenIddict.Core/OpenIddictExtensions.cs

@ -5,160 +5,93 @@
*/
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Extensions.Options;
using OpenIddict;
using OpenIddict.Infrastructure;
namespace Microsoft.AspNetCore.Builder {
public static class OpenIddictExtensions {
public static IdentityBuilder AddOpenIddictCore<TApplication>(
[NotNull] this IdentityBuilder builder,
[NotNull] Action<OpenIddictConfiguration> configuration)
where TApplication : class {
if (builder == null) {
throw new ArgumentNullException(nameof(builder));
}
if (configuration == null) {
throw new ArgumentNullException(nameof(configuration));
}
builder.Services.AddAuthentication();
builder.Services.AddDistributedMemoryCache();
builder.Services.TryAddSingleton(
typeof(IOpenIdConnectServerProvider),
typeof(OpenIddictProvider<,>).MakeGenericType(
builder.UserType, typeof(TApplication)));
builder.Services.TryAddScoped(
typeof(OpenIddictManager<,>).MakeGenericType(
builder.UserType, typeof(TApplication)));
builder.Services.TryAddTransient(
typeof(OpenIddictServices<,>).MakeGenericType(
builder.UserType, typeof(TApplication)));
var instance = new OpenIddictConfiguration(builder.Services) {
/// <summary>
/// Registers the OpenIddict core services in the DI container.
/// When using this method, custom stores must be manually registered.
/// </summary>
/// <typeparam name="TUser">The type of the User entity.</typeparam>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <typeparam name="TScope">The type of the Scope entity.</typeparam>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <param name="services">The services collection.</param>
/// <remarks>
/// Note: the core services include native support for the non-interactive flows
/// (resource owner password credentials, client credentials, refresh token).
/// To support interactive flows like authorization code or implicit/hybrid,
/// consider adding the MVC module or creating your own authorization controller.
/// </remarks>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddOpenIddict<TUser, TApplication, TAuthorization, TScope, TToken>(
[NotNull] this IServiceCollection services)
where TUser : class
where TApplication : class
where TAuthorization : class
where TScope : class
where TToken : class {
if (services == null) {
throw new ArgumentNullException(nameof(services));
}
var builder = new OpenIddictBuilder(services) {
ApplicationType = typeof(TApplication),
RoleType = builder.RoleType,
UserType = builder.UserType
AuthorizationType = typeof(TAuthorization),
ScopeType = typeof(TScope),
TokenType = typeof(TToken),
UserType = typeof(TUser)
};
builder.Services.TryAddSingleton(instance);
configuration(instance);
return builder;
}
public static OpenIddictConfiguration UseManager<TManager>([NotNull] this OpenIddictConfiguration configuration) {
if (configuration == null) {
throw new ArgumentNullException(nameof(configuration));
}
var contract = typeof(OpenIddictManager<,>).MakeGenericType(configuration.UserType,
configuration.ApplicationType);
if (!contract.IsAssignableFrom(typeof(TManager))) {
throw new InvalidOperationException("Custom managers must be derived from OpenIddictManager.");
}
configuration.Services.Replace(ServiceDescriptor.Scoped(contract, typeof(TManager)));
return configuration;
}
public static OpenIddictConfiguration UseStore<TStore>([NotNull] this OpenIddictConfiguration configuration) {
if (configuration == null) {
throw new ArgumentNullException(nameof(configuration));
}
var contract = typeof(IOpenIddictStore<,>).MakeGenericType(configuration.UserType,
configuration.ApplicationType);
if (!contract.IsAssignableFrom(typeof(TStore))) {
throw new InvalidOperationException("Custom stores must implement IOpenIddictStore.");
}
configuration.Services.Replace(ServiceDescriptor.Scoped(contract, typeof(TStore)));
return configuration;
}
public static OpenIddictBuilder AddModule(
[NotNull] this OpenIddictBuilder builder,
[NotNull] string name, int position,
[NotNull] Action<IApplicationBuilder> registration) {
if (builder == null) {
throw new ArgumentNullException(nameof(builder));
}
if (string.IsNullOrEmpty(name)) {
throw new ArgumentNullException(nameof(name));
}
if (registration == null) {
throw new ArgumentNullException(nameof(registration));
}
// Note: always call ToArray to make sure the foreach
// block doesn't iterate on the modified collection.
foreach (var module in builder.Modules.Where(module => string.Equals(module.Name, name)).ToArray()) {
builder.Modules.Remove(module);
}
// Register the services required by the OpenID Connect server middleware.
builder.Services.AddAuthentication();
builder.Services.AddDistributedMemoryCache();
builder.Modules.Add(new OpenIddictModule {
Name = name,
Position = position,
Registration = registration
builder.Configure(options => {
// Register the OpenID Connect server provider in the OpenIddict options.
options.Provider = new OpenIddictProvider<TUser, TApplication, TAuthorization, TScope, TToken>();
});
return builder;
}
// Register the OpenIddict core services in the DI container.
builder.Services.TryAddScoped<OpenIddictApplicationManager<TApplication>>();
builder.Services.TryAddScoped<OpenIddictAuthorizationManager<TAuthorization>>();
builder.Services.TryAddScoped<OpenIddictScopeManager<TScope>>();
builder.Services.TryAddScoped<OpenIddictTokenManager<TToken, TUser>>();
builder.Services.TryAddScoped<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
public static IApplicationBuilder UseOpenIddictCore([NotNull] this IApplicationBuilder app) {
return app.UseOpenIddictCore(options => { });
return builder;
}
public static IApplicationBuilder UseOpenIddictCore(
[NotNull] this IApplicationBuilder app,
[NotNull] Action<OpenIddictBuilder> configuration) {
/// <summary>
/// Registers OpenIddict in the ASP.NET Core pipeline.
/// </summary>
/// <param name="app">The application builder used to register middleware instances.</param>
/// <returns>The <see cref="IApplicationBuilder"/>.</returns>
public static IApplicationBuilder UseOpenIddict([NotNull] this IApplicationBuilder app) {
if (app == null) {
throw new ArgumentNullException(nameof(app));
}
if (configuration == null) {
throw new ArgumentNullException(nameof(configuration));
}
// Resolve the OpenIddict options from the DI container.
var options = app.ApplicationServices.GetRequiredService<IOptions<OpenIddictOptions>>().Value;
var builder = new OpenIddictBuilder();
// Resolve the OpenIddict provider from the services container.
builder.Options.Provider = app.ApplicationServices.GetRequiredService<IOpenIdConnectServerProvider>();
// By default, enable AllowInsecureHttp in development/testing environments.
var environment = app.ApplicationServices.GetRequiredService<IHostingEnvironment>();
builder.Options.AllowInsecureHttp = environment.IsDevelopment() || environment.IsEnvironment("Testing");
// Run the configuration delegate
// provided by the application.
configuration.Invoke(builder);
// Add OpenIdConnectServerMiddleware to the ASP.NET Core pipeline.
builder.AddModule("ASOS", 0, map => map.UseOpenIdConnectServer(builder.Options));
// Get the modules registered by the application
// and add the OpenID Connect server middleware.
var modules = options.Modules.ToList();
modules.Add(new OpenIddictModule("OpenID Connect server", 0, builder => builder.UseOpenIdConnectServer(options)));
// Register the OpenIddict modules in the ASP.NET Core pipeline.
foreach (var module in builder.Modules.OrderBy(module => module.Position)) {
if (module.Registration == null) {
throw new InvalidOperationException("The registration delegate cannot be null.");
foreach (var module in modules.OrderBy(module => module.Position)) {
if (module?.Registration == null) {
throw new InvalidOperationException("An invalid OpenIddict module was registered.");
}
module.Registration(app);
@ -166,129 +99,5 @@ namespace Microsoft.AspNetCore.Builder {
return app;
}
/// <summary>
/// Registers a <see cref="X509Certificate2"/> used to sign the tokens issued by OpenIddict.
/// </summary>
/// <param name="builder">The builder used to configure OpenIddict.</param>
/// <param name="certificate">The certificate used to sign the security tokens issued by the server.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder UseSigningCertificate(
[NotNull] this OpenIddictBuilder builder, [NotNull] X509Certificate2 certificate) {
if (builder == null) {
throw new ArgumentNullException(nameof(builder));
}
// Register the certificate in the ASOS/OpenIddict options.
builder.Options.SigningCredentials.AddCertificate(certificate);
return builder;
}
/// <summary>
/// Registers a <see cref="X509Certificate2"/> retrieved from
/// an embedded resource to sign the tokens issued by OpenIddict.
/// </summary>
/// <param name="builder">The builder used to configure OpenIddict.</param>
/// <param name="assembly">The assembly containing the certificate.</param>
/// <param name="resource">The name of the embedded resource.</param>
/// <param name="password">The password used to open the certificate.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder UseSigningCertificate(
[NotNull] this OpenIddictBuilder builder, [NotNull] Assembly assembly,
[NotNull] string resource, [NotNull] string password) {
if (builder == null) {
throw new ArgumentNullException(nameof(builder));
}
// Register the certificate in the ASOS/OpenIddict options.
builder.Options.SigningCredentials.AddCertificate(assembly, resource, password);
return builder;
}
/// <summary>
/// Registers a <see cref="X509Certificate2"/> extracted
/// from a stream to sign the tokens issued by OpenIddict.
/// </summary>
/// <param name="builder">The builder used to configure OpenIddict.</param>
/// <param name="stream">The stream containing the certificate.</param>
/// <param name="password">The password used to open the certificate.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder UseSigningCertificate(
[NotNull] this OpenIddictBuilder builder,
[NotNull] Stream stream, [NotNull] string password) {
if (builder == null) {
throw new ArgumentNullException(nameof(builder));
}
// Register the certificate in the ASOS/OpenIddict options.
builder.Options.SigningCredentials.AddCertificate(stream, password);
return builder;
}
/// <summary>
/// Registers a <see cref="X509Certificate2"/> extracted
/// from a stream to sign the tokens issued by OpenIddict.
/// </summary>
/// <param name="builder">The builder used to configure OpenIddict.</param>
/// <param name="stream">The stream containing the certificate.</param>
/// <param name="password">The password used to open the certificate.</param>
/// <param name="flags">An enumeration of flags indicating how and where to store the private key of the certificate.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder UseSigningCertificate(
[NotNull] this OpenIddictBuilder builder, [NotNull] Stream stream,
[NotNull] string password, X509KeyStorageFlags flags) {
if (builder == null) {
throw new ArgumentNullException(nameof(builder));
}
// Register the certificate in the ASOS/OpenIddict options.
builder.Options.SigningCredentials.AddCertificate(stream, password, flags);
return builder;
}
/// <summary>
/// Registers a <see cref="X509Certificate2"/> retrieved from the
/// X.509 machine store to sign the tokens issued by OpenIddict.
/// </summary>
/// <param name="builder">The builder used to configure OpenIddict.</param>
/// <param name="thumbprint">The thumbprint of the certificate used to identify it in the X.509 store.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder UseSigningCertificate(
[NotNull] this OpenIddictBuilder builder, [NotNull] string thumbprint) {
if (builder == null) {
throw new ArgumentNullException(nameof(builder));
}
// Register the certificate in the ASOS/OpenIddict options.
builder.Options.SigningCredentials.AddCertificate(thumbprint);
return builder;
}
/// <summary>
/// Registers a <see cref="X509Certificate2"/> retrieved from the
/// given X.509 store to sign the tokens issued by OpenIddict.
/// </summary>
/// <param name="builder">The builder used to configure OpenIddict.</param>
/// <param name="thumbprint">The thumbprint of the certificate used to identify it in the X.509 store.</param>
/// <param name="name">The name of the X.509 store.</param>
/// <param name="location">The location of the X.509 store.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder UseSigningCertificate(
[NotNull] this OpenIddictBuilder builder,
[NotNull] string thumbprint, StoreName name, StoreLocation location) {
if (builder == null) {
throw new ArgumentNullException(nameof(builder));
}
// Register the certificate in the ASOS/OpenIddict options.
builder.Options.SigningCredentials.AddCertificate(thumbprint, name, location);
return builder;
}
}
}

68
src/OpenIddict.Core/OpenIddictHelpers.cs

@ -1,68 +0,0 @@
using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Identity;
namespace OpenIddict {
public static class OpenIddictHelpers {
public static async Task<bool> IsConfidentialApplicationAsync<TUser, TApplication>(
[NotNull] this OpenIddictManager<TUser, TApplication> manager, [NotNull] TApplication application)
where TUser : class
where TApplication : class {
if (manager == null) {
throw new ArgumentNullException(nameof(manager));
}
if (application == null) {
throw new ArgumentNullException(nameof(application));
}
var type = await manager.GetApplicationTypeAsync(application);
return string.Equals(type, OpenIddictConstants.ApplicationTypes.Confidential, StringComparison.OrdinalIgnoreCase);
}
public static async Task<bool> IsPublicApplicationAsync<TUser, TApplication>(
[NotNull] this OpenIddictManager<TUser, TApplication> manager, [NotNull] TApplication application)
where TUser : class
where TApplication : class {
if (manager == null) {
throw new ArgumentNullException(nameof(manager));
}
if (application == null) {
throw new ArgumentNullException(nameof(application));
}
var type = await manager.GetApplicationTypeAsync(application);
return string.Equals(type, OpenIddictConstants.ApplicationTypes.Public, StringComparison.OrdinalIgnoreCase);
}
public static async Task<string> FindClaimAsync<TUser>(
[NotNull] this UserManager<TUser> manager,
[NotNull] TUser user, [NotNull] string type) where TUser : class {
if (manager == null) {
throw new ArgumentNullException(nameof(manager));
}
if (user == null) {
throw new ArgumentNullException(nameof(user));
}
if (string.IsNullOrEmpty(type)) {
throw new ArgumentNullException(nameof(type));
}
// Note: GetClaimsAsync will automatically throw an exception
// if the underlying store doesn't support custom claims.
var claims = await manager.GetClaimsAsync(user);
if (claims.Count != 0) {
return claims[0]?.Value;
}
return null;
}
}
}

195
src/OpenIddict.Core/OpenIddictManager.cs

@ -1,195 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Server;
using CryptoHelper;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace OpenIddict {
public class OpenIddictManager<TUser, TApplication> where TUser : class where TApplication : class {
public OpenIddictManager([NotNull] OpenIddictServices<TUser, TApplication> services) {
Services = services;
}
/// <summary>
/// Gets the HTTP context associated with the current manager.
/// </summary>
protected virtual HttpContext Context => Services.Context;
/// <summary>
/// Gets the cancellation token used to abort async operations.
/// </summary>
protected virtual CancellationToken CancellationToken => Context?.RequestAborted ?? CancellationToken.None;
/// <summary>
/// Gets the logger associated with the current manager.
/// </summary>
protected virtual ILogger Logger => Services.Logger;
/// <summary>
/// Gets the options associated with the current manager.
/// </summary>
protected virtual IdentityOptions Options => Services.Services.GetRequiredService<IOptions<IdentityOptions>>().Value;
/// <summary>
/// Gets the servuces associated with the current manager.
/// </summary>
protected virtual OpenIddictServices<TUser, TApplication> Services { get; }
/// <summary>
/// Gets the store associated with the current manager.
/// </summary>
protected virtual IOpenIddictStore<TUser, TApplication> Store => Services.Store;
public virtual async Task<ClaimsIdentity> CreateIdentityAsync(TUser user, IEnumerable<string> scopes) {
if (user == null) {
throw new ArgumentNullException(nameof(user));
}
if (scopes == null) {
throw new ArgumentNullException(nameof(scopes));
}
var identity = new ClaimsIdentity(
OpenIdConnectServerDefaults.AuthenticationScheme,
Options.ClaimsIdentity.UserNameClaimType,
Options.ClaimsIdentity.RoleClaimType);
// Note: the name identifier is always included in both identity and
// access tokens, even if an explicit destination is not specified.
identity.AddClaim(ClaimTypes.NameIdentifier, await Services.Users.GetUserIdAsync(user));
// Resolve the email address associated with the user if the underlying store supports it.
var email = Services.Users.SupportsUserEmail ? await Services.Users.GetEmailAsync(user) : null;
// Only add the name claim if the "profile" scope was granted.
if (scopes.Contains(OpenIdConnectConstants.Scopes.Profile)) {
var username = await Services.Users.GetUserNameAsync(user);
// Throw an exception if the username corresponds to the registered
// email address and if the "email" scope has not been requested.
if (!scopes.Contains(OpenIdConnectConstants.Scopes.Email) &&
!string.IsNullOrEmpty(email) &&
string.Equals(username, email, StringComparison.OrdinalIgnoreCase)) {
throw new InvalidOperationException("The 'email' scope is required.");
}
identity.AddClaim(ClaimTypes.Name, username,
OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
}
// Only add the email address if the "email" scope was granted.
if (!string.IsNullOrEmpty(email) && scopes.Contains(OpenIdConnectConstants.Scopes.Email)) {
identity.AddClaim(ClaimTypes.Email, email,
OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
}
if (Services.Users.SupportsUserRole && scopes.Contains(OpenIddictConstants.Scopes.Roles)) {
foreach (var role in await Services.Users.GetRolesAsync(user)) {
identity.AddClaim(identity.RoleClaimType, role,
OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
}
}
if (Services.Users.SupportsUserSecurityStamp) {
var identifier = await Services.Users.GetSecurityStampAsync(user);
if (!string.IsNullOrEmpty(identifier)) {
identity.AddClaim(Options.ClaimsIdentity.SecurityStampClaimType, identifier,
OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
}
}
return identity;
}
public virtual Task<TApplication> FindApplicationByIdAsync(string identifier) {
return Store.FindApplicationByIdAsync(identifier, CancellationToken);
}
public virtual Task<TApplication> FindApplicationByLogoutRedirectUri(string url) {
return Store.FindApplicationByLogoutRedirectUri(url, CancellationToken);
}
public virtual async Task<string> GetApplicationTypeAsync(TApplication application) {
if (application == null) {
throw new ArgumentNullException(nameof(application));
}
var type = await Store.GetApplicationTypeAsync(application, CancellationToken);
// Ensure the application type returned by the store is supported by the manager.
if (!string.Equals(type, OpenIddictConstants.ApplicationTypes.Confidential, StringComparison.OrdinalIgnoreCase) &&
!string.Equals(type, OpenIddictConstants.ApplicationTypes.Public, StringComparison.OrdinalIgnoreCase)) {
throw new InvalidOperationException("Only 'confidential' or 'public' applications are " +
"supported by the default OpenIddict manager.");
}
return type;
}
public virtual Task<string> GetDisplayNameAsync(TApplication application) {
if (application == null) {
throw new ArgumentNullException(nameof(application));
}
return Store.GetDisplayNameAsync(application, CancellationToken);
}
public virtual async Task<bool> ValidateRedirectUriAsync(TApplication application, string address) {
if (application == null) {
throw new ArgumentNullException(nameof(application));
}
if (!string.Equals(address, await Store.GetRedirectUriAsync(application, CancellationToken), StringComparison.Ordinal)) {
Logger.LogWarning("Client validation failed because {RedirectUri} was not a valid redirect_uri " +
"for {Client}", address, await GetDisplayNameAsync(application));
return false;
}
return true;
}
public virtual async Task<bool> ValidateSecretAsync(TApplication application, string secret) {
if (application == null) {
throw new ArgumentNullException(nameof(application));
}
if (!await this.IsConfidentialApplicationAsync(application)) {
Logger.LogWarning("Client authentication cannot be enforced for non-confidential applications.");
return false;
}
var hash = await Store.GetHashedSecretAsync(application, CancellationToken);
if (string.IsNullOrEmpty(hash)) {
Logger.LogError("Client authentication failed for {Client} because " +
"no client secret was associated with the application.");
return false;
}
if (!Crypto.VerifyHashedPassword(hash, secret)) {
Logger.LogWarning("Client authentication failed for {Client}.", await GetDisplayNameAsync(application));
return false;
}
return true;
}
}
}

47
src/OpenIddict.Core/OpenIddictModule.cs

@ -1,25 +1,56 @@
using System;
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Diagnostics;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Builder;
namespace OpenIddict {
/// <summary>
/// Defines an OpenIddict module.
/// Represents an OpenIddict module.
/// </summary>
[DebuggerDisplay("{Name,nq}")]
public class OpenIddictModule {
/// <summary>
/// Initializes a new OpenIddict module.
/// </summary>
/// <param name="name">The name of the module.</param>
/// <param name="position">The position of the module in the ASP.NET Core pipeline.</param>
/// <param name="registration">The delegate used to register the module in the pipeline.</param>
public OpenIddictModule(
[NotNull] string name, int position,
[NotNull] Action<IApplicationBuilder> registration) {
Name = name;
Position = position;
Registration = registration;
}
/// <summary>
/// Initializes a new OpenIddict module.
/// </summary>
/// <param name="registration">The delegate used to register the module in the pipeline.</param>
public OpenIddictModule([NotNull] Action<IApplicationBuilder> registration) {
Registration = registration;
}
/// <summary>
/// Gets or sets the name of the module.
/// </summary>
public string Name { get; set; }
public string Name { get; }
/// <summary>
/// Gets or sets the position of the module in the ASP.NET pipeline.
/// Gets or sets the position of the module in the ASP.NET Core pipeline.
/// </summary>
public int Position { get; set; }
public int Position { get; }
/// <summary>
/// Gets or sets the delegate used to register
/// the OpenIddict module in the ASP.NET pipeline.
/// Gets or sets the delegate used to register the
/// OpenIddict module in the ASP.NET Core pipeline.
/// </summary>
public Action<IApplicationBuilder> Registration { get; set; }
public Action<IApplicationBuilder> Registration { get; }
}
}

18
src/OpenIddict.Core/OpenIddictOptions.cs

@ -4,12 +4,28 @@
* the license and the contributors participating to this project.
*/
using System;
using System.Collections.Generic;
using AspNet.Security.OpenIdConnect.Server;
using Microsoft.AspNetCore.Http;
namespace OpenIddict {
/// <summary>
/// Provides various settings needed to configure OpenIddict.
/// </summary>
public class OpenIddictOptions : OpenIdConnectServerOptions {
public OpenIddictOptions() {
ApplicationCanDisplayErrors = true;
// By default, disable the authorization and logout endpoints.
AuthorizationEndpointPath = LogoutEndpointPath = PathString.Empty;
// Use the same lifespan as the default security stamp
// verification interval used by ASP.NET Core Identity.
AccessTokenLifetime = TimeSpan.FromMinutes(30);
}
/// <summary>
/// Gets the list of the OpenIddict modules registered in the application.
/// </summary>
public ICollection<OpenIddictModule> Modules { get; } = new List<OpenIddictModule>();
}
}

72
src/OpenIddict.Core/Stores/IOpenIddictApplicationStore.cs

@ -0,0 +1,72 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System.Threading;
using System.Threading.Tasks;
namespace OpenIddict {
/// <summary>
/// Provides methods allowing to manage the applications stored in a database.
/// </summary>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
public interface IOpenIddictApplicationStore<TApplication> where TApplication : class {
/// <summary>
/// Creates a new application.
/// </summary>
/// <param name="application">The application to create.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
Task CreateAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Retrieves an application using its unique identifier.
/// </summary>
/// <param name="identifier">The unique identifier associated with the application.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
Task<TApplication> FindByIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves an application using its post_logout_redirect_uri.
/// </summary>
/// <param name="url">The post_logout_redirect_uri associated with the application.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
Task<TApplication> FindByLogoutRedirectUri(string url, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the client type associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
Task<string> GetClientTypeAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the display name associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
Task<string> GetDisplayNameAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the callback address associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
Task<string> GetRedirectUriAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the hashed secret associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
Task<string> GetHashedSecretAsync(TApplication application, CancellationToken cancellationToken);
}
}

13
src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs

@ -0,0 +1,13 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
namespace OpenIddict {
/// <summary>
/// Provides methods allowing to manage the authorizations stored in a database.
/// </summary>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
public interface IOpenIddictAuthorizationStore<TAuthorization> where TAuthorization : class { }
}

13
src/OpenIddict.Core/Stores/IOpenIddictScopeStore.cs

@ -0,0 +1,13 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
namespace OpenIddict {
/// <summary>
/// Provides methods allowing to manage the scopes stored in a database.
/// </summary>
/// <typeparam name="TScope">The type of the Scope entity.</typeparam>
public interface IOpenIddictScopeStore<TScope> where TScope : class { }
}

41
src/OpenIddict.Core/Stores/IOpenIddictTokenStore.cs

@ -0,0 +1,41 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System.Threading;
using System.Threading.Tasks;
namespace OpenIddict {
/// <summary>
/// Provides methods allowing to manage the tokens stored in a database.
/// </summary>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
public interface IOpenIddictTokenStore<TToken> where TToken : class {
/// <summary>
/// Creates a new token, defined by a unique identifier and a token type.
/// </summary>
/// <param name="identifier">The unique identifier associated with the token to create.</param>
/// <param name="type">The token type.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
Task CreateAsync(string identifier, string type, CancellationToken cancellationToken);
/// <summary>
/// Retrieves an token using its unique identifier.
/// </summary>
/// <param name="identifier">The unique identifier associated with the token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
Task<TToken> FindByIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Revokes a token.
/// </summary>
/// <param name="token">The token to revoke.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
Task RevokeAsync(TToken token, CancellationToken cancellationToken);
}
}

1
src/OpenIddict.Core/project.json

@ -37,7 +37,6 @@
"CryptoHelper": "1.0.0-rc2-final",
"JetBrains.Annotations": { "type": "build", "version": "10.1.4" },
"Microsoft.AspNetCore.Identity": "1.0.0-rc2-final",
"Microsoft.Extensions.Configuration": "1.0.0-rc2-final",
"Microsoft.Extensions.Caching.Memory": "1.0.0-rc2-final"
},

39
src/OpenIddict.EF/OpenIddictContext.cs

@ -1,39 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using OpenIddict.Models;
namespace OpenIddict {
public class OpenIddictContext<TUser, TApplication, TRole, TKey> : IdentityDbContext<TUser, TRole, TKey>
where TUser : IdentityUser<TKey>
where TApplication : Application<TKey>
where TRole : IdentityRole<TKey>
where TKey : IEquatable<TKey> {
protected OpenIddictContext() { }
public OpenIddictContext(DbContextOptions options)
: base(options) { }
public DbSet<TApplication> Applications { get; set; }
}
public class OpenIddictContext<TUser> : OpenIddictContext<TUser, Application, IdentityRole, string> where TUser : IdentityUser {
protected OpenIddictContext() { }
public OpenIddictContext(DbContextOptions options)
: base(options) { }
}
public class OpenIddictContext : OpenIddictContext<IdentityUser> {
protected OpenIddictContext() { }
public OpenIddictContext(DbContextOptions options)
: base(options) { }
}
}

117
src/OpenIddict.EF/OpenIddictExtensions.cs

@ -1,117 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Linq;
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using OpenIddict;
using OpenIddict.Models;
namespace Microsoft.AspNetCore.Builder {
public static class OpenIddictExtensions {
public static OpenIddictConfiguration UseEntityFramework([NotNull] this OpenIddictConfiguration configuration) {
if (configuration == null) {
throw new ArgumentNullException(nameof(configuration));
}
if (!IsSubclassOf(configuration.ApplicationType, typeof(Application<>))) {
throw new InvalidOperationException("The default store cannot be used with application " +
"entities that are not derived from Application<TKey>.");
}
configuration.Services.AddScoped(
typeof(IOpenIddictStore<,>).MakeGenericType(configuration.UserType, configuration.ApplicationType),
typeof(OpenIddictStore<,,,>).MakeGenericType(
/* TUser: */ configuration.UserType,
/* TApplication: */ configuration.ApplicationType,
/* TContext: */ ResolveContextType(configuration),
/* TKey: */ ResolveKeyType(configuration)));
return configuration;
}
private static Type ResolveContextType([NotNull] OpenIddictConfiguration configuration) {
var service = (from registration in configuration.Services
where registration.ServiceType.IsConstructedGenericType
let definition = registration.ServiceType.GetGenericTypeDefinition()
where definition == typeof(IUserStore<>)
select registration.ImplementationType).FirstOrDefault();
if (service == null) {
throw new InvalidOperationException(
"The type of the database context cannot be automatically inferred. " +
"Make sure 'AddOpenIddict()' is the last chained call when configuring the services.");
}
TypeInfo type;
for (type = service.GetTypeInfo(); type != null; type = type.BaseType?.GetTypeInfo()) {
if (!type.IsGenericType) {
continue;
}
var definition = type.GetGenericTypeDefinition();
if (definition == null) {
continue;
}
if (definition != typeof(UserStore<,,,>)) {
continue;
}
return (from argument in type.AsType().GetGenericArguments()
where typeof(DbContext).IsAssignableFrom(argument)
select argument).Single();
}
throw new InvalidOperationException("The type of the database context cannot be automatically inferred.");
}
private static Type ResolveKeyType([NotNull] OpenIddictConfiguration configuration) {
TypeInfo type;
for (type = configuration.UserType.GetTypeInfo(); type != null; type = type.BaseType?.GetTypeInfo()) {
if (!type.IsGenericType) {
continue;
}
var definition = type.GetGenericTypeDefinition();
if (definition == null) {
continue;
}
if (definition != typeof(IdentityUser<>)) {
continue;
}
return type.AsType().GetGenericArguments().Single();
}
throw new InvalidOperationException(
"The type of the key identifier used by the user " +
$"entity '{configuration.UserType}' cannot be automatically inferred.");
}
private static bool IsSubclassOf([NotNull] Type type, [NotNull] Type generic) {
while (type != null && type != typeof(object)) {
var current = type.GetTypeInfo().IsGenericType ?
type.GetGenericTypeDefinition() :
type;
if (current == generic) {
return true;
}
type = type.GetTypeInfo().BaseType;
}
return false;
}
}
}

78
src/OpenIddict.EF/OpenIddictStore.cs

@ -1,78 +0,0 @@
using System;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using OpenIddict.Models;
namespace OpenIddict {
public class OpenIddictStore<TUser, TApplication, TContext, TKey> : IOpenIddictStore<TUser, TApplication>
where TUser : IdentityUser<TKey>
where TApplication : Application<TKey>
where TContext : DbContext
where TKey : IEquatable<TKey> {
public OpenIddictStore(TContext context) {
Context = context;
}
/// <summary>
/// Gets the database context associated with the current store.
/// </summary>
public virtual TContext Context { get; }
public DbSet<TApplication> Applications {
get { return Context.Set<TApplication>(); }
}
public virtual Task<TApplication> FindApplicationByIdAsync(string identifier, CancellationToken cancellationToken) {
var converter = TypeDescriptor.GetConverter(typeof(TKey));
// If the string key cannot be converted to TKey, return null
// to indicate that the requested application doesn't exist.
if (!converter.CanConvertFrom(typeof(string))) {
return Task.FromResult<TApplication>(null);
}
var key = (TKey) converter.ConvertFromInvariantString(identifier);
return Applications.SingleOrDefaultAsync(application => application.Id.Equals(key), cancellationToken);
}
public virtual Task<TApplication> FindApplicationByLogoutRedirectUri(string url, CancellationToken cancellationToken) {
return Applications.SingleOrDefaultAsync(application => application.LogoutRedirectUri == url, cancellationToken);
}
public virtual Task<string> GetApplicationTypeAsync(TApplication application, CancellationToken cancellationToken) {
if (application == null) {
throw new ArgumentNullException(nameof(application));
}
return Task.FromResult(application.Type);
}
public virtual Task<string> GetDisplayNameAsync(TApplication application, CancellationToken cancellationToken) {
if (application == null) {
throw new ArgumentNullException(nameof(application));
}
return Task.FromResult(application.DisplayName);
}
public virtual Task<string> GetRedirectUriAsync(TApplication application, CancellationToken cancellationToken) {
if (application == null) {
throw new ArgumentNullException(nameof(application));
}
return Task.FromResult(application.RedirectUri);
}
public virtual Task<string> GetHashedSecretAsync(TApplication application, CancellationToken cancellationToken) {
if (application == null) {
throw new ArgumentNullException(nameof(application));
}
return Task.FromResult(application.Secret);
}
}
}

60
src/OpenIddict.EntityFramework/Models/OpenIddictApplication.cs

@ -0,0 +1,60 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
namespace OpenIddict {
/// <summary>
/// Represents an OpenIddict application.
/// </summary>
public class OpenIddictApplication : OpenIddictApplication<string> {
public OpenIddictApplication() {
// Generate a new string identifier.
Id = Guid.NewGuid().ToString();
}
}
/// <summary>
/// Represents an OpenIddict application.
/// </summary>
public class OpenIddictApplication<TKey> where TKey : IEquatable<TKey> {
/// <summary>
/// Gets or sets the display name
/// associated with the current application.
/// </summary>
public virtual string DisplayName { get; set; }
/// <summary>
/// Gets or sets the unique identifier
/// associated with the current application.
/// </summary>
public virtual TKey Id { get; set; }
/// <summary>
/// Gets or sets the logout callback URL
/// associated with the current application.
/// </summary>
public virtual string LogoutRedirectUri { get; set; }
/// <summary>
/// Gets or sets the callback URL
/// associated with the current application.
/// </summary>
public virtual string RedirectUri { get; set; }
/// <summary>
/// Gets or sets the hashed secret
/// associated with the current application.
/// </summary>
public virtual string Secret { get; set; }
/// <summary>
/// Gets or sets the application type
/// associated with the current application.
/// </summary>
public virtual string Type { get; set; }
}
}

54
src/OpenIddict.EntityFramework/Models/OpenIddictAuthorization.cs

@ -0,0 +1,54 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Collections.Generic;
namespace OpenIddict {
/// <summary>
/// Represents an OpenIddict authorization.
/// </summary>
public class OpenIddictAuthorization : OpenIddictAuthorization<OpenIddictToken> { }
/// <summary>
/// Represents an OpenIddict authorization.
/// </summary>
public class OpenIddictAuthorization<TToken> : OpenIddictAuthorization<TToken, string> {
public OpenIddictAuthorization() {
// Generate a new string identifier.
Id = Guid.NewGuid().ToString();
}
}
/// <summary>
/// Represents an OpenIddict authorization.
/// </summary>
public class OpenIddictAuthorization<TToken, TKey> where TKey : IEquatable<TKey> {
/// <summary>
/// Gets or sets the unique identifier
/// associated with the current authorization.
/// </summary>
public virtual TKey Id { get; set; }
/// <summary>
/// Gets or sets the space-delimited scopes
/// associated with the current authorization.
/// </summary>
public virtual string Scope { get; set; }
/// <summary>
/// Gets or sets the list of tokens
/// associated with the current authorization.
/// </summary>
public virtual IList<TToken> Tokens { get; } = new List<TToken>();
/// <summary>
/// Gets or sets the identifier of the user profile
/// associated with the current authorization.
/// </summary>
public virtual TKey UserId { get; set; }
}
}

31
src/OpenIddict.EntityFramework/Models/OpenIddictScope.cs

@ -0,0 +1,31 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
namespace OpenIddict {
/// <summary>
/// Represents an OpenIddict scope.
/// </summary>
public class OpenIddictScope : OpenIddictScope<string> { }
/// <summary>
/// Represents an OpenIddict scope.
/// </summary>
public class OpenIddictScope<TKey> where TKey : IEquatable<TKey> {
/// <summary>
/// Gets or sets the public description
/// associated with the current scope.
/// </summary>
public virtual string Description { get; set; }
/// <summary>
/// Gets or sets the unique identifier
/// associated with the current scope.
/// </summary>
public virtual TKey Id { get; set; }
}
}

48
src/OpenIddict.EntityFramework/Models/OpenIddictToken.cs

@ -0,0 +1,48 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
namespace OpenIddict {
/// <summary>
/// Represents an OpenIddict token.
/// </summary>
public class OpenIddictToken : OpenIddictToken<string> {
public OpenIddictToken() {
// Generate a new string identifier.
Id = Guid.NewGuid().ToString();
}
}
/// <summary>
/// Represents an OpenIddict token.
/// </summary>
public class OpenIddictToken<TKey> where TKey : IEquatable<TKey> {
/// <summary>
/// Gets or sets the identifier of the authorization attached with the current token.
/// This property may be null if the token was issued without
/// requiring the user consent or is bound to a client application.
/// </summary>
public virtual TKey AuthorizationId { get; set; }
/// <summary>
/// Gets or sets the unique identifier
/// associated with the current token.
/// </summary>
public virtual TKey Id { get; set; }
/// <summary>
/// Gets or sets the type of the current token.
/// </summary>
public virtual string Type { get; set; }
/// <summary>
/// Gets or sets the identifier of the user attached with the current token.
/// This property is null if the token represents a client application.
/// </summary>
public virtual TKey UserId { get; set; }
}
}

41
src/OpenIddict.EntityFramework/Models/OpenIddictUser.cs

@ -0,0 +1,41 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
namespace OpenIddict {
/// <summary>
/// Represents an OpenIddict user.
/// </summary>
public class OpenIddictUser : OpenIddictUser<OpenIddictAuthorization, OpenIddictToken> { }
/// <summary>
/// Represents an OpenIddict user.
/// </summary>
public class OpenIddictUser<TAuthorization, TToken> : OpenIddictUser<TAuthorization, TToken, string> {
public OpenIddictUser() {
// Generate a new string identifier.
Id = Guid.NewGuid().ToString();
}
}
/// <summary>
/// Represents an OpenIddict user.
/// </summary>
public class OpenIddictUser<TAuthorization, TToken, TKey> : IdentityUser<TKey> where TKey : IEquatable<TKey> {
/// <summary>
/// Gets the list of the authorizations associated with this user profile.
/// </summary>
public virtual IList<TAuthorization> Authorizations { get; } = new List<TAuthorization>();
/// <summary>
/// Gets the list of the tokens associated with this user profile.
/// </summary>
public virtual IList<TToken> Tokens { get; } = new List<TToken>();
}
}

0
src/OpenIddict.EF/OpenIddict.EF.xproj → src/OpenIddict.EntityFramework/OpenIddict.EntityFramework.xproj

152
src/OpenIddict.EntityFramework/OpenIddictContext.cs

@ -0,0 +1,152 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace OpenIddict {
/// <summary>
/// Represents an OpenIddict-powered Entity Framework context.
/// </summary>
public class OpenIddictContext : OpenIddictContext<OpenIddictUser> {
/// <summary>
/// Initializes a new OpenIddict context without configuring the Entity Framework options.
/// </summary>
protected OpenIddictContext() { }
/// <summary>
/// Initializes a new OpenIddict context.
/// </summary>
/// <param name="options">The options used to configure the Entity Framework context.</param>
public OpenIddictContext(DbContextOptions options) : base(options) { }
}
/// <summary>
/// Represents an OpenIddict-powered Entity Framework context.
/// </summary>
/// <typeparam name="TUser">The type of the User entity.</typeparam>
public class OpenIddictContext<TUser> : OpenIddictContext<TUser, OpenIddictApplication,
OpenIddictAuthorization,
OpenIddictScope,
OpenIddictToken, IdentityRole, string>
where TUser : OpenIddictUser {
/// <summary>
/// Initializes a new OpenIddict context without configuring the Entity Framework options.
/// </summary>
protected OpenIddictContext() { }
/// <summary>
/// Initializes a new OpenIddict context.
/// </summary>
/// <param name="options">The options used to configure the Entity Framework context.</param>
public OpenIddictContext(DbContextOptions options) : base(options) { }
}
/// <summary>
/// Represents an OpenIddict-powered Entity Framework context.
/// </summary>
/// <typeparam name="TUser">The type of the User entity.</typeparam>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <typeparam name="TScope">The type of the Scope entity.</typeparam>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <typeparam name="TRole">The type of the Role entity.</typeparam>
/// <typeparam name="TKey">The type of the primary key used by the Identity/OpenIddict entities.</typeparam>
public class OpenIddictContext<TUser, TApplication, TAuthorization, TScope, TToken, TRole, TKey> : IdentityDbContext<TUser, TRole, TKey>
where TUser : OpenIddictUser<TAuthorization, TToken, TKey>
where TApplication : OpenIddictApplication<TKey>
where TAuthorization : OpenIddictAuthorization<TToken, TKey>
where TScope : OpenIddictScope<TKey>
where TToken : OpenIddictToken<TKey>
where TRole : IdentityRole<TKey>
where TKey : IEquatable<TKey> {
/// <summary>
/// Initializes a new OpenIddict context without configuring the Entity Framework options.
/// </summary>
protected OpenIddictContext() { }
/// <summary>
/// Initializes a new OpenIddict context.
/// </summary>
/// <param name="options">The options used to configure the Entity Framework context.</param>
public OpenIddictContext(DbContextOptions options) : base(options) { }
/// <summary>
/// Gets or sets the database set containing the applications.
/// </summary>
public DbSet<TApplication> Applications { get; set; }
/// <summary>
/// Gets or sets the database set containing the authorizations.
/// </summary>
public DbSet<TAuthorization> Authorizations { get; set; }
/// <summary>
/// Gets or sets the database set containing the scopes.
/// </summary>
public DbSet<TScope> Scopes { get; set; }
/// <summary>
/// Gets or sets the database set containing the tokens.
/// </summary>
public DbSet<TToken> Tokens { get; set; }
/// <summary>
/// Registers the OpenIddict entities in the Entity Framework context.
/// </summary>
/// <param name="builder">The model builder used by Entity Framework.</param>
protected override void OnModelCreating(ModelBuilder builder) {
base.OnModelCreating(builder);
// Configure the TApplication entity.
builder.Entity<TApplication>(entity => {
entity.HasKey(application => application.Id);
entity.ToTable("OpenIddictApplications");
});
// Configure the TAuthorization entity.
builder.Entity<TAuthorization>(entity => {
entity.HasKey(authorization => authorization.Id);
entity.HasMany(authorization => authorization.Tokens)
.WithOne()
.HasForeignKey(token => token.AuthorizationId)
.IsRequired(required: false);
entity.ToTable("OpenIddictAuthorizations");
});
// Configure the TScope entity.
builder.Entity<TScope>(entity => {
entity.HasKey(scope => scope.Id);
entity.ToTable("OpenIddictScopes");
});
// Configure the TToken entity.
builder.Entity<TToken>(entity => {
entity.HasKey(token => token.Id);
entity.ToTable("OpenIddictTokens");
});
// Configure the TUser entity.
builder.Entity<TUser>(entity => {
entity.HasMany(user => user.Authorizations)
.WithOne()
.HasForeignKey(authorization => authorization.UserId)
.IsRequired(required: false);
entity.HasMany(user => user.Tokens)
.WithOne()
.HasForeignKey(token => token.UserId)
.IsRequired(required: false);
});
}
}
}

79
src/OpenIddict.EntityFramework/OpenIddictExtensions.cs

@ -0,0 +1,79 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Diagnostics;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection.Extensions;
using OpenIddict;
namespace Microsoft.AspNetCore.Builder {
public static class OpenIddictExtensions {
/// <summary>
/// Registers the Entity Framework stores.
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddEntityFramework<TContext>([NotNull] this OpenIddictBuilder builder)
where TContext : DbContext {
return builder.AddEntityFramework<TContext, string>();
}
/// <summary>
/// Registers the Entity Framework stores.
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddEntityFramework<TContext, TKey>([NotNull] this OpenIddictBuilder builder)
where TContext : DbContext
where TKey : IEquatable<TKey> {
if (builder == null) {
throw new ArgumentNullException(nameof(builder));
}
Debug.Assert(builder.ApplicationType != null &&
builder.AuthorizationType != null &&
builder.ScopeType != null &&
builder.TokenType != null, "The entity types exposed by OpenIddictBuilder shouldn't be null.");
// Register the application store in the DI container.
builder.Services.TryAddScoped(
typeof(IOpenIddictApplicationStore<>).MakeGenericType(builder.ApplicationType),
typeof(OpenIddictApplicationStore<,,>).MakeGenericType(
/* TApplication: */ builder.ApplicationType,
/* TContext: */ typeof(TContext),
/* TKey: */ typeof(TKey)));
// Register the authorization store in the DI container.
builder.Services.TryAddScoped(
typeof(IOpenIddictAuthorizationStore<>).MakeGenericType(builder.AuthorizationType),
typeof(OpenIddictAuthorizationStore<,,,>).MakeGenericType(
/* TAuthorization: */ builder.AuthorizationType,
/* TToken: */ builder.TokenType,
/* TContext: */ typeof(TContext),
/* TKey: */ typeof(TKey)));
// Register the scope store in the DI container.
builder.Services.TryAddScoped(
typeof(IOpenIddictScopeStore<>).MakeGenericType(builder.ScopeType),
typeof(OpenIddictScopeStore<,,>).MakeGenericType(
/* TScope: */ builder.ScopeType,
/* TContext: */ typeof(TContext),
/* TKey: */ typeof(TKey)));
// Register the token store in the DI container.
builder.Services.TryAddScoped(
typeof(IOpenIddictTokenStore<>).MakeGenericType(builder.TokenType),
typeof(OpenIddictTokenStore<,,>).MakeGenericType(
/* TToken: */ builder.TokenType,
/* TContext: */ typeof(TContext),
/* TKey: */ typeof(TKey)));
return builder;
}
}
}

140
src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs

@ -0,0 +1,140 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
namespace OpenIddict {
/// <summary>
/// Provides methods allowing to manage the applications stored in a database.
/// </summary>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <typeparam name="TContext">The type of the Entity Framework database context.</typeparam>
/// <typeparam name="TKey">The type of the entity primary keys.</typeparam>
public class OpenIddictApplicationStore<TApplication, TContext, TKey> : IOpenIddictApplicationStore<TApplication>
where TApplication : OpenIddictApplication<TKey>
where TContext : DbContext
where TKey : IEquatable<TKey> {
public OpenIddictApplicationStore(TContext context) {
Context = context;
}
/// <summary>
/// Gets the database context associated with the current store.
/// </summary>
protected virtual TContext Context { get; }
/// <summary>
/// Gets the database set corresponding to the <typeparamref name="TApplication"/> entity.
/// </summary>
protected DbSet<TApplication> Applications => Context.Set<TApplication>();
/// <summary>
/// Creates a new application.
/// </summary>
/// <param name="application">The application to create.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public virtual Task CreateAsync(TApplication application, CancellationToken cancellationToken) {
if (application == null) {
throw new ArgumentNullException(nameof(application));
}
Context.Add(application);
return Context.SaveChangesAsync(cancellationToken);
}
/// <summary>
/// Retrieves an application using its unique identifier.
/// </summary>
/// <param name="identifier">The unique identifier associated with the application.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public virtual Task<TApplication> FindByIdAsync(string identifier, CancellationToken cancellationToken) {
var converter = TypeDescriptor.GetConverter(typeof(TKey));
// If the string key cannot be converted to TKey, return null
// to indicate that the requested application doesn't exist.
if (!converter.CanConvertFrom(typeof(string))) {
return Task.FromResult<TApplication>(null);
}
var key = (TKey) converter.ConvertFromInvariantString(identifier);
return Applications.SingleOrDefaultAsync(application => application.Id.Equals(key), cancellationToken);
}
/// <summary>
/// Retrieves an application using its post_logout_redirect_uri.
/// </summary>
/// <param name="url">The post_logout_redirect_uri associated with the application.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public virtual Task<TApplication> FindByLogoutRedirectUri(string url, CancellationToken cancellationToken) {
return Applications.SingleOrDefaultAsync(application => application.LogoutRedirectUri == url, cancellationToken);
}
/// <summary>
/// Retrieves the client type associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public virtual Task<string> GetClientTypeAsync(TApplication application, CancellationToken cancellationToken) {
if (application == null) {
throw new ArgumentNullException(nameof(application));
}
return Task.FromResult(application.Type);
}
/// <summary>
/// Retrieves the display name associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public virtual Task<string> GetDisplayNameAsync(TApplication application, CancellationToken cancellationToken) {
if (application == null) {
throw new ArgumentNullException(nameof(application));
}
return Task.FromResult(application.DisplayName);
}
/// <summary>
/// Retrieves the callback address associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public virtual Task<string> GetRedirectUriAsync(TApplication application, CancellationToken cancellationToken) {
if (application == null) {
throw new ArgumentNullException(nameof(application));
}
return Task.FromResult(application.RedirectUri);
}
/// <summary>
/// Retrieves the hashed secret associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public virtual Task<string> GetHashedSecretAsync(TApplication application, CancellationToken cancellationToken) {
if (application == null) {
throw new ArgumentNullException(nameof(application));
}
return Task.FromResult(application.Secret);
}
}
}

37
src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs

@ -0,0 +1,37 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using Microsoft.EntityFrameworkCore;
namespace OpenIddict {
/// <summary>
/// Provides methods allowing to manage the authorizations stored in a database.
/// </summary>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <typeparam name="TContext">The type of the Entity Framework database context.</typeparam>
/// <typeparam name="TKey">The type of the entity primary keys.</typeparam>
public class OpenIddictAuthorizationStore<TAuthorization, TToken, TContext, TKey> : IOpenIddictAuthorizationStore<TAuthorization>
where TAuthorization : OpenIddictAuthorization<TToken, TKey>
where TToken : OpenIddictToken<TKey>
where TContext : DbContext
where TKey : IEquatable<TKey> {
public OpenIddictAuthorizationStore(TContext context) {
Context = context;
}
/// <summary>
/// Gets the database context associated with the current store.
/// </summary>
protected virtual TContext Context { get; }
/// <summary>
/// Gets the database set corresponding to the <typeparamref name="TAuthorization"/> entity.
/// </summary>
protected DbSet<TAuthorization> Authorizations => Context.Set<TAuthorization>();
}
}

35
src/OpenIddict.EntityFramework/Stores/OpenIddictScopeStore.cs

@ -0,0 +1,35 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using Microsoft.EntityFrameworkCore;
namespace OpenIddict {
/// <summary>
/// Provides methods allowing to manage the scopes stored in a database.
/// </summary>
/// <typeparam name="TScope">The type of the Scope entity.</typeparam>
/// <typeparam name="TContext">The type of the Entity Framework database context.</typeparam>
/// <typeparam name="TKey">The type of the entity primary keys.</typeparam>
public class OpenIddictScopeStore<TScope, TContext, TKey> : IOpenIddictScopeStore<TScope>
where TScope : OpenIddictScope<TKey>
where TContext : DbContext
where TKey : IEquatable<TKey> {
public OpenIddictScopeStore(TContext context) {
Context = context;
}
/// <summary>
/// Gets the database context associated with the current store.
/// </summary>
protected virtual TContext Context { get; }
/// <summary>
/// Gets the database set corresponding to the <typeparamref name="TScope"/> entity.
/// </summary>
protected DbSet<TScope> Authorizations => Context.Set<TScope>();
}
}

97
src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs

@ -0,0 +1,97 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
namespace OpenIddict {
/// <summary>
/// Provides methods allowing to manage the tokens stored in a database.
/// </summary>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <typeparam name="TContext">The type of the Entity Framework database context.</typeparam>
/// <typeparam name="TKey">The type of the entity primary keys.</typeparam>
public class OpenIddictTokenStore<TToken, TContext, TKey> : IOpenIddictTokenStore<TToken>
where TToken : OpenIddictToken<TKey>, new()
where TContext : DbContext
where TKey : IEquatable<TKey> {
public OpenIddictTokenStore(TContext context) {
Context = context;
}
/// <summary>
/// Gets the database context associated with the current store.
/// </summary>
protected virtual TContext Context { get; }
/// <summary>
/// Gets the database set corresponding to the <typeparamref name="TToken"/> entity.
/// </summary>
protected DbSet<TToken> Tokens => Context.Set<TToken>();
/// <summary>
/// Creates a new token, defined by a unique identifier and a token type.
/// </summary>
/// <param name="identifier">The unique identifier associated with the token to create.</param>
/// <param name="type">The token type.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public virtual Task CreateAsync(string identifier, string type, CancellationToken cancellationToken) {
var converter = TypeDescriptor.GetConverter(typeof(TKey));
// Ensure that the key type is compatible with string keys.
if (!converter.CanConvertFrom(typeof(string))) {
return Task.FromResult(0);
}
var key = (TKey) converter.ConvertFromInvariantString(identifier);
var token = new TToken { Id = key, Type = type };
Tokens.Add(token);
return Context.SaveChangesAsync(cancellationToken);
}
/// <summary>
/// Retrieves an token using its unique identifier.
/// </summary>
/// <param name="identifier">The unique identifier associated with the token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public virtual Task<TToken> FindByIdAsync(string identifier, CancellationToken cancellationToken) {
var converter = TypeDescriptor.GetConverter(typeof(TKey));
// If the string key cannot be converted to TKey, return null
// to indicate that the requested token doesn't exist.
if (!converter.CanConvertFrom(typeof(string))) {
return Task.FromResult<TToken>(null);
}
var key = (TKey) converter.ConvertFromInvariantString(identifier);
return Tokens.SingleOrDefaultAsync(token => token.Id.Equals(key), cancellationToken);
}
/// <summary>
/// Revokes a token.
/// </summary>
/// <param name="token">The token to revoke.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public virtual Task RevokeAsync(TToken token, CancellationToken cancellationToken) {
if (token == null) {
throw new ArgumentNullException(nameof(token));
}
Context.Remove(token);
return Context.SaveChangesAsync(cancellationToken);
}
}
}

3
src/OpenIddict.EF/project.json → src/OpenIddict.EntityFramework/project.json

@ -35,8 +35,7 @@
"dependencies": {
"JetBrains.Annotations": { "type": "build", "version": "10.1.4" },
"Microsoft.AspNetCore.Identity.EntityFrameworkCore": "1.0.0-rc2-final",
"OpenIddict.Core": { "target": "project" },
"OpenIddict.Models": { "target": "project" }
"OpenIddict.Core": { "target": "project" }
},
"frameworks": {

20
src/OpenIddict.Models/Application.cs

@ -1,20 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
namespace OpenIddict.Models {
public class Application : Application<string> { }
public class Application<TKey> where TKey : IEquatable<TKey> {
public TKey Id { get; set; }
public string DisplayName { get; set; }
public string RedirectUri { get; set; }
public string LogoutRedirectUri { get; set; }
public string Secret { get; set; }
public string Type { get; set; }
}
}

18
src/OpenIddict.Models/OpenIddict.Models.xproj

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>79ae02c3-2ab4-4495-bedd-685a714ea51c</ProjectGuid>
<RootNamespace>OpenIddict.Models</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

49
src/OpenIddict.Models/project.json

@ -1,49 +0,0 @@
{
"version": "1.0.0-alpha2-*",
"description": "Contains the default models used by OpenIddict.",
"authors": [ "Kévin Chalet" ],
"packOptions": {
"owners": [ "Kévin Chalet" ],
"projectUrl": "https://github.com/openiddict/openiddict-core",
"iconUrl": "https://avatars3.githubusercontent.com/u/13908567?s=64",
"licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.html",
"repository": {
"type": "git",
"url": "git://github.com/openiddict/openiddict-core"
},
"tags": [
"aspnetcore",
"authentication",
"jwt",
"openidconnect",
"openiddict",
"security"
]
},
"buildOptions": {
"warningsAsErrors": true,
"nowarn": [ "CS1591" ],
"xmlDoc": true
},
"frameworks": {
"net451": { },
"netstandard1.3": {
"dependencies": {
"System.Runtime": "4.1.0-rc2-24027"
},
"imports": [
"dotnet5.4",
"portable-net451+win8"
]
}
}
}

51
src/OpenIddict.Mvc/OpenIddictController.cs

@ -10,30 +10,23 @@ using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace OpenIddict.Mvc {
// 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] OpenIddictServices<TUser, TApplication> services) {
Services = services;
}
/// <summary>
/// Gets the OpenIddict services used by the controller.
/// </summary>
protected virtual OpenIddictServices<TUser, TApplication> Services { get; }
public class OpenIddictController<TUser, TApplication, TAuthorization, TToken> : Controller
where TUser : class where TApplication : class where TAuthorization : class where TToken : class {
[HttpGet, HttpPost]
public virtual async Task<IActionResult> Authorize() {
public virtual async Task<IActionResult> Authorize([FromServices] OpenIddictApplicationManager<TApplication> applications) {
// Note: when a fatal error occurs during the request processing, an OpenID Connect response
// is prematurely forged and added to the ASP.NET context by OpenIdConnectServerHandler.
// is prematurely forged and added to the ASP.NET Core context by OpenIdConnectServerHandler.
var response = HttpContext.GetOpenIdConnectResponse();
if (response != null) {
return View("Error", response);
@ -63,8 +56,8 @@ namespace OpenIddict.Mvc {
// Note: AspNet.Security.OpenIdConnect.Server automatically ensures an application
// corresponds to the client_id specified in the authorization request using
// IOpenIdConnectServerProvider.ValidateClientRedirectUri (see OpenIddictProvider.cs).
var application = await Services.Applications.FindApplicationByIdAsync(request.ClientId);
// IOpenIdConnectServerProvider.ValidateAuthorizationRequest (see OpenIddictProvider.cs).
var application = await applications.FindByIdAsync(request.ClientId);
if (application == null) {
return View("Error", new OpenIdConnectMessage {
Error = OpenIdConnectConstants.Errors.InvalidClient,
@ -72,11 +65,15 @@ namespace OpenIddict.Mvc {
});
}
return View("Authorize", Tuple.Create(request, await Services.Applications.GetDisplayNameAsync(application)));
return View("Authorize", Tuple.Create(request, await applications.GetDisplayNameAsync(application)));
}
[Authorize, HttpPost, ValidateAntiForgeryToken]
public virtual async Task<IActionResult> Accept() {
public virtual async Task<IActionResult> Accept(
[FromServices] UserManager<TUser> users,
[FromServices] OpenIddictApplicationManager<TApplication> applications,
[FromServices] OpenIddictTokenManager<TToken, TUser> tokens,
[FromServices] IOptions<OpenIddictOptions> options) {
var response = HttpContext.GetOpenIdConnectResponse();
if (response != null) {
return View("Error", response);
@ -91,7 +88,7 @@ namespace OpenIddict.Mvc {
}
// Retrieve the user data using the unique identifier.
var user = await Services.Users.GetUserAsync(User);
var user = await users.GetUserAsync(User);
if (user == null) {
return View("Error", new OpenIdConnectMessage {
Error = OpenIdConnectConstants.Errors.ServerError,
@ -101,10 +98,10 @@ namespace OpenIddict.Mvc {
// Create a new ClaimsIdentity containing the claims that
// will be used to create an id_token, a token or a code.
var identity = await Services.Applications.CreateIdentityAsync(user, request.GetScopes());
var identity = await tokens.CreateIdentityAsync(user, request.GetScopes());
Debug.Assert(identity != null);
var application = await Services.Applications.FindApplicationByIdAsync(request.ClientId);
var application = await applications.FindByIdAsync(request.ClientId);
if (application == null) {
return View("Error", new OpenIdConnectMessage {
Error = OpenIdConnectConstants.Errors.InvalidClient,
@ -116,7 +113,7 @@ namespace OpenIddict.Mvc {
var ticket = new AuthenticationTicket(
new ClaimsPrincipal(identity),
new AuthenticationProperties(),
Services.Options.AuthenticationScheme);
options.Value.AuthenticationScheme);
ticket.SetResources(request.GetResources());
ticket.SetScopes(request.GetScopes());
@ -128,7 +125,7 @@ namespace OpenIddict.Mvc {
}
[Authorize, HttpPost, ValidateAntiForgeryToken]
public virtual Task<IActionResult> Deny() {
public virtual Task<IActionResult> Deny([FromServices] IOptions<OpenIddictOptions> options) {
var response = HttpContext.GetOpenIdConnectResponse();
if (response != null) {
return Task.FromResult<IActionResult>(View("Error", response));
@ -145,7 +142,7 @@ namespace OpenIddict.Mvc {
// Notify ASOS that the authorization grant has been denied by the resource owner.
// Note: OpenIdConnectServerHandler will automatically take care of redirecting
// the user agent to the client application using the appropriate response_mode.
return Task.FromResult<IActionResult>(Forbid(Services.Options.AuthenticationScheme));
return Task.FromResult<IActionResult>(Forbid(options.Value.AuthenticationScheme));
}
[HttpGet]
@ -167,15 +164,17 @@ namespace OpenIddict.Mvc {
}
[ActionName(nameof(Logout)), HttpPost, ValidateAntiForgeryToken]
public virtual async Task<IActionResult> Signout() {
public virtual async Task<IActionResult> Signout(
[FromServices] SignInManager<TUser> signin,
[FromServices] IOptions<OpenIddictOptions> options) {
// Instruct the cookies middleware to delete the local cookie created
// when the user agent is redirected from the external identity provider
// after a successful authentication flow (e.g Google or Facebook).
await Services.SignIn.SignOutAsync();
await signin.SignOutAsync();
// Returning a SignOutResult will ask ASOS to redirect the user agent
// to the post_logout_redirect_uri specified by the client application.
return SignOut(Services.Options.AuthenticationScheme);
return SignOut(options.Value.AuthenticationScheme);
}
}
}

131
src/OpenIddict.Mvc/OpenIddictExtensions.cs

@ -8,12 +8,12 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Options;
@ -22,43 +22,67 @@ using OpenIddict.Mvc;
namespace Microsoft.AspNetCore.Builder {
public static class OpenIddictExtensions {
public static OpenIddictBuilder UseMvc([NotNull] this OpenIddictBuilder builder) {
/// <summary>
/// Registers the MVC module, including the built-in
/// authorization controller and the default consent views.
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddMvc([NotNull] this OpenIddictBuilder builder) {
if (builder == null) {
throw new ArgumentNullException(nameof(builder));
}
// Run MVC in an isolated environment.
builder.Services.AddMvc();
builder.Configure(options => {
// Set ApplicationCanDisplayErrors to true to allow OpenIddictController
// to intercept the error responses returned by the OpenID Connect server.
options.ApplicationCanDisplayErrors = true;
if (!options.AuthorizationEndpointPath.HasValue) {
// Restore the default authorization endpoint path in the OpenIddict options.
options.AuthorizationEndpointPath = OpenIdConnectServerDefaults.AuthorizationEndpointPath;
}
if (!options.LogoutEndpointPath.HasValue) {
// Restore the default logout endpoint path in the OpenIddict options.
options.LogoutEndpointPath = OpenIdConnectServerDefaults.LogoutEndpointPath;
}
});
// Run the MVC module in an isolated environment.
return builder.AddModule("MVC", 10, app => app.Isolate(map => map.UseMvc(routes => {
var options = app.ApplicationServices.GetRequiredService<IOptions<OpenIddictOptions>>().Value;
// Register the actions corresponding to the authorization endpoint.
if (builder.Options.AuthorizationEndpointPath.HasValue) {
routes.MapRoute("{D97891B4}", builder.Options.AuthorizationEndpointPath.Value.Substring(1), new {
controller = "OpenIddict", action = nameof(OpenIddictController<object, object>.Authorize)
if (options.AuthorizationEndpointPath.HasValue) {
routes.MapRoute("{D97891B4}", options.AuthorizationEndpointPath.Value.Substring(1), new {
controller = "OpenIddict", action = nameof(OpenIddictController<object, object, object, object>.Authorize)
});
routes.MapRoute("{7148DB83}", builder.Options.AuthorizationEndpointPath.Value.Substring(1) + "/accept", new {
controller = "OpenIddict", action = nameof(OpenIddictController<object, object>.Accept)
routes.MapRoute("{7148DB83}", options.AuthorizationEndpointPath.Value.Substring(1) + "/accept", new {
controller = "OpenIddict", action = nameof(OpenIddictController<object, object, object, object>.Accept)
});
routes.MapRoute("{23438BCC}", builder.Options.AuthorizationEndpointPath.Value.Substring(1) + "/deny", new {
controller = "OpenIddict", action = nameof(OpenIddictController<object, object>.Deny)
routes.MapRoute("{23438BCC}", options.AuthorizationEndpointPath.Value.Substring(1) + "/deny", new {
controller = "OpenIddict", action = nameof(OpenIddictController<object, object, object, object>.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 = "OpenIddict", action = nameof(OpenIddictController<object, object>.Logout)
if (options.LogoutEndpointPath.HasValue) {
routes.MapRoute("{C7DB102A}", options.LogoutEndpointPath.Value.Substring(1), new {
controller = "OpenIddict", action = nameof(OpenIddictController<object, object, object, object>.Logout)
});
}
}), services => {
var configuration = app.ApplicationServices.GetRequiredService<OpenIddictConfiguration>();
services.AddMvc()
// Note: ConfigureApplicationPartManager() must be
// called before AddControllersAsServices().
.ConfigureApplicationPartManager(manager => {
manager.ApplicationParts.Clear();
manager.ApplicationParts.Add(new OpenIddictPart(configuration));
manager.ApplicationParts.Add(new OpenIddictPart(builder));
})
.AddControllersAsServices()
@ -71,62 +95,72 @@ namespace Microsoft.AspNetCore.Builder {
// Update the Razor options to also use an embedded file provider that
// falls back to the current assembly when searching for views.
options.FileProviders.Add(new EmbeddedFileProvider(
assembly: typeof(OpenIddictController<,>).GetTypeInfo().Assembly,
baseNamespace: typeof(OpenIddictController<,>).Namespace));
assembly: typeof(OpenIddictController<,,,>).GetTypeInfo().Assembly,
baseNamespace: typeof(OpenIddictController<,,,>).Namespace));
});
// Register the user manager in the isolated container.
services.AddScoped(typeof(OpenIddictManager<,>).MakeGenericType(configuration.UserType, configuration.ApplicationType), provider => {
// Register the application manager in the isolated container.
services.AddScoped(typeof(OpenIddictApplicationManager<>).MakeGenericType(builder.ApplicationType), provider => {
var accessor = provider.GetRequiredService<IHttpContextAccessor>();
var container = (IServiceProvider) accessor.HttpContext.Items[typeof(IServiceProvider)];
Debug.Assert(container != null);
Debug.Assert(container != null, "The parent DI container cannot be resolved from the HTTP context.");
// Resolve the user manager from the parent container.
return container.GetRequiredService(typeof(OpenIddictManager<,>).MakeGenericType(configuration.UserType, configuration.ApplicationType));
// Resolve the application manager from the parent container.
return container.GetRequiredService(typeof(OpenIddictApplicationManager<>).MakeGenericType(builder.ApplicationType));
});
// Register the services context in the isolated container.
services.AddScoped(typeof(OpenIddictServices<,>).MakeGenericType(configuration.UserType, configuration.ApplicationType), provider => {
// Register the authorization manager in the isolated container.
services.AddScoped(typeof(OpenIddictAuthorizationManager<>).MakeGenericType(builder.AuthorizationType), provider => {
var accessor = provider.GetRequiredService<IHttpContextAccessor>();
var container = (IServiceProvider) accessor.HttpContext.Items[typeof(IServiceProvider)];
Debug.Assert(container != null);
Debug.Assert(container != null, "The parent DI container cannot be resolved from the HTTP context.");
// Resolve the services context from the parent container.
return container.GetRequiredService(typeof(OpenIddictServices<,>).MakeGenericType(configuration.UserType, configuration.ApplicationType));
// Resolve the authorization manager from the parent container.
return container.GetRequiredService(typeof(OpenIddictAuthorizationManager<>).MakeGenericType(builder.AuthorizationType));
});
// Register the sign-in manager in the isolated container.
services.AddScoped(typeof(SignInManager<>).MakeGenericType(configuration.UserType), provider => {
services.AddScoped(typeof(SignInManager<>).MakeGenericType(builder.UserType), provider => {
var accessor = provider.GetRequiredService<IHttpContextAccessor>();
var container = (IServiceProvider) accessor.HttpContext.Items[typeof(IServiceProvider)];
Debug.Assert(container != null);
Debug.Assert(container != null, "The parent DI container cannot be resolved from the HTTP context.");
// Resolve the sign-in manager from the parent container.
return container.GetRequiredService(typeof(SignInManager<>).MakeGenericType(configuration.UserType));
return container.GetRequiredService(typeof(SignInManager<>).MakeGenericType(builder.UserType));
});
// Register the user manager in the isolated container.
services.AddScoped(typeof(UserManager<>).MakeGenericType(configuration.UserType), provider => {
// Register the token manager in the isolated container.
services.AddScoped(typeof(OpenIddictTokenManager<,>).MakeGenericType(
/* TToken: */ builder.TokenType,
/* TUser: */ builder.UserType), provider => {
var accessor = provider.GetRequiredService<IHttpContextAccessor>();
var container = (IServiceProvider) accessor.HttpContext.Items[typeof(IServiceProvider)];
Debug.Assert(container != null);
Debug.Assert(container != null, "The parent DI container cannot be resolved from the HTTP context.");
// Resolve the user manager from the parent container.
return container.GetRequiredService(typeof(UserManager<>).MakeGenericType(configuration.UserType));
// Resolve the token manager from the parent container.
return container.GetRequiredService(typeof(OpenIddictTokenManager<,>).MakeGenericType(
/* TToken: */ builder.TokenType, /* TUser: */ builder.UserType));
});
// Register the compilation service in the isolated container.
services.AddScoped(provider => {
// Register the user manager in the isolated container.
services.AddScoped(typeof(UserManager<>).MakeGenericType(builder.UserType), provider => {
var accessor = provider.GetRequiredService<IHttpContextAccessor>();
var container = (IServiceProvider) accessor.HttpContext.Items[typeof(IServiceProvider)];
Debug.Assert(container != null);
Debug.Assert(container != null, "The parent DI container cannot be resolved from the HTTP context.");
// Resolve the compilation service from the parent container.
return container.GetRequiredService<ICompilationService>();
// Resolve the user manager from the parent container.
return container.GetRequiredService(typeof(UserManager<>).MakeGenericType(builder.UserType));
});
// Register the options in the isolated container.
services.AddSingleton(Options.Create(builder.Options));
services.AddSingleton(provider => {
var accessor = provider.GetRequiredService<IHttpContextAccessor>();
var container = (IServiceProvider) accessor.HttpContext.Items[typeof(IServiceProvider)];
Debug.Assert(container != null, "The parent DI container cannot be resolved from the HTTP context.");
// Resolve the user manager from the parent container.
return container.GetRequiredService<IOptions<OpenIddictOptions>>();
});
}));
}
@ -135,7 +169,7 @@ namespace Microsoft.AspNetCore.Builder {
// Ensure the convention is only applied to the intended controller.
Debug.Assert(controller.ControllerType != null);
Debug.Assert(controller.ControllerType.IsGenericType);
Debug.Assert(controller.ControllerType.GetGenericTypeDefinition() == typeof(OpenIddictController<,>));
Debug.Assert(controller.ControllerType.GetGenericTypeDefinition() == typeof(OpenIddictController<,,,>));
// Note: manually updating the controller name is required
// to remove the ending markers added to the generic type name.
@ -144,12 +178,13 @@ namespace Microsoft.AspNetCore.Builder {
}
private class OpenIddictPart : ApplicationPart, IApplicationPartTypeProvider {
public OpenIddictPart(OpenIddictConfiguration configuration) {
public OpenIddictPart(OpenIddictBuilder builder) {
Types = new[] {
typeof(OpenIddictController<,>)
.MakeGenericType(configuration.UserType,
configuration.ApplicationType)
.GetTypeInfo()
typeof(OpenIddictController<,,,>).MakeGenericType(
/* TUser: */ builder.UserType,
/* TApplication: */ builder.ApplicationType,
/* TAuthorization: */ builder.AuthorizationType,
/* TToken: */ builder.TokenType).GetTypeInfo()
};
}

45
src/OpenIddict.Security/OpenIddictExtensions.cs

@ -1,24 +1,42 @@
using System;
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Cors.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using NWebsec.AspNetCore.Middleware;
namespace Microsoft.AspNetCore.Builder {
public static class OpenIddictExtensions {
public static OpenIddictBuilder UseNWebsec([NotNull] this OpenIddictBuilder builder) {
/// <summary>
/// Registers the NWebsec module using the default Content Security Policy.
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddNWebsec([NotNull] this OpenIddictBuilder builder) {
if (builder == null) {
throw new ArgumentNullException(nameof(builder));
}
return builder.UseNWebsec(options => {
return builder.AddNWebsec(options => {
options.DefaultSources(directive => directive.Self())
.ImageSources(directive => directive.Self().CustomSources("*"))
.ScriptSources(directive => directive.Self().UnsafeInline())
.StyleSources(directive => directive.Self().UnsafeInline());
.ImageSources(directive => directive.Self())
.ScriptSources(directive => directive.Self())
.StyleSources(directive => directive.Self());
});
}
public static OpenIddictBuilder UseNWebsec(
/// <summary>
/// Registers the NWebsec module using the specified Content Security Policy.
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <param name="configuration">The delegate used to configure the Content Security Policy options.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddNWebsec(
[NotNull] this OpenIddictBuilder builder,
[NotNull] Action<IFluentCspOptions> configuration) {
if (builder == null) {
@ -40,15 +58,21 @@ namespace Microsoft.AspNetCore.Builder {
// Insert a new middleware responsible of setting the X-Frame-Options header.
// See https://nwebsec.codeplex.com/wikipage?title=Configuring%20security%20headers&referringTitle=NWebsec
app.UseXfo(options => options.Deny());
app.UseXfo(xfo => xfo.Deny());
// Insert a new middleware responsible of setting the X-Xss-Protection header.
// See https://nwebsec.codeplex.com/wikipage?title=Configuring%20security%20headers&referringTitle=NWebsec
app.UseXXssProtection(options => options.EnabledWithBlockMode());
app.UseXXssProtection(xss => xss.EnabledWithBlockMode());
});
}
public static OpenIddictBuilder UseCors(
/// <summary>
/// Registers the CORS module.
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <param name="configuration">The delegate used to configure the CORS policy.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddCors(
[NotNull] this OpenIddictBuilder builder,
[NotNull] Action<CorsPolicyBuilder> configuration) {
if (builder == null) {
@ -59,6 +83,7 @@ namespace Microsoft.AspNetCore.Builder {
throw new ArgumentNullException(nameof(configuration));
}
builder.Services.AddCors();
builder.AddModule("CORS", -10, map => map.UseCors(configuration));
return builder;

114
src/OpenIddict/OpenIddictExtensions.cs

@ -6,56 +6,96 @@
using System;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Identity;
using OpenIddict.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using OpenIddict;
namespace Microsoft.AspNetCore.Builder {
public static class OpenIddictExtensions {
public static IdentityBuilder AddOpenIddict([NotNull] this IdentityBuilder builder) {
if (builder == null) {
throw new ArgumentNullException(nameof(builder));
/// <summary>
/// Registers the default OpenIddict services in the DI container,
/// including the Entity Framework stores and the built-in entities.
/// </summary>
/// <typeparam name="TContext">The type of the Entity Framework database context.</typeparam>
/// <param name="services">The services collection.</param>
/// <remarks>
/// Note: the core services include native support for the non-interactive flows
/// (resource owner password credentials, client credentials, refresh token).
/// To support interactive flows like authorization code or implicit/hybrid,
/// consider adding the MVC module or creating your own authorization controller.
/// </remarks>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddOpenIddict<TContext>([NotNull] this IServiceCollection services)
where TContext : DbContext {
if (services == null) {
throw new ArgumentNullException(nameof(services));
}
return builder.AddOpenIddictCore<Application>(configuration => {
// Use the EF adapter by default.
configuration.UseEntityFramework();
});
return services.AddOpenIddict<OpenIddictUser, TContext>();
}
public static IdentityBuilder AddOpenIddict<TApplication>([NotNull] this IdentityBuilder builder)
where TApplication : class {
if (builder == null) {
throw new ArgumentNullException(nameof(builder));
/// <summary>
/// Registers the default OpenIddict services in the DI container,
/// including the Entity Framework stores and the specified entities.
/// </summary>
/// <typeparam name="TUser">The type of the User entity.</typeparam>
/// <typeparam name="TContext">The type of the Entity Framework database context.</typeparam>
/// <param name="services">The services collection.</param>
/// <remarks>
/// Note: the core services include native support for the non-interactive flows
/// (resource owner password credentials, client credentials, refresh token).
/// To support interactive flows like authorization code or implicit/hybrid,
/// consider adding the MVC module or creating your own authorization controller.
/// </remarks>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddOpenIddict<TUser, TContext>([NotNull] this IServiceCollection services)
where TUser : OpenIddictUser
where TContext : DbContext {
if (services == null) {
throw new ArgumentNullException(nameof(services));
}
return builder.AddOpenIddictCore<TApplication>(configuration => {
// Use the EF adapter by default.
configuration.UseEntityFramework();
});
return services.AddOpenIddict<TUser, OpenIddictApplication,
OpenIddictAuthorization,
OpenIddictScope,
OpenIddictToken, TContext, string>();
}
public static IApplicationBuilder UseOpenIddict([NotNull] this IApplicationBuilder app) {
return app.UseOpenIddict(options => { });
}
public static IApplicationBuilder UseOpenIddict(
[NotNull] this IApplicationBuilder app,
[NotNull] Action<OpenIddictBuilder> configuration) {
if (app == null) {
throw new ArgumentNullException(nameof(app));
/// <summary>
/// Registers the default OpenIddict services in the DI container,
/// including the Entity Framework stores and the specified entities.
/// </summary>
/// <typeparam name="TUser">The type of the User entity.</typeparam>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <typeparam name="TScope">The type of the Scope entity.</typeparam>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <typeparam name="TContext">The type of the Entity Framework database context.</typeparam>
/// <typeparam name="TKey">The type of the entity primary keys.</typeparam>
/// <param name="services">The services collection.</param>
/// <remarks>
/// Note: the core services include native support for the non-interactive flows
/// (resource owner password credentials, client credentials, refresh token).
/// To support interactive flows like authorization code or implicit/hybrid,
/// consider adding the MVC module or creating your own authorization controller.
/// </remarks>
/// <returns>The <see cref="IServiceCollection"/>.</returns>
public static OpenIddictBuilder AddOpenIddict<TUser, TApplication, TAuthorization, TScope, TToken, TContext, TKey>(
[NotNull] this IServiceCollection services)
where TUser : OpenIddictUser<TAuthorization, TToken, TKey>
where TApplication : OpenIddictApplication<TKey>
where TAuthorization : OpenIddictAuthorization<TToken, TKey>
where TScope : OpenIddictScope<TKey>
where TToken : OpenIddictToken<TKey>
where TContext : DbContext
where TKey : IEquatable<TKey> {
if (services == null) {
throw new ArgumentNullException(nameof(services));
}
if (configuration == null) {
throw new ArgumentNullException(nameof(configuration));
}
return app.UseOpenIddictCore(builder => {
builder.UseAssets();
builder.UseNWebsec();
builder.UseMvc();
configuration(builder);
});
// Register the OpenIddict core services and the default EntityFramework stores.
return services.AddOpenIddict<TUser, TApplication, TAuthorization, TScope, TToken>()
.AddEntityFramework<TContext, TKey>();
}
}
}

9
src/OpenIddict/project.json

@ -34,18 +34,15 @@
"dependencies": {
"JetBrains.Annotations": { "type": "build", "version": "10.1.4" },
"OpenIddict.Assets": { "target": "project" },
"OpenIddict.EF": { "target": "project" },
"OpenIddict.Mvc": { "target": "project" },
"OpenIddict.Security": { "target": "project" }
"OpenIddict.EntityFramework": { "target": "project" }
},
"frameworks": {
"net451": { },
"netstandard1.5": {
"netstandard1.4": {
"imports": [
"dotnet5.6",
"dotnet5.5",
"portable-net451+win8"
]
}

Loading…
Cancel
Save