Browse Source

Decouple OpenIddict from ASP.NET Core Identity

pull/237/head
Kévin Chalet 10 years ago
parent
commit
d8fadc0f99
  1. 42
      samples/Mvc.Server/Controllers/AccountController.cs
  2. 95
      samples/Mvc.Server/Controllers/AuthorizationController.cs
  3. 4
      samples/Mvc.Server/Startup.cs
  4. 32
      src/OpenIddict.Core/Infrastructure/OpenIddictHelpers.cs
  5. 64
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Authentication.cs
  6. 27
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Discovery.cs
  7. 54
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Exchange.cs
  8. 20
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Introspection.cs
  9. 9
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Revocation.cs
  10. 67
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Serialization.cs
  11. 12
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Session.cs
  12. 78
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Userinfo.cs
  13. 4
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.cs
  14. 17
      src/OpenIddict.Core/Infrastructure/OpenIddictServices.cs
  15. 9
      src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs
  16. 192
      src/OpenIddict.Core/Managers/OpenIddictUserManager.cs
  17. 44
      src/OpenIddict.Core/OpenIddictBuilder.cs
  18. 21
      src/OpenIddict.Core/OpenIddictExtensions.cs
  19. 55
      src/OpenIddict.Core/Stores/IOpenIddictUserStore.cs
  20. 3
      src/OpenIddict.Core/project.json
  21. 16
      src/OpenIddict.EntityFramework/OpenIddictExtensions.cs
  22. 4
      src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs
  23. 162
      src/OpenIddict.EntityFramework/Stores/OpenIddictUserStore.cs
  24. 2
      src/OpenIddict.EntityFramework/project.json
  25. 2
      src/OpenIddict.Mvc/project.json
  26. 102
      src/OpenIddict/OpenIddictExtensions.cs
  27. 2
      src/OpenIddict/project.json

42
samples/Mvc.Server/Controllers/AccountController.cs

@ -1,6 +1,8 @@
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using AspNet.Security.OAuth.Validation;
using AspNet.Security.OpenIdConnect.Extensions;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
@ -8,6 +10,8 @@ using Microsoft.AspNetCore.Mvc.Rendering;
using Mvc.Server.Models;
using Mvc.Server.Services;
using Mvc.Server.ViewModels.Account;
using Newtonsoft.Json.Linq;
using OpenIddict;
namespace Mvc.Server.Controllers {
[Authorize]
@ -32,6 +36,44 @@ namespace Mvc.Server.Controllers {
_applicationDbContext = applicationDbContext;
}
//
// GET: /Account/Userinfo
[Authorize(ActiveAuthenticationSchemes = OAuthValidationDefaults.AuthenticationScheme)]
[HttpGet, Produces("application/json")]
public async Task<IActionResult> Userinfo() {
var user = await _userManager.GetUserAsync(User);
if (user == null) {
return BadRequest(new OpenIdConnectResponse {
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The user profile is no longer available."
});
}
var claims = new JObject();
// Note: the "sub" claim is a mandatory claim and must be included in the JSON response.
claims[OpenIdConnectConstants.Claims.Subject] = user.Id.ToString();
if (User.HasClaim(OpenIdConnectConstants.Claims.Scope, OpenIdConnectConstants.Scopes.Email)) {
claims[OpenIdConnectConstants.Claims.Email] = user.Email;
claims[OpenIdConnectConstants.Claims.EmailVerified] = user.EmailConfirmed;
}
if (User.HasClaim(OpenIdConnectConstants.Claims.Scope, OpenIdConnectConstants.Scopes.Phone)) {
claims[OpenIdConnectConstants.Claims.PhoneNumber] = user.PhoneNumber;
claims[OpenIdConnectConstants.Claims.PhoneNumberVerified] = user.PhoneNumberConfirmed;
}
if (User.HasClaim(OpenIdConnectConstants.Claims.Scope, OpenIddictConstants.Scopes.Roles)) {
claims[OpenIddictConstants.Claims.Roles] = JArray.FromObject(await _userManager.GetRolesAsync(user));
}
// Note: the complete list of standard claims supported by the OpenID Connect specification
// can be found here: http://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
return Json(claims);
}
//
// GET: /Account/Login
[HttpGet]

95
samples/Mvc.Server/Controllers/AuthorizationController.cs

@ -24,12 +24,12 @@ namespace Mvc.Server {
public class AuthorizationController : Controller {
private readonly OpenIddictApplicationManager<OpenIddictApplication<Guid>> _applicationManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly OpenIddictUserManager<ApplicationUser> _userManager;
private readonly UserManager<ApplicationUser> _userManager;
public AuthorizationController(
OpenIddictApplicationManager<OpenIddictApplication<Guid>> applicationManager,
SignInManager<ApplicationUser> signInManager,
OpenIddictUserManager<ApplicationUser> userManager) {
UserManager<ApplicationUser> userManager) {
_applicationManager = applicationManager;
_signInManager = signInManager;
_userManager = userManager;
@ -69,23 +69,8 @@ namespace Mvc.Server {
});
}
// Create a new ClaimsIdentity containing the claims that
// will be used to create an id_token, a token or a code.
var identity = await _userManager.CreateIdentityAsync(user, request.GetScopes());
// Create a new authentication ticket holding the user identity.
var ticket = new AuthenticationTicket(
new ClaimsPrincipal(identity),
new AuthenticationProperties(),
OpenIdConnectServerDefaults.AuthenticationScheme);
// Set the list of scopes granted to the client application.
ticket.SetScopes(new[] {
/* openid: */ OpenIdConnectConstants.Scopes.OpenId,
/* email: */ OpenIdConnectConstants.Scopes.Email,
/* profile: */ OpenIdConnectConstants.Scopes.Profile,
/* offline_access: */ OpenIdConnectConstants.Scopes.OfflineAccess
}.Intersect(request.GetScopes()));
// Create a new authentication ticket.
var ticket = await CreateTicketAsync(request, user);
// Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
@ -98,6 +83,9 @@ namespace Mvc.Server {
return Forbid(OpenIdConnectServerDefaults.AuthenticationScheme);
}
// Note: the logout action is only useful when implementing interactive
// flows like the authorization code flow or the implicit flow.
[HttpGet("~/connect/logout")]
public IActionResult Logout(OpenIdConnectRequest request) {
// Flow the request_id to allow OpenIddict to restore
@ -174,21 +162,8 @@ namespace Mvc.Server {
await _userManager.ResetAccessFailedCountAsync(user);
}
var identity = await _userManager.CreateIdentityAsync(user, request.GetScopes());
// Create a new authentication ticket holding the user identity.
var ticket = new AuthenticationTicket(
new ClaimsPrincipal(identity),
new AuthenticationProperties(),
OpenIdConnectServerDefaults.AuthenticationScheme);
// Set the list of scopes granted to the client application.
ticket.SetScopes(new[] {
/* openid: */ OpenIdConnectConstants.Scopes.OpenId,
/* email: */ OpenIdConnectConstants.Scopes.Email,
/* profile: */ OpenIdConnectConstants.Scopes.Profile,
/* offline_access: */ OpenIdConnectConstants.Scopes.OfflineAccess
}.Intersect(request.GetScopes()));
// Create a new authentication ticket.
var ticket = await CreateTicketAsync(request, user);
return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
}
@ -198,5 +173,57 @@ namespace Mvc.Server {
ErrorDescription = "The specified grant type is not supported."
});
}
private async Task<AuthenticationTicket> CreateTicketAsync(OpenIdConnectRequest request, ApplicationUser user) {
// Set the list of scopes granted to the client application.
// Note: the offline_access scope must be granted
// to allow OpenIddict to return a refresh token.
var scopes = new[] {
OpenIdConnectConstants.Scopes.OpenId,
OpenIdConnectConstants.Scopes.Email,
OpenIdConnectConstants.Scopes.Profile,
OpenIdConnectConstants.Scopes.OfflineAccess
}.Intersect(request.GetScopes());
// Create a new ClaimsPrincipal containing the claims that
// will be used to create an id_token, a token or a code.
var principal = await _signInManager.CreateUserPrincipalAsync(user);
// Note: by default, claims are NOT automatically included in the access and identity tokens.
// To allow OpenIddict to serialize them, you must attach them a destination, that specifies
// whether they should be included in access tokens, in identity tokens or in both.
foreach (var claim in principal.Claims) {
// Always include the user identifier in the
// access token and the identity token.
if (claim.Type == ClaimTypes.NameIdentifier) {
claim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
}
// Include the name claim, but only if the "profile" scope was requested.
else if (claim.Type == ClaimTypes.Name && scopes.Contains(OpenIdConnectConstants.Scopes.Profile)) {
claim.SetDestinations(OpenIdConnectConstants.Destinations.IdentityToken);
}
// Include the role claims, but only if the "roles" scope was requested.
else if (claim.Type == ClaimTypes.Role && scopes.Contains(OpenIddictConstants.Scopes.Roles)) {
claim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
}
// The other claims won't be added to the access
// and identity tokens and will be kept private.
}
// Create a new authentication ticket holding the user identity.
var ticket = new AuthenticationTicket(
principal, new AuthenticationProperties(),
OpenIdConnectServerDefaults.AuthenticationScheme);
ticket.SetScopes(scopes);
return ticket;
}
}
}

4
samples/Mvc.Server/Startup.cs

@ -30,7 +30,7 @@ namespace Mvc.Server {
.AddDefaultTokenProviders();
// Register the OpenIddict services, including the default Entity Framework stores.
services.AddOpenIddict<ApplicationUser, IdentityRole<Guid>, ApplicationDbContext, Guid>()
services.AddOpenIddict<ApplicationDbContext, Guid>()
// Register the ASP.NET Core MVC binder used by OpenIddict.
// Note: if you don't call this method, you won't be able to
// bind OpenIdConnectRequest or OpenIdConnectResponse parameters.
@ -40,7 +40,7 @@ namespace Mvc.Server {
.EnableAuthorizationEndpoint("/connect/authorize")
.EnableLogoutEndpoint("/connect/logout")
.EnableTokenEndpoint("/connect/token")
.EnableUserinfoEndpoint("/connect/userinfo")
.EnableUserinfoEndpoint("/Account/Userinfo")
// Note: the Mvc.Client sample only uses the code flow and the password flow, but you
// can enable the other flows if you need to support implicit or client credentials.

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

@ -5,43 +5,11 @@
*/
using System;
using System.Linq;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Identity;
namespace OpenIddict.Infrastructure {
public static class OpenIddictHelpers {
/// <summary>
/// Tries to find the given claim in the user claims.
/// </summary>
/// <typeparam name="TUser">The type of the User entity.</typeparam>
/// <param name="manager">The user manager.</param>
/// <param name="user">The user.</param>
/// <param name="type">The claim type.</param>
/// <returns>The claim value, or <c>null</c> if it cannot be found.</returns>
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();
}
/// <summary>
/// Determines whether an application is a confidential client.
/// </summary>

64
src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Authentication.cs

@ -5,7 +5,6 @@
*/
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Claims;
@ -13,10 +12,8 @@ using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;
@ -25,10 +22,10 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Bson;
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 partial class OpenIddictProvider<TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
where TApplication : class where TAuthorization : class where TScope : class where TToken : class {
public override async Task ExtractAuthorizationRequest([NotNull] ExtractAuthorizationRequestContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TApplication, TAuthorization, TScope, TToken>>();
// Reject requests using the unsupported request parameter.
if (!string.IsNullOrEmpty(context.Request.Request)) {
@ -86,7 +83,7 @@ namespace OpenIddict.Infrastructure {
}
public override async Task ValidateAuthorizationRequest([NotNull] ValidateAuthorizationRequestContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TApplication, TAuthorization, TScope, TToken>>();
// Note: the OpenID Connect server middleware supports authorization code, implicit, hybrid,
// none and custom flows but OpenIddict uses a stricter policy rejecting unknown flows.
@ -311,56 +308,7 @@ namespace OpenIddict.Infrastructure {
}
public override async Task HandleAuthorizationRequest([NotNull] HandleAuthorizationRequestContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
if (string.Equals(context.Request.Prompt, "none", StringComparison.Ordinal)) {
// 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);
Debug.Assert(principal != null, "The principal extracted from the id_token_hint shouldn't be null.");
// Note: user may be null if the user was removed after
// the initial check made by ValidateAuthorizationRequest.
var user = await services.Users.GetUserAsync(principal);
if (user == null) {
services.Logger.LogError("The authorization request was aborted because the profile corresponding " +
"to the logged in user was not found in the database: {Identifier}.",
context.HttpContext.User.GetClaim(ClaimTypes.NameIdentifier));
context.Reject(
error: OpenIdConnectConstants.Errors.ServerError,
description: "An internal error has occurred.");
return;
}
// Note: filtering the username is not needed at this stage as OpenIddictController.Accept
// and OpenIddictProvider.HandleTokenRequest 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.Users.CreateIdentityAsync(user, context.Request.GetScopes());
if (identity == null) {
throw new InvalidOperationException("The authorization request was aborted because the user manager returned a null " +
$"identity for user '{await services.Users.GetUserNameAsync(user)}'.");
}
// Create a new authentication ticket holding the user identity.
var ticket = new AuthenticationTicket(
new ClaimsPrincipal(identity),
new AuthenticationProperties(),
context.Options.AuthenticationScheme);
ticket.SetResources(context.Request.GetResources());
ticket.SetScopes(context.Request.GetScopes());
// Call SignInAsync to create and return a new OpenID Connect response containing the serialized code/tokens.
await context.HttpContext.Authentication.SignInAsync(ticket.AuthenticationScheme, ticket.Principal, ticket.Properties);
// Mark the response as handled
// to skip the rest of the pipeline.
context.HandleResponse();
return;
}
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TApplication, TAuthorization, TScope, TToken>>();
// If no request_id parameter can be found in the current request, assume the OpenID Connect request
// was not serialized yet and store the entire payload in the distributed cache to make it easier
@ -406,7 +354,7 @@ namespace OpenIddict.Infrastructure {
}
public override async Task ApplyAuthorizationResponse([NotNull] ApplyAuthorizationResponseContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TApplication, TAuthorization, TScope, TToken>>();
// Remove the authorization request from the distributed cache.
if (!string.IsNullOrEmpty(context.Request.RequestId)) {

27
src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Discovery.cs

@ -11,10 +11,10 @@ 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 partial class OpenIddictProvider<TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
where TApplication : class where TAuthorization : class where TScope : class where TToken : class {
public override Task HandleConfigurationRequest([NotNull] HandleConfigurationRequestContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TApplication, TAuthorization, TScope, TToken>>();
// Note: though it's natively supported by the OpenID Connect server middleware,
// OpenIddict disallows the use of the unsecure code_challenge_method=plain method,
@ -35,24 +35,9 @@ namespace OpenIddict.Infrastructure {
// Note: the "openid" scope is automatically
// added by the OpenID Connect server middleware.
context.Scopes.Add(OpenIdConnectConstants.Scopes.Profile);
// Only add the "email" scope if it's supported
// by the user manager and the underlying store.
if (services.Users.SupportsUserEmail) {
context.Scopes.Add(OpenIdConnectConstants.Scopes.Email);
}
// Only add the "phone" scope if it's supported
// by the user manager and the underlying store.
if (services.Users.SupportsUserPhoneNumber) {
context.Scopes.Add(OpenIdConnectConstants.Scopes.Phone);
}
// Only add the "roles" scope if it's supported
// by the user manager and the underlying store.
if (services.Users.SupportsUserRole) {
context.Scopes.Add(OpenIddictConstants.Scopes.Roles);
}
context.Scopes.Add(OpenIdConnectConstants.Scopes.Email);
context.Scopes.Add(OpenIdConnectConstants.Scopes.Phone);
context.Scopes.Add(OpenIddictConstants.Scopes.Roles);
// Only add the "offline_access" scope if "refresh_token" is listed as a supported grant type.
if (context.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.RefreshToken)) {

54
src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Exchange.cs

@ -4,23 +4,20 @@
* the license and the contributors participating to this project.
*/
using System;
using System.Diagnostics;
using System.Security.Claims;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
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 partial class OpenIddictProvider<TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
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, TAuthorization, TScope, TToken>>();
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TApplication, TAuthorization, TScope, TToken>>();
// Reject token requests that don't specify a supported grant type.
if (!services.Options.GrantTypes.Contains(context.Request.GrantType)) {
@ -180,7 +177,7 @@ namespace OpenIddict.Infrastructure {
}
public override async Task HandleTokenRequest([NotNull] HandleTokenRequestContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TApplication, TAuthorization, TScope, TToken>>();
// Note: the OpenID Connect server middleware automatically reuses the authentication ticket
// stored in the authorization code to create a new identity. To ensure the user was not removed
@ -188,19 +185,6 @@ namespace OpenIddict.Infrastructure {
if (context.Request.IsAuthorizationCodeGrantType()) {
Debug.Assert(context.Ticket != null, "The authentication ticket shouldn't be null.");
var user = await services.Users.GetUserAsync(context.Ticket.Principal);
if (user == null) {
services.Logger.LogError("The token request was rejected because the user profile associated " +
"with the authorization code was not found in the database: '{Identifier}'.",
context.Ticket.Principal.GetClaim(ClaimTypes.NameIdentifier));
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidGrant,
description: "The authorization code is no longer valid.");
return;
}
// Extract the token identifier from the authorization code.
var identifier = context.Ticket.GetTicketId();
Debug.Assert(!string.IsNullOrEmpty(identifier),
@ -232,19 +216,6 @@ namespace OpenIddict.Infrastructure {
else if (context.Request.IsRefreshTokenGrantType()) {
Debug.Assert(context.Ticket != null, "The authentication ticket shouldn't be null.");
var user = await services.Users.GetUserAsync(context.Ticket.Principal);
if (user == null) {
services.Logger.LogError("The token request was rejected because the user profile associated " +
"with the refresh token was not found in the database: '{Identifier}'.",
context.Ticket.Principal.GetClaim(ClaimTypes.NameIdentifier));
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidGrant,
description: "The refresh token is no longer valid.");
return;
}
// Extract the token identifier from the refresh token.
var identifier = context.Ticket.GetTicketId();
Debug.Assert(!string.IsNullOrEmpty(identifier),
@ -269,22 +240,7 @@ namespace OpenIddict.Infrastructure {
await services.Tokens.RevokeAsync(token);
}
// Note: the "scopes" property stored in context.AuthenticationTicket is automatically updated by the
// OpenID Connect server middleware when the client application requests a restricted scopes collection.
var identity = await services.Users.CreateIdentityAsync(user, context.Ticket.GetScopes());
if (identity == null) {
throw new InvalidOperationException("The token request was aborted because the user manager returned a null " +
$"identity for user '{await services.Users.GetUserNameAsync(user)}'.");
}
// Create a new authentication ticket holding the user identity but
// reuse the authentication properties stored in the refresh token.
var ticket = new AuthenticationTicket(
new ClaimsPrincipal(identity),
context.Ticket.Properties,
context.Options.AuthenticationScheme);
context.Validate(ticket);
context.Validate(context.Ticket);
return;
}

20
src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Introspection.cs

@ -14,10 +14,10 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
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 partial class OpenIddictProvider<TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
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, TAuthorization, TScope, TToken>>();
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TApplication, TAuthorization, TScope, TToken>>();
// Note: the OpenID Connect server middleware supports both GET and POST
// introspection requests but OpenIddict only accepts POST requests.
@ -82,7 +82,7 @@ namespace OpenIddict.Infrastructure {
}
public override async Task HandleIntrospectionRequest([NotNull] HandleIntrospectionRequestContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TApplication, TAuthorization, TScope, TToken>>();
Debug.Assert(context.Ticket != null, "The authentication ticket shouldn't be null.");
Debug.Assert(!string.IsNullOrEmpty(context.Request.ClientId), "The client_id parameter shouldn't be null.");
@ -101,18 +101,6 @@ namespace OpenIddict.Infrastructure {
return;
}
var user = await services.Users.GetUserAsync(context.Ticket.Principal);
if (user == null) {
services.Logger.LogInformation("The token {Identifier} was declared as inactive because the " +
"corresponding user ({Username}) was not found in the database.",
context.Ticket.GetTicketId(), services.Users.GetUserName(context.Ticket.Principal));
context.Claims.RemoveAll();
context.Active = false;
return;
}
// When the received ticket is revocable, ensure it is still valid.
if (context.Ticket.IsAuthorizationCode() || context.Ticket.IsRefreshToken()) {
// Retrieve the token from the database using the unique identifier stored in the authentication ticket:

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

@ -4,7 +4,6 @@
* the license and the contributors participating to this project.
*/
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions;
@ -14,10 +13,10 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
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 partial class OpenIddictProvider<TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
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>>();
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TApplication, TAuthorization, TScope, TToken>>();
// When token_type_hint is specified, reject the request if it doesn't correspond to a revocable token.
if (!string.IsNullOrEmpty(context.Request.TokenTypeHint) &&
@ -110,7 +109,7 @@ namespace OpenIddict.Infrastructure {
}
public override async Task HandleRevocationRequest([NotNull] HandleRevocationRequestContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TApplication, TAuthorization, TScope, TToken>>();
Debug.Assert(context.Ticket != null, "The authentication ticket shouldn't be null.");

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

@ -5,8 +5,6 @@
*/
using System;
using System.Diagnostics;
using System.Security.Claims;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Server;
@ -14,31 +12,12 @@ 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 partial class OpenIddictProvider<TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
where TApplication : class where TAuthorization : class where TScope : class where TToken : class {
public override async Task SerializeAuthorizationCode([NotNull] SerializeAuthorizationCodeContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
Debug.Assert(context.Request.IsAuthorizationRequest(), "The request should be an authorization request.");
Debug.Assert(!string.IsNullOrEmpty(context.Request.ClientId), "The client_id parameter shouldn't be null or empty.");
// Note: a null value could be returned by FindByIdAsync. In this case, throw an exception to abort the token request.
var user = await services.Users.FindByIdAsync(context.Ticket.Principal.GetClaim(ClaimTypes.NameIdentifier));
if (user == null) {
throw new InvalidOperationException("The token request was aborted because the user associated " +
"with the authorization code was not found in the database.");
}
var application = await services.Applications.FindByClientIdAsync(context.Request.ClientId);
if (application == null) {
throw new InvalidOperationException("The application cannot be retrieved from the database.");
}
// Persist a new token entry in the database and attach it
// to the user and the client application it is issued to.
var identifier = await services.Users.CreateTokenAsync(user, context.Request.ClientId,
OpenIdConnectConstants.TokenTypeHints.AuthorizationCode);
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TApplication, TAuthorization, TScope, TToken>>();
var identifier = await services.Tokens.CreateAsync(OpenIdConnectConstants.TokenTypeHints.AuthorizationCode);
if (string.IsNullOrEmpty(identifier)) {
throw new InvalidOperationException("The unique key associated with an authorization code cannot be null or empty.");
}
@ -50,43 +29,9 @@ namespace OpenIddict.Infrastructure {
}
public override async Task SerializeRefreshToken([NotNull] SerializeRefreshTokenContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
Debug.Assert(context.Request.IsTokenRequest(), "The request should be a token request.");
Debug.Assert(!context.Request.IsClientCredentialsGrantType(),
"A refresh token should not be issued when using grant_type=client_credentials.");
// Note: a null value could be returned by FindByIdAsync if the user was removed after the initial
// check made by HandleTokenRequest. In this case, throw an exception to abort the token request.
var user = await services.Users.FindByIdAsync(context.Ticket.Principal.GetClaim(ClaimTypes.NameIdentifier));
if (user == null) {
throw new InvalidOperationException("The token request was aborted because the user associated " +
"with the refresh token was not found in the database.");
}
string identifier;
// If the client application sending the token request is known,
// ensure the token is attached to the corresponding client entity.
if (!string.IsNullOrEmpty(context.Request.ClientId)) {
var application = await services.Applications.FindByClientIdAsync(context.Request.ClientId);
if (application == null) {
throw new InvalidOperationException("The application cannot be retrieved from the database.");
}
// Persist a new token entry in the database and attach it
// to the user and the client application it is issued to.
identifier = await services.Users.CreateTokenAsync(user, context.Request.ClientId,
OpenIdConnectConstants.TokenTypeHints.RefreshToken);
}
else {
// Persist a new token entry in the database
// and attach it to the user it corresponds to.
identifier = await services.Users.CreateTokenAsync(user,
OpenIdConnectConstants.TokenTypeHints.RefreshToken);
}
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TApplication, TAuthorization, TScope, TToken>>();
var identifier = await services.Tokens.CreateAsync(OpenIdConnectConstants.TokenTypeHints.RefreshToken);
if (string.IsNullOrEmpty(identifier)) {
throw new InvalidOperationException("The unique key associated with a refresh token cannot be null or empty.");
}

12
src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Session.cs

@ -19,10 +19,10 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Bson;
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 partial class OpenIddictProvider<TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
where TApplication : class where TAuthorization : class where TScope : class where TToken : class {
public override async Task ExtractLogoutRequest([NotNull] ExtractLogoutRequestContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TApplication, TAuthorization, TScope, TToken>>();
// If a request_id parameter can be found in the logout request,
// restore the complete logout request stored in the distributed cache.
@ -56,7 +56,7 @@ namespace OpenIddict.Infrastructure {
}
public override async Task ValidateLogoutRequest([NotNull] ValidateLogoutRequestContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TApplication, TAuthorization, TScope, TToken>>();
// Skip validation if the optional post_logout_redirect_uri
// parameter was missing from the logout request.
@ -86,7 +86,7 @@ namespace OpenIddict.Infrastructure {
}
public override async Task HandleLogoutRequest([NotNull] HandleLogoutRequestContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TApplication, TAuthorization, TScope, TToken>>();
// If no request_id parameter can be found in the current request, assume the OpenID Connect
// request was not serialized yet and store the entire payload in the distributed cache
@ -130,7 +130,7 @@ namespace OpenIddict.Infrastructure {
}
public override async Task ApplyLogoutResponse([NotNull] ApplyLogoutResponseContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TApplication, TAuthorization, TScope, TToken>>();
// Remove the logout request from the distributed cache.
if (!string.IsNullOrEmpty(context.Request.RequestId)) {

78
src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Userinfo.cs

@ -4,81 +4,19 @@
* the license and the contributors participating to this project.
*/
using System.Security.Claims;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
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, TAuthorization, TScope, TToken>>();
// Note: user may be null if the user was removed after the access token was issued.
var user = await services.Users.GetUserAsync(context.Ticket.Principal);
if (user == null) {
services.Logger.LogError("The userinfo request was aborted because the user profile " +
"corresponding to the access token was not found in the database.");
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidGrant,
description: "The access token is no longer valid.");
return;
}
// Note: "sub" is a mandatory claim.
// See http://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse
context.Subject = await services.Users.GetUserIdAsync(user);
// Only add the "preferred_username" claim if the "profile" scope was present in the access token.
// Note: filtering the username is not needed at this stage as OpenIddictController.Accept
// and OpenIddictProvider.HandleTokenRequest are expected to reject requests that don't
// include the "email" scope if the username corresponds to the registed email address.
if (context.Ticket.HasScope(OpenIdConnectConstants.Scopes.Profile)) {
context.PreferredUsername = await services.Users.GetUserNameAsync(user);
if (services.Users.SupportsUserClaim) {
context.FamilyName = await services.Users.FindClaimAsync(user, ClaimTypes.Surname);
context.GivenName = await services.Users.FindClaimAsync(user, ClaimTypes.GivenName);
context.BirthDate = await services.Users.FindClaimAsync(user, ClaimTypes.DateOfBirth);
}
}
// Only add the email address details if the "email" scope was present in the access token.
if (services.Users.SupportsUserEmail && context.Ticket.HasScope(OpenIdConnectConstants.Scopes.Email)) {
context.Email = await services.Users.GetEmailAsync(user);
// Only add the "email_verified" claim
// if the email address is non-null.
if (!string.IsNullOrEmpty(context.Email)) {
context.EmailVerified = await services.Users.IsEmailConfirmedAsync(user);
}
};
// Only add the phone number details if the "phone" scope was present in the access token.
if (services.Users.SupportsUserPhoneNumber && context.Ticket.HasScope(OpenIdConnectConstants.Scopes.Phone)) {
context.PhoneNumber = await services.Users.GetPhoneNumberAsync(user);
// Only add the "phone_number_verified"
// claim if the phone number is non-null.
if (!string.IsNullOrEmpty(context.PhoneNumber)) {
context.PhoneNumberVerified = await services.Users.IsPhoneNumberConfirmedAsync(user);
}
}
// Only add the roles list if the "roles" scope was present in the access token.
if (services.Users.SupportsUserRole && context.Ticket.HasScope(OpenIddictConstants.Scopes.Roles)) {
var roles = await services.Users.GetRolesAsync(user);
if (roles.Count != 0) {
context.Claims[OpenIddictConstants.Claims.Roles] = JArray.FromObject(roles);
}
}
public partial class OpenIddictProvider<TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
where TApplication : class where TAuthorization : class where TScope : class where TToken : class {
public override Task HandleUserinfoRequest([NotNull] HandleUserinfoRequestContext context) {
// Invoke the rest of the pipeline to allow
// the user code to handle the userinfo request.
context.SkipToNextMiddleware();
return Task.FromResult(0);
}
}
}

4
src/OpenIddict.Core/Infrastructure/OpenIddictProvider.cs

@ -9,8 +9,8 @@ using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations;
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 partial class OpenIddictProvider<TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
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

17
src/OpenIddict.Core/Infrastructure/OpenIddictServices.cs

@ -7,7 +7,6 @@
using System;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@ -16,8 +15,8 @@ namespace OpenIddict.Infrastructure {
/// <summary>
/// Exposes the common services used by OpenIddict.
/// </summary>
public class OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>
where TUser : class where TApplication : class where TAuthorization : class
public class OpenIddictServices<TApplication, TAuthorization, TScope, TToken>
where TApplication : class where TAuthorization : class
where TScope : class where TToken : class {
public OpenIddictServices([NotNull] IServiceProvider services) {
Services = services;
@ -38,7 +37,7 @@ namespace OpenIddict.Infrastructure {
/// Gets the <see cref="ILogger"/>.
/// </summary>
public virtual ILogger Logger =>
Services.GetRequiredService<ILogger<OpenIddictProvider<TUser, TApplication, TAuthorization, TScope, TToken>>>();
Services.GetRequiredService<ILogger<OpenIddictProvider<TApplication, TAuthorization, TScope, TToken>>>();
/// <summary>
/// Gets the <see cref="OpenIddictOptions"/>.
@ -50,19 +49,9 @@ namespace OpenIddict.Infrastructure {
/// </summary>
public virtual IServiceProvider Services { get; }
/// <summary>
/// Gets the <see cref="SignInManager{TUser}"/>.
/// </summary>
public virtual SignInManager<TUser> SignIn => Services.GetRequiredService<SignInManager<TUser>>();
/// <summary>
/// Gets the <see cref="OpenIddictTokenManager{TToken}"/>.
/// </summary>
public virtual OpenIddictTokenManager<TToken> Tokens => Services.GetRequiredService<OpenIddictTokenManager<TToken>>();
/// <summary>
/// Gets the <see cref="OpenIddictUserManager{TUser}"/>.
/// </summary>
public virtual OpenIddictUserManager<TUser> Users => Services.GetRequiredService<OpenIddictUserManager<TUser>>();
}
}

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

@ -8,11 +8,9 @@ using System;
using System.Threading;
using System.Threading.Tasks;
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 {
/// <summary>
@ -23,11 +21,9 @@ namespace OpenIddict {
public OpenIddictTokenManager(
[NotNull] IServiceProvider services,
[NotNull] IOpenIddictTokenStore<TToken> store,
[NotNull] IOptions<IdentityOptions> options,
[NotNull] ILogger<OpenIddictTokenManager<TToken>> logger) {
Context = services?.GetRequiredService<IHttpContextAccessor>()?.HttpContext;
Logger = logger;
Options = options.Value;
Store = store;
}
@ -46,11 +42,6 @@ namespace OpenIddict {
/// </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>

192
src/OpenIddict.Core/Managers/OpenIddictUserManager.cs

@ -1,192 +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.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 users stored in the store.
/// </summary>
/// <typeparam name="TUser">The type of the User entity.</typeparam>
public class OpenIddictUserManager<TUser> : UserManager<TUser> where TUser : class {
public OpenIddictUserManager(
[NotNull] IServiceProvider services,
[NotNull] IOpenIddictUserStore<TUser> store,
[NotNull] IOptions<IdentityOptions> options,
[NotNull] ILogger<OpenIddictUserManager<TUser>> logger,
[NotNull] IPasswordHasher<TUser> hasher,
[NotNull] IEnumerable<IUserValidator<TUser>> userValidators,
[NotNull] IEnumerable<IPasswordValidator<TUser>> passwordValidators,
[NotNull] ILookupNormalizer keyNormalizer,
[NotNull] IdentityErrorDescriber errors)
: base(store, options, hasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger) {
Context = services?.GetRequiredService<IHttpContextAccessor>()?.HttpContext;
Logger = logger;
Options = options.Value;
Store = store;
}
/// <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 identity options.
/// </summary>
protected IdentityOptions Options { get; }
/// <summary>
/// Gets the store associated with the current manager.
/// </summary>
protected new IOpenIddictUserStore<TUser> Store { 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,
/// whose result returns the <see cref="ClaimsIdentity"/> corresponding to the user.
/// </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 GetUserIdAsync(user));
// Only add the name claim if the "profile" scope was granted.
if (scopes.Contains(OpenIdConnectConstants.Scopes.Profile)) {
var username = await GetUserNameAsync(user);
identity.AddClaim(ClaimTypes.Name, username,
OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
}
// Only add the email address if the "email" scope was granted.
if (SupportsUserEmail && scopes.Contains(OpenIdConnectConstants.Scopes.Email)) {
var email = await GetEmailAsync(user);
identity.AddClaim(ClaimTypes.Email, email,
OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
}
if (SupportsUserRole && scopes.Contains(OpenIddictConstants.Scopes.Roles)) {
foreach (var role in await GetRolesAsync(user)) {
identity.AddClaim(identity.RoleClaimType, role,
OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
}
}
if (SupportsUserSecurityStamp) {
var stamp = await 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 associated with the given user.
/// </summary>
/// <param name="user">The user.</param>
/// <param name="type">The token type.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the token.
/// </returns>
public virtual Task<string> CreateTokenAsync(TUser user, string type) {
if (user == null) {
throw new ArgumentNullException(nameof(user));
}
if (string.IsNullOrEmpty(type)) {
throw new ArgumentException("The token type cannot be null or empty.", nameof(type));
}
return Store.CreateTokenAsync(user, type, CancellationToken);
}
/// <summary>
/// Creates a new token associated with the given user and
/// attached to the tokens issued to the specified client.
/// </summary>
/// <param name="user">The user.</param>
/// <param name="client">The application.</param>
/// <param name="type">The token type.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the token.
/// </returns>
public virtual Task<string> CreateTokenAsync(TUser user, string client, string type) {
if (user == null) {
throw new ArgumentNullException(nameof(user));
}
if (string.IsNullOrEmpty(type)) {
throw new ArgumentException("The token type cannot be null or empty.", nameof(type));
}
return Store.CreateTokenAsync(user, client, type, CancellationToken);
}
/// <summary>
/// Retrieves the token identifiers associated with a user.
/// </summary>
/// <param name="user">The user.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the tokens associated with the user.
/// </returns>
public virtual Task<IEnumerable<string>> GetTokensAsync(TUser user) {
if (user == null) {
throw new ArgumentNullException(nameof(user));
}
return Store.GetTokensAsync(user, CancellationToken);
}
}
}

44
src/OpenIddict.Core/OpenIddictBuilder.cs

@ -44,12 +44,6 @@ namespace Microsoft.AspNetCore.Builder {
[EditorBrowsable(EditorBrowsableState.Never)]
public Type AuthorizationType { get; set; }
/// <summary>
/// Gets or sets the type corresponding to the Role entity.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public Type RoleType { get; set; }
/// <summary>
/// Gets or sets the type corresponding to the Scope entity.
/// </summary>
@ -62,12 +56,6 @@ namespace Microsoft.AspNetCore.Builder {
[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>
@ -218,38 +206,6 @@ namespace Microsoft.AspNetCore.Builder {
return this;
}
/// <summary>
/// Adds a custom user manager.
/// </summary>
/// <typeparam name="TManager">The type of the custom manager.</typeparam>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AddUserManager<TManager>() {
var contract = typeof(OpenIddictUserManager<>).MakeGenericType(UserType);
if (!contract.IsAssignableFrom(typeof(TManager))) {
throw new InvalidOperationException("Custom managers must be derived from OpenIddictUserManager.");
}
Services.AddScoped(contract, typeof(TManager));
return this;
}
/// <summary>
/// Adds a custom user store.
/// </summary>
/// <typeparam name="TStore">The type of the custom store.</typeparam>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AddUserStore<TStore>() {
var contract = typeof(IOpenIddictUserStore<>).MakeGenericType(UserType);
if (!contract.IsAssignableFrom(typeof(TStore))) {
throw new InvalidOperationException("Custom stores must implement IOpenIddictUserStore.");
}
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.

21
src/OpenIddict.Core/OpenIddictExtensions.cs

@ -21,24 +21,14 @@ namespace Microsoft.AspNetCore.Builder {
/// 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="TRole">The type of the Role 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, TRole, TApplication, TAuthorization, TScope, TToken>(
public static OpenIddictBuilder AddOpenIddict<TApplication, TAuthorization, TScope, TToken>(
[NotNull] this IServiceCollection services)
where TUser : class
where TRole : class
where TApplication : class
where TAuthorization : class
where TScope : class
@ -50,10 +40,8 @@ namespace Microsoft.AspNetCore.Builder {
var builder = new OpenIddictBuilder(services) {
ApplicationType = typeof(TApplication),
AuthorizationType = typeof(TAuthorization),
RoleType = typeof(TRole),
ScopeType = typeof(TScope),
TokenType = typeof(TToken),
UserType = typeof(TUser)
TokenType = typeof(TToken)
};
// Register the services required by the OpenID Connect server middleware.
@ -62,7 +50,7 @@ namespace Microsoft.AspNetCore.Builder {
builder.Configure(options => {
// Register the OpenID Connect server provider in the OpenIddict options.
options.Provider = new OpenIddictProvider<TUser, TApplication, TAuthorization, TScope, TToken>();
options.Provider = new OpenIddictProvider<TApplication, TAuthorization, TScope, TToken>();
});
// Register the OpenIddict core services in the DI container.
@ -70,8 +58,7 @@ namespace Microsoft.AspNetCore.Builder {
builder.Services.TryAddScoped<OpenIddictAuthorizationManager<TAuthorization>>();
builder.Services.TryAddScoped<OpenIddictScopeManager<TScope>>();
builder.Services.TryAddScoped<OpenIddictTokenManager<TToken>>();
builder.Services.TryAddScoped<OpenIddictUserManager<TUser>>();
builder.Services.TryAddScoped<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
builder.Services.TryAddScoped<OpenIddictServices<TApplication, TAuthorization, TScope, TToken>>();
return builder;
}

55
src/OpenIddict.Core/Stores/IOpenIddictUserStore.cs

@ -1,55 +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.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
namespace OpenIddict {
/// <summary>
/// Provides methods allowing to manage the users stored in a database.
/// </summary>
/// <typeparam name="TUser">The type of the User entity.</typeparam>
public interface IOpenIddictUserStore<TUser> : IUserStore<TUser> where TUser : class {
/// <summary>
/// Creates a new token associated with the given user.
/// </summary>
/// <param name="user">The user associated with the token.</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,
/// whose result returns the unique identifier associated with the token.
/// </returns>
Task<string> CreateTokenAsync(TUser user, string type, CancellationToken cancellationToken);
/// <summary>
/// Creates a new token associated with the given user and
/// attached to the tokens issued to the specified client.
/// </summary>
/// <param name="user">The user.</param>
/// <param name="client">The application.</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,
/// whose result returns the unique identifier associated with the token.
/// </returns>
Task<string> CreateTokenAsync(TUser user, string client, string type, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the token identifiers associated with a user.
/// </summary>
/// <param name="user">The user.</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,
/// whose result returns the tokens associated with the user.
/// </returns>
Task<IEnumerable<string>> GetTokensAsync(TUser user, CancellationToken cancellationToken);
}
}

3
src/OpenIddict.Core/project.json

@ -1,5 +1,5 @@
{
"version": "1.0.0-alpha2-*",
"version": "1.0.0-beta1-*",
"description": "Core components of OpenIddict.",
"authors": [ "Kévin Chalet" ],
@ -37,7 +37,6 @@
"CryptoHelper": "2.0.0",
"JetBrains.Annotations": { "type": "build", "version": "10.1.4" },
"Microsoft.AspNetCore.Diagnostics.Abstractions": "1.0.0",
"Microsoft.AspNetCore.Identity": "1.0.0",
"Microsoft.Extensions.Caching.Memory": "1.0.0"
},

16
src/OpenIddict.EntityFramework/OpenIddictExtensions.cs

@ -37,7 +37,6 @@ namespace Microsoft.AspNetCore.Builder {
Debug.Assert(builder.ApplicationType != null &&
builder.AuthorizationType != null &&
builder.RoleType != null &&
builder.ScopeType != null &&
builder.TokenType != null, "The entity types exposed by OpenIddictBuilder shouldn't be null.");
@ -70,22 +69,9 @@ namespace Microsoft.AspNetCore.Builder {
// Register the token store in the DI container.
builder.Services.TryAddScoped(
typeof(IOpenIddictTokenStore<>).MakeGenericType(builder.TokenType),
typeof(OpenIddictTokenStore<,,,,>).MakeGenericType(
typeof(OpenIddictTokenStore<,,,>).MakeGenericType(
/* TToken: */ builder.TokenType,
/* TAuthorization: */ builder.AuthorizationType,
/* TUser: */ builder.UserType,
/* TContext: */ typeof(TContext),
/* TKey: */ typeof(TKey)));
// Register the token store in the DI container.
builder.Services.TryAddScoped(
typeof(IOpenIddictUserStore<>).MakeGenericType(builder.UserType),
typeof(OpenIddictUserStore<,,,,,,>).MakeGenericType(
/* TUser: */ builder.UserType,
/* TApplication: */ builder.ApplicationType,
/* TAuthorization: */ builder.AuthorizationType,
/* TRole: */ builder.RoleType,
/* TToken: */ builder.TokenType,
/* TContext: */ typeof(TContext),
/* TKey: */ typeof(TKey)));

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

@ -16,13 +16,11 @@ namespace OpenIddict {
/// </summary>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <typeparam name="TUser">The type of the User 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, TAuthorization, TUser, TContext, TKey> : IOpenIddictTokenStore<TToken>
public class OpenIddictTokenStore<TToken, TAuthorization, TContext, TKey> : IOpenIddictTokenStore<TToken>
where TToken : OpenIddictToken<TKey>, new()
where TAuthorization : OpenIddictAuthorization<TKey, TToken>
where TUser : OpenIddictUser<TKey, TAuthorization, TToken>
where TContext : DbContext
where TKey : IEquatable<TKey> {
public OpenIddictTokenStore(TContext context) {

162
src/OpenIddict.EntityFramework/Stores/OpenIddictUserStore.cs

@ -1,162 +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.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace OpenIddict {
/// <summary>
/// Provides methods allowing to manage the users stored in a database.
/// </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="TRole">The type of the Role 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 OpenIddictUserStore<TUser, TApplication, TAuthorization, TRole, TToken, TContext, TKey> :
UserStore<TUser, TRole, TContext, TKey>, IOpenIddictUserStore<TUser>
where TUser : OpenIddictUser<TKey, TAuthorization, TToken>, new()
where TApplication : OpenIddictApplication<TKey, TToken>
where TAuthorization : OpenIddictAuthorization<TKey, TToken>
where TRole : IdentityRole<TKey>
where TToken : OpenIddictToken<TKey>, new()
where TContext : DbContext
where TKey : IEquatable<TKey> {
public OpenIddictUserStore(TContext context)
: base(context) { }
/// <summary>
/// Gets the database set corresponding to the <typeparamref name="TApplication"/> entity.
/// </summary>
protected DbSet<TApplication> Applications => Context.Set<TApplication>();
/// <summary>
/// Creates a new token associated with the given user and defined by a unique identifier and a token type.
/// </summary>
/// <param name="user">The user associated with the token.</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,
/// whose result returns the unique identifier associated with the token.
/// </returns>
public virtual async Task<string> CreateTokenAsync(TUser user, string type, CancellationToken cancellationToken) {
if (user == null) {
throw new ArgumentNullException(nameof(user));
}
if (string.IsNullOrEmpty(type)) {
throw new ArgumentException("The token type cannot be null or empty.");
}
// Ensure that the key type can be serialized.
var converter = TypeDescriptor.GetConverter(typeof(TKey));
if (!converter.CanConvertTo(typeof(string))) {
throw new InvalidOperationException($"The '{typeof(TKey).Name}' key type is not supported.");
}
var token = new TToken { Type = type };
user.Tokens.Add(token);
Context.Update(user);
await Context.SaveChangesAsync(cancellationToken);
return converter.ConvertToInvariantString(token.Id);
}
/// <summary>
/// Creates a new token associated with the given user and
/// attached to the tokens issued to the specified client.
/// </summary>
/// <param name="user">The user.</param>
/// <param name="client">The application.</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,
/// whose result returns the unique identifier associated with the token.
/// </returns>
public virtual async Task<string> CreateTokenAsync(TUser user, string client, string type, CancellationToken cancellationToken) {
if (user == null) {
throw new ArgumentNullException(nameof(user));
}
if (string.IsNullOrEmpty(client)) {
throw new ArgumentException("The client identifier cannot be null or empty.");
}
if (string.IsNullOrEmpty(type)) {
throw new ArgumentException("The token type cannot be null or empty.");
}
// Ensure that the key type can be serialized.
var converter = TypeDescriptor.GetConverter(typeof(TKey));
if (!converter.CanConvertTo(typeof(string))) {
throw new InvalidOperationException($"The '{typeof(TKey).Name}' key type is not supported.");
}
var application = await Applications.FirstOrDefaultAsync(entity => entity.ClientId.Equals(client), cancellationToken);
if (application == null) {
throw new InvalidOperationException("The application cannot be found in the database.");
}
var token = new TToken { Type = type };
application.Tokens.Add(token);
user.Tokens.Add(token);
Context.Update(application);
Context.Update(user);
await Context.SaveChangesAsync(cancellationToken);
return converter.ConvertToInvariantString(token.Id);
}
/// <summary>
/// Retrieves the token identifiers associated with a user.
/// </summary>
/// <param name="user">The user.</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,
/// whose result returns the tokens associated with the user.
/// </returns>
public virtual async Task<IEnumerable<string>> GetTokensAsync(TUser user, CancellationToken cancellationToken) {
if (user == null) {
throw new ArgumentNullException(nameof(user));
}
// Ensure that the key type can be serialized.
var converter = TypeDescriptor.GetConverter(typeof(TKey));
if (!converter.CanConvertTo(typeof(string))) {
throw new InvalidOperationException($"The '{typeof(TKey).Name}' key type is not supported.");
}
var query = from entity in Users
where entity.Id.Equals(user.Id)
from token in entity.Tokens
select token.Id;
var tokens = new List<string>();
foreach (var identifier in await query.ToArrayAsync()) {
tokens.Add(converter.ConvertToInvariantString(identifier));
}
return tokens;
}
}
}

2
src/OpenIddict.EntityFramework/project.json

@ -1,5 +1,5 @@
{
"version": "1.0.0-alpha2-*",
"version": "1.0.0-beta1-*",
"description": "Entity Framework adapter for OpenIddict.",
"authors": [ "Kévin Chalet" ],

2
src/OpenIddict.Mvc/project.json

@ -1,5 +1,5 @@
{
"version": "1.0.0-alpha2-*",
"version": "1.0.0-beta1-*",
"description": "OpenIddict binders for ASP.NET Core MVC.",
"authors": [ "Kévin Chalet" ],

102
src/OpenIddict/OpenIddictExtensions.cs

@ -6,7 +6,6 @@
using System;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using OpenIddict;
@ -19,12 +18,6 @@ namespace Microsoft.AspNetCore.Builder {
/// </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 {
@ -32,102 +25,37 @@ namespace Microsoft.AspNetCore.Builder {
throw new ArgumentNullException(nameof(services));
}
return services.AddOpenIddict<OpenIddictUser, TContext>();
return services.AddOpenIddict<OpenIddictApplication,
OpenIddictAuthorization,
OpenIddictScope,
OpenIddictToken, TContext, string>();
}
/// <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 services.AddOpenIddict<TUser, IdentityRole, OpenIddictApplication,
OpenIddictAuthorization,
OpenIddictScope,
OpenIddictToken, TContext, string>();
}
/// <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="TRole">The type of the Role 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, TRole, TContext>([NotNull] this IServiceCollection services)
where TUser : OpenIddictUser
where TRole : IdentityRole
where TContext : DbContext {
if (services == null) {
throw new ArgumentNullException(nameof(services));
}
return services.AddOpenIddict<TUser, TRole, OpenIddictApplication,
OpenIddictAuthorization,
OpenIddictScope,
OpenIddictToken, TContext, string>();
}
/// <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="TRole">The type of the Role 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="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddOpenIddict<TUser, TRole, TContext, TKey>([NotNull] this IServiceCollection services)
where TUser : OpenIddictUser<TKey>
where TRole : IdentityRole<TKey>
public static OpenIddictBuilder AddOpenIddict<TContext, TKey>([NotNull] this IServiceCollection services)
where TContext : DbContext
where TKey : IEquatable<TKey> {
if (services == null) {
throw new ArgumentNullException(nameof(services));
}
return services.AddOpenIddict<TUser, TRole, OpenIddictApplication<TKey>,
OpenIddictAuthorization<TKey>,
OpenIddictScope<TKey>,
OpenIddictToken<TKey>, TContext, TKey>();
return services.AddOpenIddict<OpenIddictApplication<TKey>,
OpenIddictAuthorization<TKey>,
OpenIddictScope<TKey>,
OpenIddictToken<TKey>, TContext, TKey>();
}
/// <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="TRole">The type of the Role 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>
@ -135,17 +63,9 @@ namespace Microsoft.AspNetCore.Builder {
/// <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, TRole, TApplication, TAuthorization, TScope, TToken, TContext, TKey>(
public static OpenIddictBuilder AddOpenIddict<TApplication, TAuthorization, TScope, TToken, TContext, TKey>(
[NotNull] this IServiceCollection services)
where TUser : OpenIddictUser<TKey, TAuthorization, TToken>
where TRole : IdentityRole<TKey>
where TApplication : OpenIddictApplication<TKey, TToken>
where TAuthorization : OpenIddictAuthorization<TKey, TToken>
where TScope : OpenIddictScope<TKey>
@ -157,7 +77,7 @@ namespace Microsoft.AspNetCore.Builder {
}
// Register the OpenIddict core services and the default EntityFramework stores.
return services.AddOpenIddict<TUser, TRole, TApplication, TAuthorization, TScope, TToken>()
return services.AddOpenIddict<TApplication, TAuthorization, TScope, TToken>()
.AddEntityFramework<TContext, TKey>();
}
}

2
src/OpenIddict/project.json

@ -1,5 +1,5 @@
{
"version": "1.0.0-alpha2-*",
"version": "1.0.0-beta1-*",
"description": "Easy-to-use OpenID Connect server for ASP.NET Core.",
"authors": [ "Kévin Chalet" ],

Loading…
Cancel
Save