Browse Source

Update the code base to use file-scoped namespaces

pull/1336/head
Kévin Chalet 5 years ago
parent
commit
148a987a23
  1. 35
      samples/Mvc.Client/Controllers/AuthenticationController.cs
  2. 47
      samples/Mvc.Client/Controllers/HomeController.cs
  3. 17
      samples/Mvc.Client/Program.cs
  4. 121
      samples/Mvc.Client/Startup.cs
  5. 749
      samples/Mvc.Server/Controllers/AccountController.cs
  6. 843
      samples/Mvc.Server/Controllers/AuthorizationController.cs
  7. 35
      samples/Mvc.Server/Controllers/ErrorController.cs
  8. 39
      samples/Mvc.Server/Controllers/HomeController.cs
  9. 543
      samples/Mvc.Server/Controllers/ManageController.cs
  10. 79
      samples/Mvc.Server/Controllers/ResourceController.cs
  11. 89
      samples/Mvc.Server/Controllers/UserinfoController.cs
  12. 33
      samples/Mvc.Server/Helpers/AsyncEnumerableExtensions.cs
  13. 47
      samples/Mvc.Server/Helpers/FormValueRequiredAttribute.cs
  14. 23
      samples/Mvc.Server/Models/ApplicationDbContext.cs
  15. 9
      samples/Mvc.Server/Models/ApplicationUser.cs
  16. 17
      samples/Mvc.Server/Program.cs
  17. 9
      samples/Mvc.Server/Services/IEmailSender.cs
  18. 9
      samples/Mvc.Server/Services/ISmsSender.cs
  19. 29
      samples/Mvc.Server/Services/MessageServices.cs
  20. 341
      samples/Mvc.Server/Startup.cs
  21. 13
      samples/Mvc.Server/ViewModels/Account/ExternalLoginConfirmationViewModel.cs
  22. 13
      samples/Mvc.Server/ViewModels/Account/ForgotPasswordViewModel.cs
  23. 23
      samples/Mvc.Server/ViewModels/Account/LoginViewModel.cs
  24. 33
      samples/Mvc.Server/ViewModels/Account/RegisterViewModel.cs
  25. 31
      samples/Mvc.Server/ViewModels/Account/ResetPasswordViewModel.cs
  26. 15
      samples/Mvc.Server/ViewModels/Account/SendCodeViewModel.cs
  27. 25
      samples/Mvc.Server/ViewModels/Account/VerifyCodeViewModel.cs
  28. 15
      samples/Mvc.Server/ViewModels/Authorization/AuthorizeViewModel.cs
  29. 29
      samples/Mvc.Server/ViewModels/Authorization/VerifyViewModel.cs
  30. 15
      samples/Mvc.Server/ViewModels/Manage/AddPhoneNumberViewModel.cs
  31. 33
      samples/Mvc.Server/ViewModels/Manage/ChangePasswordViewModel.cs
  32. 11
      samples/Mvc.Server/ViewModels/Manage/ConfigureTwoFactorViewModel.cs
  33. 9
      samples/Mvc.Server/ViewModels/Manage/FactorViewModel.cs
  34. 17
      samples/Mvc.Server/ViewModels/Manage/IndexViewModel.cs
  35. 11
      samples/Mvc.Server/ViewModels/Manage/ManageLoginsViewModel.cs
  36. 13
      samples/Mvc.Server/ViewModels/Manage/RemoveLoginViewModel.cs
  37. 25
      samples/Mvc.Server/ViewModels/Manage/SetPasswordViewModel.cs
  38. 19
      samples/Mvc.Server/ViewModels/Manage/VerifyPhoneNumberViewModel.cs
  39. 15
      samples/Mvc.Server/ViewModels/Shared/ErrorViewModel.cs
  40. 211
      samples/Mvc.Server/Worker.cs
  41. 101
      shared/OpenIddict.Extensions/Helpers/OpenIddictHelpers.cs
  42. 107
      src/OpenIddict.Abstractions/Caches/IOpenIddictApplicationCache.cs
  43. 173
      src/OpenIddict.Abstractions/Caches/IOpenIddictAuthorizationCache.cs
  44. 107
      src/OpenIddict.Abstractions/Caches/IOpenIddictScopeCache.cs
  45. 183
      src/OpenIddict.Abstractions/Caches/IOpenIddictTokenCache.cs
  46. 101
      src/OpenIddict.Abstractions/Descriptors/OpenIddictApplicationDescriptor.cs
  47. 91
      src/OpenIddict.Abstractions/Descriptors/OpenIddictAuthorizationDescriptor.cs
  48. 77
      src/OpenIddict.Abstractions/Descriptors/OpenIddictScopeDescriptor.cs
  49. 111
      src/OpenIddict.Abstractions/Descriptors/OpenIddictTokenDescriptor.cs
  50. 901
      src/OpenIddict.Abstractions/Managers/IOpenIddictApplicationManager.cs
  51. 811
      src/OpenIddict.Abstractions/Managers/IOpenIddictAuthorizationManager.cs
  52. 725
      src/OpenIddict.Abstractions/Managers/IOpenIddictScopeManager.cs
  53. 907
      src/OpenIddict.Abstractions/Managers/IOpenIddictTokenManager.cs
  54. 49
      src/OpenIddict.Abstractions/OpenIddictBuilder.cs
  55. 841
      src/OpenIddict.Abstractions/OpenIddictConstants.cs
  56. 225
      src/OpenIddict.Abstractions/OpenIddictExceptions.cs
  57. 71
      src/OpenIddict.Abstractions/OpenIddictExtensions.cs
  58. 103
      src/OpenIddict.Abstractions/Primitives/OpenIddictConverter.cs
  59. 2809
      src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs
  60. 611
      src/OpenIddict.Abstractions/Primitives/OpenIddictMessage.cs
  61. 1335
      src/OpenIddict.Abstractions/Primitives/OpenIddictParameter.cs
  62. 875
      src/OpenIddict.Abstractions/Primitives/OpenIddictRequest.cs
  63. 371
      src/OpenIddict.Abstractions/Primitives/OpenIddictResponse.cs
  64. 23
      src/OpenIddict.Abstractions/Resolvers/IOpenIddictApplicationStoreResolver.cs
  65. 23
      src/OpenIddict.Abstractions/Resolvers/IOpenIddictAuthorizationStoreResolver.cs
  66. 23
      src/OpenIddict.Abstractions/Resolvers/IOpenIddictScopeStoreResolver.cs
  67. 23
      src/OpenIddict.Abstractions/Resolvers/IOpenIddictTokenStoreResolver.cs
  68. 755
      src/OpenIddict.Abstractions/Stores/IOpenIddictApplicationStore.cs
  69. 687
      src/OpenIddict.Abstractions/Stores/IOpenIddictAuthorizationStore.cs
  70. 529
      src/OpenIddict.Abstractions/Stores/IOpenIddictScopeStore.cs
  71. 855
      src/OpenIddict.Abstractions/Stores/IOpenIddictTokenStore.cs
  72. 507
      src/OpenIddict.Core/Caches/OpenIddictApplicationCache.cs
  73. 773
      src/OpenIddict.Core/Caches/OpenIddictAuthorizationCache.cs
  74. 469
      src/OpenIddict.Core/Caches/OpenIddictScopeCache.cs
  75. 845
      src/OpenIddict.Core/Caches/OpenIddictTokenCache.cs
  76. 2653
      src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs
  77. 2113
      src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
  78. 1723
      src/OpenIddict.Core/Managers/OpenIddictScopeManager.cs
  79. 2415
      src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs
  80. 1371
      src/OpenIddict.Core/OpenIddictCoreBuilder.cs
  81. 177
      src/OpenIddict.Core/OpenIddictCoreExtensions.cs
  82. 95
      src/OpenIddict.Core/OpenIddictCoreOptions.cs
  83. 37
      src/OpenIddict.Core/Resolvers/OpenIddictApplicationStoreResolver.cs
  84. 37
      src/OpenIddict.Core/Resolvers/OpenIddictAuthorizationStoreResolver.cs
  85. 37
      src/OpenIddict.Core/Resolvers/OpenIddictScopeStoreResolver.cs
  86. 37
      src/OpenIddict.Core/Resolvers/OpenIddictTokenStoreResolver.cs
  87. 203
      src/OpenIddict.EntityFramework.Models/OpenIddictEntityFrameworkApplication.cs
  88. 119
      src/OpenIddict.EntityFramework.Models/OpenIddictEntityFrameworkAuthorization.cs
  89. 111
      src/OpenIddict.EntityFramework.Models/OpenIddictEntityFrameworkScope.cs
  90. 175
      src/OpenIddict.EntityFramework.Models/OpenIddictEntityFrameworkToken.cs
  91. 97
      src/OpenIddict.EntityFramework/Configurations/OpenIddictEntityFrameworkApplicationConfiguration.cs
  92. 71
      src/OpenIddict.EntityFramework/Configurations/OpenIddictEntityFrameworkAuthorizationConfiguration.cs
  93. 53
      src/OpenIddict.EntityFramework/Configurations/OpenIddictEntityFrameworkScopeConfiguration.cs
  94. 73
      src/OpenIddict.EntityFramework/Configurations/OpenIddictEntityFrameworkTokenConfiguration.cs
  95. 185
      src/OpenIddict.EntityFramework/OpenIddictEntityFrameworkBuilder.cs
  96. 117
      src/OpenIddict.EntityFramework/OpenIddictEntityFrameworkExtensions.cs
  97. 121
      src/OpenIddict.EntityFramework/OpenIddictEntityFrameworkHelpers.cs
  98. 23
      src/OpenIddict.EntityFramework/OpenIddictEntityFrameworkOptions.cs
  99. 101
      src/OpenIddict.EntityFramework/Resolvers/OpenIddictEntityFrameworkApplicationStoreResolver.cs
  100. 101
      src/OpenIddict.EntityFramework/Resolvers/OpenIddictEntityFrameworkAuthorizationStoreResolver.cs

35
samples/Mvc.Client/Controllers/AuthenticationController.cs

@ -3,25 +3,24 @@ using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Mvc;
namespace Mvc.Client.Controllers
namespace Mvc.Client.Controllers;
public class AuthenticationController : Controller
{
public class AuthenticationController : Controller
[HttpGet("~/login")]
public ActionResult LogIn()
{
[HttpGet("~/login")]
public ActionResult LogIn()
{
// Instruct the OIDC client middleware to redirect the user agent to the identity provider.
// Note: the authenticationType parameter must match the value configured in Startup.cs
return Challenge(new AuthenticationProperties { RedirectUri = "/" }, OpenIdConnectDefaults.AuthenticationScheme);
}
// Instruct the OIDC client middleware to redirect the user agent to the identity provider.
// Note: the authenticationType parameter must match the value configured in Startup.cs
return Challenge(new AuthenticationProperties { RedirectUri = "/" }, OpenIdConnectDefaults.AuthenticationScheme);
}
[HttpGet("~/logout"), HttpPost("~/logout")]
public ActionResult LogOut()
{
// Instruct the cookies middleware to delete the local cookie created when the user agent
// is redirected from the identity provider after a successful authorization flow and
// to redirect the user agent to the identity provider to sign out.
return SignOut(CookieAuthenticationDefaults.AuthenticationScheme, OpenIdConnectDefaults.AuthenticationScheme);
}
[HttpGet("~/logout"), HttpPost("~/logout")]
public ActionResult LogOut()
{
// Instruct the cookies middleware to delete the local cookie created when the user agent
// is redirected from the identity provider after a successful authorization flow and
// to redirect the user agent to the identity provider to sign out.
return SignOut(CookieAuthenticationDefaults.AuthenticationScheme, OpenIdConnectDefaults.AuthenticationScheme);
}
}
}

47
samples/Mvc.Client/Controllers/HomeController.cs

@ -9,37 +9,36 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Mvc.Client.Controllers
namespace Mvc.Client.Controllers;
public class HomeController : Controller
{
public class HomeController : Controller
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly IHttpClientFactory _httpClientFactory;
public HomeController(IHttpClientFactory httpClientFactory)
=> _httpClientFactory = httpClientFactory;
public HomeController(IHttpClientFactory httpClientFactory)
=> _httpClientFactory = httpClientFactory;
[HttpGet("~/")]
public ActionResult Index() => View("Home");
[HttpGet("~/")]
public ActionResult Index() => View("Home");
[Authorize, HttpPost("~/")]
public async Task<ActionResult> Index(CancellationToken cancellationToken)
[Authorize, HttpPost("~/")]
public async Task<ActionResult> Index(CancellationToken cancellationToken)
{
var token = await HttpContext.GetTokenAsync(CookieAuthenticationDefaults.AuthenticationScheme, OpenIdConnectParameterNames.AccessToken);
if (string.IsNullOrEmpty(token))
{
var token = await HttpContext.GetTokenAsync(CookieAuthenticationDefaults.AuthenticationScheme, OpenIdConnectParameterNames.AccessToken);
if (string.IsNullOrEmpty(token))
{
throw new InvalidOperationException("The access token cannot be found in the authentication ticket. " +
"Make sure that SaveTokens is set to true in the OIDC options.");
}
throw new InvalidOperationException("The access token cannot be found in the authentication ticket. " +
"Make sure that SaveTokens is set to true in the OIDC options.");
}
using var client = _httpClientFactory.CreateClient();
using var client = _httpClientFactory.CreateClient();
using var request = new HttpRequestMessage(HttpMethod.Get, "https://localhost:44395/api/message");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
using var request = new HttpRequestMessage(HttpMethod.Get, "https://localhost:44395/api/message");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
using var response = await client.SendAsync(request, cancellationToken);
response.EnsureSuccessStatusCode();
using var response = await client.SendAsync(request, cancellationToken);
response.EnsureSuccessStatusCode();
return View("Home", model: await response.Content.ReadAsStringAsync(cancellationToken));
}
return View("Home", model: await response.Content.ReadAsStringAsync(cancellationToken));
}
}
}

17
samples/Mvc.Client/Program.cs

@ -1,15 +1,14 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace Mvc.Client
namespace Mvc.Client;
public static class Program
{
public static class Program
{
public static void Main(string[] args) =>
CreateHostBuilder(args).Build().Run();
public static void Main(string[] args) =>
CreateHostBuilder(args).Build().Run();
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(builder => builder.UseStartup<Startup>());
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(builder => builder.UseStartup<Startup>());
}

121
samples/Mvc.Client/Startup.cs

@ -7,80 +7,79 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Mvc.Client
namespace Mvc.Client;
public class Startup
{
public class Startup
public void ConfigureServices(IServiceCollection services)
{
public void ConfigureServices(IServiceCollection services)
services.AddAuthentication(options =>
{
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
options.LoginPath = "/login";
options.ExpireTimeSpan = TimeSpan.FromMinutes(50);
options.SlidingExpiration = false;
})
.AddCookie(options =>
{
options.LoginPath = "/login";
options.ExpireTimeSpan = TimeSpan.FromMinutes(50);
options.SlidingExpiration = false;
})
.AddOpenIdConnect(options =>
.AddOpenIdConnect(options =>
{
// Note: these settings must match the application details
// inserted in the database at the server level.
options.ClientId = "mvc";
options.ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654";
options.RequireHttpsMetadata = false;
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
// Use the authorization code flow.
options.ResponseType = OpenIdConnectResponseType.Code;
options.AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet;
// Note: setting the Authority allows the OIDC client middleware to automatically
// retrieve the identity provider's configuration and spare you from setting
// the different endpoints URIs or the token validation parameters explicitly.
options.Authority = "https://localhost:44395/";
options.Scope.Add("email");
options.Scope.Add("roles");
options.Scope.Add("offline_access");
options.Scope.Add("demo_api");
options.SecurityTokenValidator = new JwtSecurityTokenHandler
{
// Note: these settings must match the application details
// inserted in the database at the server level.
options.ClientId = "mvc";
options.ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654";
options.RequireHttpsMetadata = false;
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
// Use the authorization code flow.
options.ResponseType = OpenIdConnectResponseType.Code;
options.AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet;
// Disable the built-in JWT claims mapping feature.
InboundClaimTypeMap = new Dictionary<string, string>()
};
// Note: setting the Authority allows the OIDC client middleware to automatically
// retrieve the identity provider's configuration and spare you from setting
// the different endpoints URIs or the token validation parameters explicitly.
options.Authority = "https://localhost:44395/";
options.TokenValidationParameters.NameClaimType = "name";
options.TokenValidationParameters.RoleClaimType = "role";
options.Scope.Add("email");
options.Scope.Add("roles");
options.Scope.Add("offline_access");
options.Scope.Add("demo_api");
options.AccessDeniedPath = "/";
});
options.SecurityTokenValidator = new JwtSecurityTokenHandler
{
// Disable the built-in JWT claims mapping feature.
InboundClaimTypeMap = new Dictionary<string, string>()
};
services.AddHttpClient();
options.TokenValidationParameters.NameClaimType = "name";
options.TokenValidationParameters.RoleClaimType = "role";
options.AccessDeniedPath = "/";
});
services.AddHttpClient();
services.AddControllersWithViews();
}
services.AddControllersWithViews();
}
public void Configure(IApplicationBuilder app)
{
app.UseDeveloperExceptionPage();
public void Configure(IApplicationBuilder app)
{
app.UseDeveloperExceptionPage();
app.UseStaticFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(options => options.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}"));
}
app.UseEndpoints(options => options.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}"));
}
}
}

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

@ -9,463 +9,462 @@ using Mvc.Server.Models;
using Mvc.Server.Services;
using Mvc.Server.ViewModels.Account;
namespace Mvc.Server.Controllers
namespace Mvc.Server.Controllers;
[Authorize]
public class AccountController : Controller
{
[Authorize]
public class AccountController : Controller
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IEmailSender _emailSender;
private readonly ISmsSender _smsSender;
private readonly ApplicationDbContext _applicationDbContext;
private static bool _databaseChecked;
public AccountController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IEmailSender emailSender,
ISmsSender smsSender,
ApplicationDbContext applicationDbContext)
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IEmailSender _emailSender;
private readonly ISmsSender _smsSender;
private readonly ApplicationDbContext _applicationDbContext;
private static bool _databaseChecked;
public AccountController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IEmailSender emailSender,
ISmsSender smsSender,
ApplicationDbContext applicationDbContext)
{
_userManager = userManager;
_signInManager = signInManager;
_emailSender = emailSender;
_smsSender = smsSender;
_applicationDbContext = applicationDbContext;
}
_userManager = userManager;
_signInManager = signInManager;
_emailSender = emailSender;
_smsSender = smsSender;
_applicationDbContext = applicationDbContext;
}
//
// GET: /Account/Login
[HttpGet]
[AllowAnonymous]
public IActionResult Login(string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
return View();
}
//
// GET: /Account/Login
[HttpGet]
[AllowAnonymous]
public IActionResult Login(string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
return View();
}
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
EnsureDatabaseCreated(_applicationDbContext);
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
EnsureDatabaseCreated(_applicationDbContext);
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
return RedirectToLocal(returnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
}
if (result.IsLockedOut)
{
return View("Lockout");
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return View(model);
}
return RedirectToLocal(returnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
}
if (result.IsLockedOut)
{
return View("Lockout");
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return View(model);
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// GET: /Account/Register
[HttpGet]
[AllowAnonymous]
public IActionResult Register(string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
return View();
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
//
// GET: /Account/Register
[HttpGet]
[AllowAnonymous]
public IActionResult Register(string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
return View();
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
{
EnsureDatabaseCreated(_applicationDbContext);
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
EnsureDatabaseCreated(_applicationDbContext);
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=532713
// Send an email with this link
//var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
//var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Context.Request.Scheme);
//await _emailSender.SendEmailAsync(model.Email, "Confirm your account",
// "Please confirm your account by clicking this link: <a href=\"" + callbackUrl + "\">link</a>");
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToLocal(returnUrl);
}
AddErrors(result);
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=532713
// Send an email with this link
//var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
//var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Context.Request.Scheme);
//await _emailSender.SendEmailAsync(model.Email, "Confirm your account",
// "Please confirm your account by clicking this link: <a href=\"" + callbackUrl + "\">link</a>");
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToLocal(returnUrl);
}
// If we got this far, something failed, redisplay form
return View(model);
AddErrors(result);
}
//
// POST: /Account/LogOff
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> LogOff()
// If we got this far, something failed, redisplay form
return View(model);
}
//
// POST: /Account/LogOff
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> LogOff()
{
await _signInManager.SignOutAsync();
return RedirectToAction(nameof(HomeController.Index), "Home");
}
//
// POST: /Account/ExternalLogin
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public IActionResult ExternalLogin(string provider, string returnUrl = null)
{
EnsureDatabaseCreated(_applicationDbContext);
// Request a redirect to the external login provider.
var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl });
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
return new ChallengeResult(provider, properties);
}
//
// GET: /Account/ExternalLoginCallback
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null)
{
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info is null)
{
await _signInManager.SignOutAsync();
return RedirectToAction(nameof(HomeController.Index), "Home");
return RedirectToAction(nameof(Login));
}
//
// POST: /Account/ExternalLogin
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public IActionResult ExternalLogin(string provider, string returnUrl = null)
// Sign in the user with this external login provider if the user already has a login.
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false);
if (result.Succeeded)
{
return RedirectToLocal(returnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl });
}
if (result.IsLockedOut)
{
EnsureDatabaseCreated(_applicationDbContext);
// Request a redirect to the external login provider.
var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl });
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
return new ChallengeResult(provider, properties);
return View("Lockout");
}
else
{
// If the user does not have an account, then ask the user to create an account.
ViewData["ReturnUrl"] = returnUrl;
ViewData["LoginProvider"] = info.LoginProvider;
var email = info.Principal.FindFirstValue(ClaimTypes.Email);
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email });
}
}
//
// GET: /Account/ExternalLoginCallback
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null)
//
// POST: /Account/ExternalLoginConfirmation
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl = null)
{
if (ModelState.IsValid)
{
// Get the information about the user from the external login provider
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info is null)
{
return RedirectToAction(nameof(Login));
return View("ExternalLoginFailure");
}
// Sign in the user with this external login provider if the user already has a login.
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false);
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await _userManager.CreateAsync(user);
if (result.Succeeded)
{
return RedirectToLocal(returnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl });
}
if (result.IsLockedOut)
{
return View("Lockout");
}
else
{
// If the user does not have an account, then ask the user to create an account.
ViewData["ReturnUrl"] = returnUrl;
ViewData["LoginProvider"] = info.LoginProvider;
var email = info.Principal.FindFirstValue(ClaimTypes.Email);
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email });
}
}
//
// POST: /Account/ExternalLoginConfirmation
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl = null)
{
if (ModelState.IsValid)
{
// Get the information about the user from the external login provider
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info is null)
{
return View("ExternalLoginFailure");
}
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await _userManager.CreateAsync(user);
result = await _userManager.AddLoginAsync(user, info);
if (result.Succeeded)
{
result = await _userManager.AddLoginAsync(user, info);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToLocal(returnUrl);
}
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToLocal(returnUrl);
}
AddErrors(result);
}
ViewData["ReturnUrl"] = returnUrl;
return View(model);
AddErrors(result);
}
// GET: /Account/ConfirmEmail
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> ConfirmEmail(string userId, string code)
ViewData["ReturnUrl"] = returnUrl;
return View(model);
}
// GET: /Account/ConfirmEmail
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> ConfirmEmail(string userId, string code)
{
if (userId is null || code is null)
{
if (userId is null || code is null)
{
return View("Error");
}
var user = await _userManager.FindByIdAsync(userId);
if (user is null)
{
return View("Error");
}
var result = await _userManager.ConfirmEmailAsync(user, code);
return View(result.Succeeded ? "ConfirmEmail" : "Error");
return View("Error");
}
//
// GET: /Account/ForgotPassword
[HttpGet]
[AllowAnonymous]
public IActionResult ForgotPassword()
var user = await _userManager.FindByIdAsync(userId);
if (user is null)
{
return View();
return View("Error");
}
var result = await _userManager.ConfirmEmailAsync(user, code);
return View(result.Succeeded ? "ConfirmEmail" : "Error");
}
//
// GET: /Account/ForgotPassword
[HttpGet]
[AllowAnonymous]
public IActionResult ForgotPassword()
{
return View();
}
//
// POST: /Account/ForgotPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
//
// POST: /Account/ForgotPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
if (ModelState.IsValid)
{
if (ModelState.IsValid)
var user = await _userManager.FindByNameAsync(model.Email);
if (user is null || !(await _userManager.IsEmailConfirmedAsync(user)))
{
var user = await _userManager.FindByNameAsync(model.Email);
if (user is null || !(await _userManager.IsEmailConfirmedAsync(user)))
{
// Don't reveal that the user does not exist or is not confirmed
return View("ForgotPasswordConfirmation");
}
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=532713
// Send an email with this link
//var code = await _userManager.GeneratePasswordResetTokenAsync(user);
//var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: Context.Request.Scheme);
//await _emailSender.SendEmailAsync(model.Email, "Reset Password",
// "Please reset your password by clicking here: <a href=\"" + callbackUrl + "\">link</a>");
//return View("ForgotPasswordConfirmation");
// Don't reveal that the user does not exist or is not confirmed
return View("ForgotPasswordConfirmation");
}
// If we got this far, something failed, redisplay form
return View(model);
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=532713
// Send an email with this link
//var code = await _userManager.GeneratePasswordResetTokenAsync(user);
//var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: Context.Request.Scheme);
//await _emailSender.SendEmailAsync(model.Email, "Reset Password",
// "Please reset your password by clicking here: <a href=\"" + callbackUrl + "\">link</a>");
//return View("ForgotPasswordConfirmation");
}
//
// GET: /Account/ForgotPasswordConfirmation
[HttpGet]
[AllowAnonymous]
public IActionResult ForgotPasswordConfirmation()
// If we got this far, something failed, redisplay form
return View(model);
}
//
// GET: /Account/ForgotPasswordConfirmation
[HttpGet]
[AllowAnonymous]
public IActionResult ForgotPasswordConfirmation()
{
return View();
}
//
// GET: /Account/ResetPassword
[HttpGet]
[AllowAnonymous]
public IActionResult ResetPassword(string code = null)
{
return code is null ? View("Error") : View();
}
//
// POST: /Account/ResetPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ResetPassword(ResetPasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View();
return View(model);
}
//
// GET: /Account/ResetPassword
[HttpGet]
[AllowAnonymous]
public IActionResult ResetPassword(string code = null)
var user = await _userManager.FindByNameAsync(model.Email);
if (user is null)
{
return code is null ? View("Error") : View();
// Don't reveal that the user does not exist
return RedirectToAction(nameof(AccountController.ResetPasswordConfirmation), "Account");
}
var result = await _userManager.ResetPasswordAsync(user, model.Code, model.Password);
if (result.Succeeded)
{
return RedirectToAction(nameof(AccountController.ResetPasswordConfirmation), "Account");
}
AddErrors(result);
return View();
}
//
// GET: /Account/ResetPasswordConfirmation
[HttpGet]
[AllowAnonymous]
public IActionResult ResetPasswordConfirmation()
{
return View();
}
//
// POST: /Account/ResetPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ResetPassword(ResetPasswordViewModel model)
//
// GET: /Account/SendCode
[HttpGet]
[AllowAnonymous]
public async Task<ActionResult> SendCode(string returnUrl = null, bool rememberMe = false)
{
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user is null)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await _userManager.FindByNameAsync(model.Email);
if (user is null)
{
// Don't reveal that the user does not exist
return RedirectToAction(nameof(AccountController.ResetPasswordConfirmation), "Account");
}
var result = await _userManager.ResetPasswordAsync(user, model.Code, model.Password);
if (result.Succeeded)
{
return RedirectToAction(nameof(AccountController.ResetPasswordConfirmation), "Account");
}
AddErrors(result);
return View();
return View("Error");
}
var userFactors = await _userManager.GetValidTwoFactorProvidersAsync(user);
var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList();
return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl, RememberMe = rememberMe });
}
//
// GET: /Account/ResetPasswordConfirmation
[HttpGet]
[AllowAnonymous]
public IActionResult ResetPasswordConfirmation()
//
// POST: /Account/SendCode
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SendCode(SendCodeViewModel model)
{
if (!ModelState.IsValid)
{
return View();
}
//
// GET: /Account/SendCode
[HttpGet]
[AllowAnonymous]
public async Task<ActionResult> SendCode(string returnUrl = null, bool rememberMe = false)
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user is null)
{
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user is null)
{
return View("Error");
}
var userFactors = await _userManager.GetValidTwoFactorProvidersAsync(user);
var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList();
return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl, RememberMe = rememberMe });
return View("Error");
}
//
// POST: /Account/SendCode
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SendCode(SendCodeViewModel model)
// Generate the token and send it
var code = await _userManager.GenerateTwoFactorTokenAsync(user, model.SelectedProvider);
if (string.IsNullOrWhiteSpace(code))
{
if (!ModelState.IsValid)
{
return View();
}
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user is null)
{
return View("Error");
}
return View("Error");
}
// Generate the token and send it
var code = await _userManager.GenerateTwoFactorTokenAsync(user, model.SelectedProvider);
if (string.IsNullOrWhiteSpace(code))
{
return View("Error");
}
var message = "Your security code is: " + code;
if (model.SelectedProvider == "Email")
{
await _emailSender.SendEmailAsync(await _userManager.GetEmailAsync(user), "Security Code", message);
}
else if (model.SelectedProvider == "Phone")
{
await _smsSender.SendSmsAsync(await _userManager.GetPhoneNumberAsync(user), message);
}
var message = "Your security code is: " + code;
if (model.SelectedProvider == "Email")
{
await _emailSender.SendEmailAsync(await _userManager.GetEmailAsync(user), "Security Code", message);
}
else if (model.SelectedProvider == "Phone")
{
await _smsSender.SendSmsAsync(await _userManager.GetPhoneNumberAsync(user), message);
}
return RedirectToAction(nameof(VerifyCode), new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe });
}
return RedirectToAction(nameof(VerifyCode), new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe });
//
// GET: /Account/VerifyCode
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> VerifyCode(string provider, bool rememberMe, string returnUrl = null)
{
// Require that the user has already logged in via username/password or external login
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user is null)
{
return View("Error");
}
return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe });
}
//
// GET: /Account/VerifyCode
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> VerifyCode(string provider, bool rememberMe, string returnUrl = null)
//
// POST: /Account/VerifyCode
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> VerifyCode(VerifyCodeViewModel model)
{
if (!ModelState.IsValid)
{
// Require that the user has already logged in via username/password or external login
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user is null)
{
return View("Error");
}
return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe });
return View(model);
}
//
// POST: /Account/VerifyCode
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> VerifyCode(VerifyCodeViewModel model)
// The following code protects for brute force attacks against the two factor codes.
// If a user enters incorrect codes for a specified amount of time then the user account
// will be locked out for a specified amount of time.
var result = await _signInManager.TwoFactorSignInAsync(model.Provider, model.Code, model.RememberMe, model.RememberBrowser);
if (result.Succeeded)
{
if (!ModelState.IsValid)
{
return View(model);
}
// The following code protects for brute force attacks against the two factor codes.
// If a user enters incorrect codes for a specified amount of time then the user account
// will be locked out for a specified amount of time.
var result = await _signInManager.TwoFactorSignInAsync(model.Provider, model.Code, model.RememberMe, model.RememberBrowser);
if (result.Succeeded)
{
return RedirectToLocal(model.ReturnUrl);
}
if (result.IsLockedOut)
{
return View("Lockout");
}
else
{
ModelState.AddModelError("", "Invalid code.");
return View(model);
}
return RedirectToLocal(model.ReturnUrl);
}
if (result.IsLockedOut)
{
return View("Lockout");
}
else
{
ModelState.AddModelError("", "Invalid code.");
return View(model);
}
}
#region Helpers
#region Helpers
// The following code creates the database and schema if they don't exist.
// This is a temporary workaround since deploying database through EF migrations is
// not yet supported in this release.
// Please see this http://go.microsoft.com/fwlink/?LinkID=615859 for more information on how to do deploy the database
// when publishing your application.
private static void EnsureDatabaseCreated(ApplicationDbContext context)
// The following code creates the database and schema if they don't exist.
// This is a temporary workaround since deploying database through EF migrations is
// not yet supported in this release.
// Please see this http://go.microsoft.com/fwlink/?LinkID=615859 for more information on how to do deploy the database
// when publishing your application.
private static void EnsureDatabaseCreated(ApplicationDbContext context)
{
if (!_databaseChecked)
{
if (!_databaseChecked)
{
_databaseChecked = true;
context.Database.EnsureCreated();
}
_databaseChecked = true;
context.Database.EnsureCreated();
}
}
private void AddErrors(IdentityResult result)
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
ModelState.AddModelError(string.Empty, error.Description);
}
}
private async Task<ApplicationUser> GetCurrentUserAsync()
{
return await _userManager.GetUserAsync(User);
}
private async Task<ApplicationUser> GetCurrentUserAsync()
private IActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return await _userManager.GetUserAsync(User);
return Redirect(returnUrl);
}
private IActionResult RedirectToLocal(string returnUrl)
else
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction(nameof(HomeController.Index), "Home");
}
return RedirectToAction(nameof(HomeController.Index), "Home");
}
#endregion
}
#endregion
}

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

@ -23,519 +23,518 @@ using OpenIddict.Abstractions;
using OpenIddict.Server.AspNetCore;
using static OpenIddict.Abstractions.OpenIddictConstants;
namespace Mvc.Server
namespace Mvc.Server;
public class AuthorizationController : Controller
{
public class AuthorizationController : Controller
private readonly IOpenIddictApplicationManager _applicationManager;
private readonly IOpenIddictAuthorizationManager _authorizationManager;
private readonly IOpenIddictScopeManager _scopeManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly UserManager<ApplicationUser> _userManager;
public AuthorizationController(
IOpenIddictApplicationManager applicationManager,
IOpenIddictAuthorizationManager authorizationManager,
IOpenIddictScopeManager scopeManager,
SignInManager<ApplicationUser> signInManager,
UserManager<ApplicationUser> userManager)
{
private readonly IOpenIddictApplicationManager _applicationManager;
private readonly IOpenIddictAuthorizationManager _authorizationManager;
private readonly IOpenIddictScopeManager _scopeManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly UserManager<ApplicationUser> _userManager;
public AuthorizationController(
IOpenIddictApplicationManager applicationManager,
IOpenIddictAuthorizationManager authorizationManager,
IOpenIddictScopeManager scopeManager,
SignInManager<ApplicationUser> signInManager,
UserManager<ApplicationUser> userManager)
{
_applicationManager = applicationManager;
_authorizationManager = authorizationManager;
_scopeManager = scopeManager;
_signInManager = signInManager;
_userManager = userManager;
}
_applicationManager = applicationManager;
_authorizationManager = authorizationManager;
_scopeManager = scopeManager;
_signInManager = signInManager;
_userManager = userManager;
}
#region Authorization code, implicit and hybrid flows
// Note: to support interactive flows like the code flow,
// you must provide your own authorization endpoint action:
#region Authorization code, implicit and hybrid flows
// Note: to support interactive flows like the code flow,
// you must provide your own authorization endpoint action:
[HttpGet("~/connect/authorize")]
[HttpPost("~/connect/authorize")]
[IgnoreAntiforgeryToken]
public async Task<IActionResult> Authorize()
[HttpGet("~/connect/authorize")]
[HttpPost("~/connect/authorize")]
[IgnoreAntiforgeryToken]
public async Task<IActionResult> Authorize()
{
var request = HttpContext.GetOpenIddictServerRequest() ??
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved.");
// Retrieve the user principal stored in the authentication cookie.
// If a max_age parameter was provided, ensure that the cookie is not too old.
// If the user principal can't be extracted or the cookie is too old, redirect the user to the login page.
var result = await HttpContext.AuthenticateAsync(IdentityConstants.ApplicationScheme);
if (result == null || !result.Succeeded || (request.MaxAge != null && result.Properties?.IssuedUtc != null &&
DateTimeOffset.UtcNow - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value)))
{
var request = HttpContext.GetOpenIddictServerRequest() ??
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved.");
// Retrieve the user principal stored in the authentication cookie.
// If a max_age parameter was provided, ensure that the cookie is not too old.
// If the user principal can't be extracted or the cookie is too old, redirect the user to the login page.
var result = await HttpContext.AuthenticateAsync(IdentityConstants.ApplicationScheme);
if (result == null || !result.Succeeded || (request.MaxAge != null && result.Properties?.IssuedUtc != null &&
DateTimeOffset.UtcNow - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value)))
// If the client application requested promptless authentication,
// return an error indicating that the user is not logged in.
if (request.HasPrompt(Prompts.None))
{
// If the client application requested promptless authentication,
// return an error indicating that the user is not logged in.
if (request.HasPrompt(Prompts.None))
{
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.LoginRequired,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is not logged in."
}));
}
return Challenge(
authenticationSchemes: IdentityConstants.ApplicationScheme,
properties: new AuthenticationProperties
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
RedirectUri = Request.PathBase + Request.Path + QueryString.Create(
Request.HasFormContentType ? Request.Form.ToList() : Request.Query.ToList())
});
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.LoginRequired,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is not logged in."
}));
}
// If prompt=login was specified by the client application,
// immediately return the user agent to the login page.
if (request.HasPrompt(Prompts.Login))
{
// To avoid endless login -> authorization redirects, the prompt=login flag
// is removed from the authorization request payload before redirecting the user.
var prompt = string.Join(" ", request.GetPrompts().Remove(Prompts.Login));
return Challenge(
authenticationSchemes: IdentityConstants.ApplicationScheme,
properties: new AuthenticationProperties
{
RedirectUri = Request.PathBase + Request.Path + QueryString.Create(
Request.HasFormContentType ? Request.Form.ToList() : Request.Query.ToList())
});
}
var parameters = Request.HasFormContentType ?
Request.Form.Where(parameter => parameter.Key != Parameters.Prompt).ToList() :
Request.Query.Where(parameter => parameter.Key != Parameters.Prompt).ToList();
// If prompt=login was specified by the client application,
// immediately return the user agent to the login page.
if (request.HasPrompt(Prompts.Login))
{
// To avoid endless login -> authorization redirects, the prompt=login flag
// is removed from the authorization request payload before redirecting the user.
var prompt = string.Join(" ", request.GetPrompts().Remove(Prompts.Login));
parameters.Add(KeyValuePair.Create(Parameters.Prompt, new StringValues(prompt)));
var parameters = Request.HasFormContentType ?
Request.Form.Where(parameter => parameter.Key != Parameters.Prompt).ToList() :
Request.Query.Where(parameter => parameter.Key != Parameters.Prompt).ToList();
return Challenge(
authenticationSchemes: IdentityConstants.ApplicationScheme,
properties: new AuthenticationProperties
{
RedirectUri = Request.PathBase + Request.Path + QueryString.Create(parameters)
});
}
parameters.Add(KeyValuePair.Create(Parameters.Prompt, new StringValues(prompt)));
return Challenge(
authenticationSchemes: IdentityConstants.ApplicationScheme,
properties: new AuthenticationProperties
{
RedirectUri = Request.PathBase + Request.Path + QueryString.Create(parameters)
});
}
// Retrieve the profile of the logged in user.
var user = await _userManager.GetUserAsync(result.Principal) ??
throw new InvalidOperationException("The user details cannot be retrieved.");
// Retrieve the profile of the logged in user.
var user = await _userManager.GetUserAsync(result.Principal) ??
throw new InvalidOperationException("The user details cannot be retrieved.");
// Retrieve the application details from the database.
var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ??
throw new InvalidOperationException("Details concerning the calling client application cannot be found.");
// Retrieve the application details from the database.
var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ??
throw new InvalidOperationException("Details concerning the calling client application cannot be found.");
// Retrieve the permanent authorizations associated with the user and the calling client application.
var authorizations = await _authorizationManager.FindAsync(
subject: await _userManager.GetUserIdAsync(user),
client : await _applicationManager.GetIdAsync(application),
status : Statuses.Valid,
type : AuthorizationTypes.Permanent,
scopes : request.GetScopes()).ToListAsync();
// Retrieve the permanent authorizations associated with the user and the calling client application.
var authorizations = await _authorizationManager.FindAsync(
subject: await _userManager.GetUserIdAsync(user),
client : await _applicationManager.GetIdAsync(application),
status : Statuses.Valid,
type : AuthorizationTypes.Permanent,
scopes : request.GetScopes()).ToListAsync();
switch (await _applicationManager.GetConsentTypeAsync(application))
{
// If the consent is external (e.g when authorizations are granted by a sysadmin),
// immediately return an error if no authorization can be found in the database.
case ConsentTypes.External when !authorizations.Any():
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
"The logged in user is not allowed to access this client application."
}));
// If the consent is implicit or if an authorization was found,
// return an authorization response without displaying the consent form.
case ConsentTypes.Implicit:
case ConsentTypes.External when authorizations.Any():
case ConsentTypes.Explicit when authorizations.Any() && !request.HasPrompt(Prompts.Consent):
var principal = await _signInManager.CreateUserPrincipalAsync(user);
// Note: in this sample, the granted scopes match the requested scope
// but you may want to allow the user to uncheck specific scopes.
// For that, simply restrict the list of scopes before calling SetScopes.
principal.SetScopes(request.GetScopes());
principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync());
// Automatically create a permanent authorization to avoid requiring explicit consent
// for future authorization or token requests containing the same scopes.
var authorization = authorizations.LastOrDefault();
if (authorization is null)
switch (await _applicationManager.GetConsentTypeAsync(application))
{
// If the consent is external (e.g when authorizations are granted by a sysadmin),
// immediately return an error if no authorization can be found in the database.
case ConsentTypes.External when !authorizations.Any():
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
authorization = await _authorizationManager.CreateAsync(
principal: principal,
subject : await _userManager.GetUserIdAsync(user),
client : await _applicationManager.GetIdAsync(application),
type : AuthorizationTypes.Permanent,
scopes : principal.GetScopes());
}
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
"The logged in user is not allowed to access this client application."
}));
principal.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization));
// If the consent is implicit or if an authorization was found,
// return an authorization response without displaying the consent form.
case ConsentTypes.Implicit:
case ConsentTypes.External when authorizations.Any():
case ConsentTypes.Explicit when authorizations.Any() && !request.HasPrompt(Prompts.Consent):
var principal = await _signInManager.CreateUserPrincipalAsync(user);
foreach (var claim in principal.Claims)
{
claim.SetDestinations(GetDestinations(claim, principal));
}
return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
// At this point, no authorization was found in the database and an error must be returned
// if the client application specified prompt=none in the authorization request.
case ConsentTypes.Explicit when request.HasPrompt(Prompts.None):
case ConsentTypes.Systematic when request.HasPrompt(Prompts.None):
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
"Interactive user consent is required."
}));
// In every other case, render the consent form.
default: return View(new AuthorizeViewModel
// Note: in this sample, the granted scopes match the requested scope
// but you may want to allow the user to uncheck specific scopes.
// For that, simply restrict the list of scopes before calling SetScopes.
principal.SetScopes(request.GetScopes());
principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync());
// Automatically create a permanent authorization to avoid requiring explicit consent
// for future authorization or token requests containing the same scopes.
var authorization = authorizations.LastOrDefault();
if (authorization is null)
{
ApplicationName = await _applicationManager.GetLocalizedDisplayNameAsync(application),
Scope = request.Scope
});
}
}
authorization = await _authorizationManager.CreateAsync(
principal: principal,
subject : await _userManager.GetUserIdAsync(user),
client : await _applicationManager.GetIdAsync(application),
type : AuthorizationTypes.Permanent,
scopes : principal.GetScopes());
}
[Authorize, FormValueRequired("submit.Accept")]
[HttpPost("~/connect/authorize"), ValidateAntiForgeryToken]
public async Task<IActionResult> Accept()
{
var request = HttpContext.GetOpenIddictServerRequest() ??
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved.");
principal.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization));
// Retrieve the profile of the logged in user.
var user = await _userManager.GetUserAsync(User) ??
throw new InvalidOperationException("The user details cannot be retrieved.");
foreach (var claim in principal.Claims)
{
claim.SetDestinations(GetDestinations(claim, principal));
}
// Retrieve the application details from the database.
var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ??
throw new InvalidOperationException("Details concerning the calling client application cannot be found.");
return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
// Retrieve the permanent authorizations associated with the user and the calling client application.
var authorizations = await _authorizationManager.FindAsync(
subject: await _userManager.GetUserIdAsync(user),
client : await _applicationManager.GetIdAsync(application),
status : Statuses.Valid,
type : AuthorizationTypes.Permanent,
scopes : request.GetScopes()).ToListAsync();
// Note: the same check is already made in the other action but is repeated
// here to ensure a malicious user can't abuse this POST-only endpoint and
// force it to return a valid response without the external authorization.
if (!authorizations.Any() && await _applicationManager.HasConsentTypeAsync(application, ConsentTypes.External))
{
// At this point, no authorization was found in the database and an error must be returned
// if the client application specified prompt=none in the authorization request.
case ConsentTypes.Explicit when request.HasPrompt(Prompts.None):
case ConsentTypes.Systematic when request.HasPrompt(Prompts.None):
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
"The logged in user is not allowed to access this client application."
"Interactive user consent is required."
}));
}
var principal = await _signInManager.CreateUserPrincipalAsync(user);
// Note: in this sample, the granted scopes match the requested scope
// but you may want to allow the user to uncheck specific scopes.
// For that, simply restrict the list of scopes before calling SetScopes.
principal.SetScopes(request.GetScopes());
principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync());
// Automatically create a permanent authorization to avoid requiring explicit consent
// for future authorization or token requests containing the same scopes.
var authorization = authorizations.LastOrDefault();
if (authorization is null)
// In every other case, render the consent form.
default: return View(new AuthorizeViewModel
{
authorization = await _authorizationManager.CreateAsync(
principal: principal,
subject : await _userManager.GetUserIdAsync(user),
client : await _applicationManager.GetIdAsync(application),
type : AuthorizationTypes.Permanent,
scopes : principal.GetScopes());
}
ApplicationName = await _applicationManager.GetLocalizedDisplayNameAsync(application),
Scope = request.Scope
});
}
}
[Authorize, FormValueRequired("submit.Accept")]
[HttpPost("~/connect/authorize"), ValidateAntiForgeryToken]
public async Task<IActionResult> Accept()
{
var request = HttpContext.GetOpenIddictServerRequest() ??
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved.");
// Retrieve the profile of the logged in user.
var user = await _userManager.GetUserAsync(User) ??
throw new InvalidOperationException("The user details cannot be retrieved.");
// Retrieve the application details from the database.
var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ??
throw new InvalidOperationException("Details concerning the calling client application cannot be found.");
// Retrieve the permanent authorizations associated with the user and the calling client application.
var authorizations = await _authorizationManager.FindAsync(
subject: await _userManager.GetUserIdAsync(user),
client : await _applicationManager.GetIdAsync(application),
status : Statuses.Valid,
type : AuthorizationTypes.Permanent,
scopes : request.GetScopes()).ToListAsync();
// Note: the same check is already made in the other action but is repeated
// here to ensure a malicious user can't abuse this POST-only endpoint and
// force it to return a valid response without the external authorization.
if (!authorizations.Any() && await _applicationManager.HasConsentTypeAsync(application, ConsentTypes.External))
{
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
"The logged in user is not allowed to access this client application."
}));
}
principal.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization));
var principal = await _signInManager.CreateUserPrincipalAsync(user);
foreach (var claim in principal.Claims)
{
claim.SetDestinations(GetDestinations(claim, principal));
}
// Note: in this sample, the granted scopes match the requested scope
// but you may want to allow the user to uncheck specific scopes.
// For that, simply restrict the list of scopes before calling SetScopes.
principal.SetScopes(request.GetScopes());
principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync());
// Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
// Automatically create a permanent authorization to avoid requiring explicit consent
// for future authorization or token requests containing the same scopes.
var authorization = authorizations.LastOrDefault();
if (authorization is null)
{
authorization = await _authorizationManager.CreateAsync(
principal: principal,
subject : await _userManager.GetUserIdAsync(user),
client : await _applicationManager.GetIdAsync(application),
type : AuthorizationTypes.Permanent,
scopes : principal.GetScopes());
}
[Authorize, FormValueRequired("submit.Deny")]
[HttpPost("~/connect/authorize"), ValidateAntiForgeryToken]
// Notify OpenIddict that the authorization grant has been denied by the resource owner
// to redirect the user agent to the client application using the appropriate response_mode.
public IActionResult Deny() => Forbid(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
#endregion
#region Device flow
// Note: to support the device flow, you must provide your own verification endpoint action:
[Authorize, HttpGet("~/connect/verify")]
public async Task<IActionResult> Verify()
principal.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization));
foreach (var claim in principal.Claims)
{
var request = HttpContext.GetOpenIddictServerRequest() ??
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved.");
claim.SetDestinations(GetDestinations(claim, principal));
}
// If the user code was not specified in the query string (e.g as part of the verification_uri_complete),
// render a form to ask the user to enter the user code manually (non-digit chars are automatically ignored).
if (string.IsNullOrEmpty(request.UserCode))
{
return View(new VerifyViewModel());
}
// Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
}
// Retrieve the claims principal associated with the user code.
var result = await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
if (result.Succeeded)
{
// Retrieve the application details from the database using the client_id stored in the principal.
var application = await _applicationManager.FindByClientIdAsync(result.Principal.GetClaim(Claims.ClientId)) ??
throw new InvalidOperationException("Details concerning the calling client application cannot be found.");
[Authorize, FormValueRequired("submit.Deny")]
[HttpPost("~/connect/authorize"), ValidateAntiForgeryToken]
// Notify OpenIddict that the authorization grant has been denied by the resource owner
// to redirect the user agent to the client application using the appropriate response_mode.
public IActionResult Deny() => Forbid(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
#endregion
#region Device flow
// Note: to support the device flow, you must provide your own verification endpoint action:
[Authorize, HttpGet("~/connect/verify")]
public async Task<IActionResult> Verify()
{
var request = HttpContext.GetOpenIddictServerRequest() ??
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved.");
// Render a form asking the user to confirm the authorization demand.
return View(new VerifyViewModel
{
ApplicationName = await _applicationManager.GetLocalizedDisplayNameAsync(application),
Scope = string.Join(" ", result.Principal.GetScopes()),
UserCode = request.UserCode
});
}
// If the user code was not specified in the query string (e.g as part of the verification_uri_complete),
// render a form to ask the user to enter the user code manually (non-digit chars are automatically ignored).
if (string.IsNullOrEmpty(request.UserCode))
{
return View(new VerifyViewModel());
}
// Redisplay the form when the user code is not valid.
// Retrieve the claims principal associated with the user code.
var result = await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
if (result.Succeeded)
{
// Retrieve the application details from the database using the client_id stored in the principal.
var application = await _applicationManager.FindByClientIdAsync(result.Principal.GetClaim(Claims.ClientId)) ??
throw new InvalidOperationException("Details concerning the calling client application cannot be found.");
// Render a form asking the user to confirm the authorization demand.
return View(new VerifyViewModel
{
Error = Errors.InvalidToken,
ErrorDescription = "The specified user code is not valid. Please make sure you typed it correctly."
ApplicationName = await _applicationManager.GetLocalizedDisplayNameAsync(application),
Scope = string.Join(" ", result.Principal.GetScopes()),
UserCode = request.UserCode
});
}
[Authorize, FormValueRequired("submit.Accept")]
[HttpPost("~/connect/verify"), ValidateAntiForgeryToken]
public async Task<IActionResult> VerifyAccept()
// Redisplay the form when the user code is not valid.
return View(new VerifyViewModel
{
// Retrieve the profile of the logged in user.
var user = await _userManager.GetUserAsync(User) ??
throw new InvalidOperationException("The user details cannot be retrieved.");
// Retrieve the claims principal associated with the user code.
var result = await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
if (result.Succeeded)
{
var principal = await _signInManager.CreateUserPrincipalAsync(user);
// Note: in this sample, the granted scopes match the requested scope
// but you may want to allow the user to uncheck specific scopes.
// For that, simply restrict the list of scopes before calling SetScopes.
principal.SetScopes(result.Principal.GetScopes());
principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync());
Error = Errors.InvalidToken,
ErrorDescription = "The specified user code is not valid. Please make sure you typed it correctly."
});
}
foreach (var claim in principal.Claims)
{
claim.SetDestinations(GetDestinations(claim, principal));
}
[Authorize, FormValueRequired("submit.Accept")]
[HttpPost("~/connect/verify"), ValidateAntiForgeryToken]
public async Task<IActionResult> VerifyAccept()
{
// Retrieve the profile of the logged in user.
var user = await _userManager.GetUserAsync(User) ??
throw new InvalidOperationException("The user details cannot be retrieved.");
var properties = new AuthenticationProperties
{
// This property points to the address OpenIddict will automatically
// redirect the user to after validating the authorization demand.
RedirectUri = "/"
};
// Retrieve the claims principal associated with the user code.
var result = await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
if (result.Succeeded)
{
var principal = await _signInManager.CreateUserPrincipalAsync(user);
return SignIn(principal, properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
}
// Note: in this sample, the granted scopes match the requested scope
// but you may want to allow the user to uncheck specific scopes.
// For that, simply restrict the list of scopes before calling SetScopes.
principal.SetScopes(result.Principal.GetScopes());
principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync());
// Redisplay the form when the user code is not valid.
return View(new VerifyViewModel
foreach (var claim in principal.Claims)
{
Error = Errors.InvalidToken,
ErrorDescription = "The specified user code is not valid. Please make sure you typed it correctly."
});
}
claim.SetDestinations(GetDestinations(claim, principal));
}
[Authorize, FormValueRequired("submit.Deny")]
[HttpPost("~/connect/verify"), ValidateAntiForgeryToken]
// Notify OpenIddict that the authorization grant has been denied by the resource owner.
public IActionResult VerifyDeny() => Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties()
var properties = new AuthenticationProperties
{
// This property points to the address OpenIddict will automatically
// redirect the user to after rejecting the authorization demand.
// redirect the user to after validating the authorization demand.
RedirectUri = "/"
});
#endregion
#region Logout support for interactive flows like code and implicit
// 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() => View();
};
[ActionName(nameof(Logout)), HttpPost("~/connect/logout"), ValidateAntiForgeryToken]
public async Task<IActionResult> LogoutPost()
{
// Ask ASP.NET Core Identity to delete the local and external cookies created
// when the user agent is redirected from the external identity provider
// after a successful authentication flow (e.g Google or Facebook).
await _signInManager.SignOutAsync();
// Returning a SignOutResult will ask OpenIddict to redirect the user agent
// to the post_logout_redirect_uri specified by the client application or to
// the RedirectUri specified in the authentication properties if none was set.
return SignOut(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties
{
RedirectUri = "/"
});
return SignIn(principal, properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
}
#endregion
#region Password, authorization code, device and refresh token flows
// Note: to support non-interactive flows like password,
// you must provide your own token endpoint action:
// Redisplay the form when the user code is not valid.
return View(new VerifyViewModel
{
Error = Errors.InvalidToken,
ErrorDescription = "The specified user code is not valid. Please make sure you typed it correctly."
});
}
[HttpPost("~/connect/token"), Produces("application/json")]
public async Task<IActionResult> Exchange()
[Authorize, FormValueRequired("submit.Deny")]
[HttpPost("~/connect/verify"), ValidateAntiForgeryToken]
// Notify OpenIddict that the authorization grant has been denied by the resource owner.
public IActionResult VerifyDeny() => Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties()
{
var request = HttpContext.GetOpenIddictServerRequest() ??
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved.");
// This property points to the address OpenIddict will automatically
// redirect the user to after rejecting the authorization demand.
RedirectUri = "/"
});
#endregion
if (request.IsPasswordGrantType())
{
var user = await _userManager.FindByNameAsync(request.Username);
if (user is null)
{
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The username/password couple is invalid."
}));
}
#region Logout support for interactive flows like code and implicit
// Note: the logout action is only useful when implementing interactive
// flows like the authorization code flow or the implicit flow.
// Validate the username/password parameters and ensure the account is not locked out.
var result = await _signInManager.CheckPasswordSignInAsync(user, request.Password, lockoutOnFailure: true);
if (!result.Succeeded)
{
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The username/password couple is invalid."
}));
}
[HttpGet("~/connect/logout")]
public IActionResult Logout() => View();
var principal = await _signInManager.CreateUserPrincipalAsync(user);
[ActionName(nameof(Logout)), HttpPost("~/connect/logout"), ValidateAntiForgeryToken]
public async Task<IActionResult> LogoutPost()
{
// Ask ASP.NET Core Identity to delete the local and external cookies created
// when the user agent is redirected from the external identity provider
// after a successful authentication flow (e.g Google or Facebook).
await _signInManager.SignOutAsync();
// Returning a SignOutResult will ask OpenIddict to redirect the user agent
// to the post_logout_redirect_uri specified by the client application or to
// the RedirectUri specified in the authentication properties if none was set.
return SignOut(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties
{
RedirectUri = "/"
});
}
#endregion
// Note: in this sample, the granted scopes match the requested scope
// but you may want to allow the user to uncheck specific scopes.
// For that, simply restrict the list of scopes before calling SetScopes.
principal.SetScopes(request.GetScopes());
principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync());
#region Password, authorization code, device and refresh token flows
// Note: to support non-interactive flows like password,
// you must provide your own token endpoint action:
foreach (var claim in principal.Claims)
{
claim.SetDestinations(GetDestinations(claim, principal));
}
[HttpPost("~/connect/token"), Produces("application/json")]
public async Task<IActionResult> Exchange()
{
var request = HttpContext.GetOpenIddictServerRequest() ??
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved.");
// Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
if (request.IsPasswordGrantType())
{
var user = await _userManager.FindByNameAsync(request.Username);
if (user is null)
{
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The username/password couple is invalid."
}));
}
else if (request.IsAuthorizationCodeGrantType() || request.IsDeviceCodeGrantType() || request.IsRefreshTokenGrantType())
// Validate the username/password parameters and ensure the account is not locked out.
var result = await _signInManager.CheckPasswordSignInAsync(user, request.Password, lockoutOnFailure: true);
if (!result.Succeeded)
{
// Retrieve the claims principal stored in the authorization code/device code/refresh token.
var principal = (await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)).Principal;
// Retrieve the user profile corresponding to the authorization code/refresh token.
// Note: if you want to automatically invalidate the authorization code/refresh token
// when the user password/roles change, use the following line instead:
// var user = _signInManager.ValidateSecurityStampAsync(info.Principal);
var user = await _userManager.GetUserAsync(principal);
if (user is null)
{
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The token is no longer valid."
}));
}
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The username/password couple is invalid."
}));
}
// Ensure the user is still allowed to sign in.
if (!await _signInManager.CanSignInAsync(user))
{
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is no longer allowed to sign in."
}));
}
var principal = await _signInManager.CreateUserPrincipalAsync(user);
foreach (var claim in principal.Claims)
{
claim.SetDestinations(GetDestinations(claim, principal));
}
// Note: in this sample, the granted scopes match the requested scope
// but you may want to allow the user to uncheck specific scopes.
// For that, simply restrict the list of scopes before calling SetScopes.
principal.SetScopes(request.GetScopes());
principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync());
// Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
foreach (var claim in principal.Claims)
{
claim.SetDestinations(GetDestinations(claim, principal));
}
throw new InvalidOperationException("The specified grant type is not supported.");
// Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
}
#endregion
private IEnumerable<string> GetDestinations(Claim claim, ClaimsPrincipal principal)
else if (request.IsAuthorizationCodeGrantType() || request.IsDeviceCodeGrantType() || request.IsRefreshTokenGrantType())
{
// 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.
// Retrieve the claims principal stored in the authorization code/device code/refresh token.
var principal = (await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)).Principal;
// Retrieve the user profile corresponding to the authorization code/refresh token.
// Note: if you want to automatically invalidate the authorization code/refresh token
// when the user password/roles change, use the following line instead:
// var user = _signInManager.ValidateSecurityStampAsync(info.Principal);
var user = await _userManager.GetUserAsync(principal);
if (user is null)
{
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The token is no longer valid."
}));
}
switch (claim.Type)
// Ensure the user is still allowed to sign in.
if (!await _signInManager.CanSignInAsync(user))
{
case Claims.Name:
yield return Destinations.AccessToken;
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is no longer allowed to sign in."
}));
}
if (principal.HasScope(Scopes.Profile))
yield return Destinations.IdentityToken;
foreach (var claim in principal.Claims)
{
claim.SetDestinations(GetDestinations(claim, principal));
}
yield break;
// Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
}
case Claims.Email:
yield return Destinations.AccessToken;
throw new InvalidOperationException("The specified grant type is not supported.");
}
#endregion
if (principal.HasScope(Scopes.Email))
yield return Destinations.IdentityToken;
private IEnumerable<string> GetDestinations(Claim claim, ClaimsPrincipal principal)
{
// 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.
yield break;
switch (claim.Type)
{
case Claims.Name:
yield return Destinations.AccessToken;
case Claims.Role:
yield return Destinations.AccessToken;
if (principal.HasScope(Scopes.Profile))
yield return Destinations.IdentityToken;
if (principal.HasScope(Scopes.Roles))
yield return Destinations.IdentityToken;
yield break;
yield break;
case Claims.Email:
yield return Destinations.AccessToken;
// Never include the security stamp in the access and identity tokens, as it's a secret value.
case "AspNet.Identity.SecurityStamp": yield break;
if (principal.HasScope(Scopes.Email))
yield return Destinations.IdentityToken;
default:
yield return Destinations.AccessToken;
yield break;
}
yield break;
case Claims.Role:
yield return Destinations.AccessToken;
if (principal.HasScope(Scopes.Roles))
yield return Destinations.IdentityToken;
yield break;
// Never include the security stamp in the access and identity tokens, as it's a secret value.
case "AspNet.Identity.SecurityStamp": yield break;
default:
yield return Destinations.AccessToken;
yield break;
}
}
}
}

35
samples/Mvc.Server/Controllers/ErrorController.cs

@ -8,26 +8,25 @@ using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Mvc;
using Mvc.Server.ViewModels.Shared;
namespace Mvc.Server
namespace Mvc.Server;
public class ErrorController : Controller
{
public class ErrorController : Controller
[HttpGet, HttpPost, Route("~/error")]
public IActionResult Error()
{
[HttpGet, HttpPost, Route("~/error")]
public IActionResult Error()
// If the error was not caused by an invalid
// OIDC request, display a generic error page.
var response = HttpContext.GetOpenIddictServerResponse();
if (response is null)
{
// If the error was not caused by an invalid
// OIDC request, display a generic error page.
var response = HttpContext.GetOpenIddictServerResponse();
if (response is null)
{
return View(new ErrorViewModel());
}
return View(new ErrorViewModel
{
Error = response.Error,
ErrorDescription = response.ErrorDescription
});
return View(new ErrorViewModel());
}
return View(new ErrorViewModel
{
Error = response.Error,
ErrorDescription = response.ErrorDescription
});
}
}
}

39
samples/Mvc.Server/Controllers/HomeController.cs

@ -1,31 +1,30 @@
using Microsoft.AspNetCore.Mvc;
namespace Mvc.Server.Controllers
namespace Mvc.Server.Controllers;
public class HomeController : Controller
{
public class HomeController : Controller
public IActionResult Index()
{
public IActionResult Index()
{
return View();
}
return View();
}
public IActionResult About()
{
ViewData["Message"] = "Your application description page.";
public IActionResult About()
{
ViewData["Message"] = "Your application description page.";
return View();
}
return View();
}
public IActionResult Contact()
{
ViewData["Message"] = "Your contact page.";
public IActionResult Contact()
{
ViewData["Message"] = "Your contact page.";
return View();
}
return View();
}
public IActionResult Error()
{
return View("~/Views/Shared/Error.cshtml");
}
public IActionResult Error()
{
return View("~/Views/Shared/Error.cshtml");
}
}

543
samples/Mvc.Server/Controllers/ManageController.cs

@ -8,338 +8,337 @@ using Mvc.Server.Models;
using Mvc.Server.Services;
using Mvc.Server.ViewModels.Manage;
namespace Mvc.Server.Controllers
namespace Mvc.Server.Controllers;
[Authorize]
public class ManageController : Controller
{
[Authorize]
public class ManageController : Controller
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IEmailSender _emailSender;
private readonly ISmsSender _smsSender;
private readonly ILogger _logger;
public ManageController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IEmailSender emailSender,
ISmsSender smsSender,
ILoggerFactory loggerFactory)
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IEmailSender _emailSender;
private readonly ISmsSender _smsSender;
private readonly ILogger _logger;
_userManager = userManager;
_signInManager = signInManager;
_emailSender = emailSender;
_smsSender = smsSender;
_logger = loggerFactory.CreateLogger<ManageController>();
}
public ManageController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IEmailSender emailSender,
ISmsSender smsSender,
ILoggerFactory loggerFactory)
{
_userManager = userManager;
_signInManager = signInManager;
_emailSender = emailSender;
_smsSender = smsSender;
_logger = loggerFactory.CreateLogger<ManageController>();
}
//
// GET: /Manage/Index
[HttpGet]
public async Task<IActionResult> Index(ManageMessageId? message = null)
{
ViewData["StatusMessage"] =
message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
: message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
: message == ManageMessageId.SetTwoFactorSuccess ? "Your two-factor authentication provider has been set."
: message == ManageMessageId.Error ? "An error has occurred."
: message == ManageMessageId.AddPhoneSuccess ? "Your phone number was added."
: message == ManageMessageId.RemovePhoneSuccess ? "Your phone number was removed."
: "";
//
// GET: /Manage/Index
[HttpGet]
public async Task<IActionResult> Index(ManageMessageId? message = null)
var user = await GetCurrentUserAsync();
var model = new IndexViewModel
{
ViewData["StatusMessage"] =
message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
: message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
: message == ManageMessageId.SetTwoFactorSuccess ? "Your two-factor authentication provider has been set."
: message == ManageMessageId.Error ? "An error has occurred."
: message == ManageMessageId.AddPhoneSuccess ? "Your phone number was added."
: message == ManageMessageId.RemovePhoneSuccess ? "Your phone number was removed."
: "";
var user = await GetCurrentUserAsync();
var model = new IndexViewModel
{
HasPassword = await _userManager.HasPasswordAsync(user),
PhoneNumber = await _userManager.GetPhoneNumberAsync(user),
TwoFactor = await _userManager.GetTwoFactorEnabledAsync(user),
Logins = await _userManager.GetLoginsAsync(user),
BrowserRemembered = await _signInManager.IsTwoFactorClientRememberedAsync(user)
};
return View(model);
}
HasPassword = await _userManager.HasPasswordAsync(user),
PhoneNumber = await _userManager.GetPhoneNumberAsync(user),
TwoFactor = await _userManager.GetTwoFactorEnabledAsync(user),
Logins = await _userManager.GetLoginsAsync(user),
BrowserRemembered = await _signInManager.IsTwoFactorClientRememberedAsync(user)
};
return View(model);
}
//
// POST: /Manage/RemoveLogin
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> RemoveLogin(RemoveLoginViewModel account)
//
// POST: /Manage/RemoveLogin
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> RemoveLogin(RemoveLoginViewModel account)
{
ManageMessageId? message = ManageMessageId.Error;
var user = await GetCurrentUserAsync();
if (user is not null)
{
ManageMessageId? message = ManageMessageId.Error;
var user = await GetCurrentUserAsync();
if (user is not null)
var result = await _userManager.RemoveLoginAsync(user, account.LoginProvider, account.ProviderKey);
if (result.Succeeded)
{
var result = await _userManager.RemoveLoginAsync(user, account.LoginProvider, account.ProviderKey);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
message = ManageMessageId.RemoveLoginSuccess;
}
await _signInManager.SignInAsync(user, isPersistent: false);
message = ManageMessageId.RemoveLoginSuccess;
}
return RedirectToAction(nameof(ManageLogins), new { Message = message });
}
return RedirectToAction(nameof(ManageLogins), new { Message = message });
}
//
// GET: /Manage/AddPhoneNumber
public IActionResult AddPhoneNumber()
{
return View();
}
//
// GET: /Manage/AddPhoneNumber
public IActionResult AddPhoneNumber()
{
return View();
}
//
// POST: /Manage/AddPhoneNumber
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddPhoneNumber(AddPhoneNumberViewModel model)
//
// POST: /Manage/AddPhoneNumber
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddPhoneNumber(AddPhoneNumberViewModel model)
{
if (!ModelState.IsValid)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Generate the token and send it
var user = await GetCurrentUserAsync();
var code = await _userManager.GenerateChangePhoneNumberTokenAsync(user, model.PhoneNumber);
await _smsSender.SendSmsAsync(model.PhoneNumber, "Your security code is: " + code);
return RedirectToAction(nameof(VerifyPhoneNumber), new { PhoneNumber = model.PhoneNumber });
return View(model);
}
// Generate the token and send it
var user = await GetCurrentUserAsync();
var code = await _userManager.GenerateChangePhoneNumberTokenAsync(user, model.PhoneNumber);
await _smsSender.SendSmsAsync(model.PhoneNumber, "Your security code is: " + code);
return RedirectToAction(nameof(VerifyPhoneNumber), new { PhoneNumber = model.PhoneNumber });
}
//
// POST: /Manage/EnableTwoFactorAuthentication
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EnableTwoFactorAuthentication()
//
// POST: /Manage/EnableTwoFactorAuthentication
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EnableTwoFactorAuthentication()
{
var user = await GetCurrentUserAsync();
if (user is not null)
{
var user = await GetCurrentUserAsync();
if (user is not null)
{
await _userManager.SetTwoFactorEnabledAsync(user, true);
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation(1, "User enabled two-factor authentication.");
}
return RedirectToAction(nameof(Index), "Manage");
await _userManager.SetTwoFactorEnabledAsync(user, true);
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation(1, "User enabled two-factor authentication.");
}
return RedirectToAction(nameof(Index), "Manage");
}
//
// POST: /Manage/DisableTwoFactorAuthentication
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DisableTwoFactorAuthentication()
//
// POST: /Manage/DisableTwoFactorAuthentication
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DisableTwoFactorAuthentication()
{
var user = await GetCurrentUserAsync();
if (user is not null)
{
var user = await GetCurrentUserAsync();
if (user is not null)
{
await _userManager.SetTwoFactorEnabledAsync(user, false);
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation(2, "User disabled two-factor authentication.");
}
return RedirectToAction(nameof(Index), "Manage");
await _userManager.SetTwoFactorEnabledAsync(user, false);
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation(2, "User disabled two-factor authentication.");
}
return RedirectToAction(nameof(Index), "Manage");
}
//
// GET: /Manage/VerifyPhoneNumber
[HttpGet]
public async Task<IActionResult> VerifyPhoneNumber(string phoneNumber)
{
var code = await _userManager.GenerateChangePhoneNumberTokenAsync(await GetCurrentUserAsync(), phoneNumber);
// Send an SMS to verify the phone number
return phoneNumber is null ? View("Error") : View(new VerifyPhoneNumberViewModel { PhoneNumber = phoneNumber });
}
//
// GET: /Manage/VerifyPhoneNumber
[HttpGet]
public async Task<IActionResult> VerifyPhoneNumber(string phoneNumber)
//
// POST: /Manage/VerifyPhoneNumber
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> VerifyPhoneNumber(VerifyPhoneNumberViewModel model)
{
if (!ModelState.IsValid)
{
var code = await _userManager.GenerateChangePhoneNumberTokenAsync(await GetCurrentUserAsync(), phoneNumber);
// Send an SMS to verify the phone number
return phoneNumber is null ? View("Error") : View(new VerifyPhoneNumberViewModel { PhoneNumber = phoneNumber });
return View(model);
}
//
// POST: /Manage/VerifyPhoneNumber
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> VerifyPhoneNumber(VerifyPhoneNumberViewModel model)
var user = await GetCurrentUserAsync();
if (user is not null)
{
if (!ModelState.IsValid)
var result = await _userManager.ChangePhoneNumberAsync(user, model.PhoneNumber, model.Code);
if (result.Succeeded)
{
return View(model);
}
var user = await GetCurrentUserAsync();
if (user is not null)
{
var result = await _userManager.ChangePhoneNumberAsync(user, model.PhoneNumber, model.Code);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.AddPhoneSuccess });
}
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.AddPhoneSuccess });
}
// If we got this far, something failed, redisplay the form
ModelState.AddModelError(string.Empty, "Failed to verify phone number");
return View(model);
}
// If we got this far, something failed, redisplay the form
ModelState.AddModelError(string.Empty, "Failed to verify phone number");
return View(model);
}
//
// GET: /Manage/RemovePhoneNumber
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> RemovePhoneNumber()
//
// GET: /Manage/RemovePhoneNumber
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> RemovePhoneNumber()
{
var user = await GetCurrentUserAsync();
if (user is not null)
{
var user = await GetCurrentUserAsync();
if (user is not null)
var result = await _userManager.SetPhoneNumberAsync(user, null);
if (result.Succeeded)
{
var result = await _userManager.SetPhoneNumberAsync(user, null);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.RemovePhoneSuccess });
}
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.RemovePhoneSuccess });
}
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error });
}
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error });
}
//
// GET: /Manage/ChangePassword
[HttpGet]
public IActionResult ChangePassword()
{
return View();
}
//
// GET: /Manage/ChangePassword
[HttpGet]
public IActionResult ChangePassword()
//
// POST: /Manage/ChangePassword
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ChangePassword(ChangePasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View();
return View(model);
}
//
// POST: /Manage/ChangePassword
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ChangePassword(ChangePasswordViewModel model)
var user = await GetCurrentUserAsync();
if (user is not null)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await GetCurrentUserAsync();
if (user is not null)
var result = await _userManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword);
if (result.Succeeded)
{
var result = await _userManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation(3, "User changed their password successfully.");
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.ChangePasswordSuccess });
}
AddErrors(result);
return View(model);
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation(3, "User changed their password successfully.");
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.ChangePasswordSuccess });
}
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error });
AddErrors(result);
return View(model);
}
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error });
}
//
// GET: /Manage/SetPassword
[HttpGet]
public IActionResult SetPassword()
{
return View();
}
//
// GET: /Manage/SetPassword
[HttpGet]
public IActionResult SetPassword()
{
return View();
}
//
// POST: /Manage/SetPassword
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SetPassword(SetPasswordViewModel model)
//
// POST: /Manage/SetPassword
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SetPassword(SetPasswordViewModel model)
{
if (!ModelState.IsValid)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await GetCurrentUserAsync();
if (user is not null)
{
var result = await _userManager.AddPasswordAsync(user, model.NewPassword);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.SetPasswordSuccess });
}
AddErrors(result);
return View(model);
}
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error });
return View(model);
}
//GET: /Manage/ManageLogins
[HttpGet]
public async Task<IActionResult> ManageLogins(ManageMessageId? message = null)
var user = await GetCurrentUserAsync();
if (user is not null)
{
ViewData["StatusMessage"] =
message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed."
: message == ManageMessageId.AddLoginSuccess ? "The external login was added."
: message == ManageMessageId.Error ? "An error has occurred."
: "";
var user = await GetCurrentUserAsync();
if (user is null)
var result = await _userManager.AddPasswordAsync(user, model.NewPassword);
if (result.Succeeded)
{
return View("Error");
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.SetPasswordSuccess });
}
var userLogins = await _userManager.GetLoginsAsync(user);
var otherLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).Where(auth => userLogins.All(ul => auth.Name != ul.LoginProvider)).ToList();
ViewData["ShowRemoveButton"] = user.PasswordHash is not null || userLogins.Count > 1;
return View(new ManageLoginsViewModel
{
CurrentLogins = userLogins,
OtherLogins = otherLogins
});
AddErrors(result);
return View(model);
}
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error });
}
//
// POST: /Manage/LinkLogin
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult LinkLogin(string provider)
//GET: /Manage/ManageLogins
[HttpGet]
public async Task<IActionResult> ManageLogins(ManageMessageId? message = null)
{
ViewData["StatusMessage"] =
message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed."
: message == ManageMessageId.AddLoginSuccess ? "The external login was added."
: message == ManageMessageId.Error ? "An error has occurred."
: "";
var user = await GetCurrentUserAsync();
if (user is null)
{
// Request a redirect to the external login provider to link a login for the current user
var redirectUrl = Url.Action("LinkLoginCallback", "Manage");
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, _userManager.GetUserId(User));
return Challenge(properties, provider);
return View("Error");
}
//
// GET: /Manage/LinkLoginCallback
[HttpGet]
public async Task<ActionResult> LinkLoginCallback()
var userLogins = await _userManager.GetLoginsAsync(user);
var otherLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).Where(auth => userLogins.All(ul => auth.Name != ul.LoginProvider)).ToList();
ViewData["ShowRemoveButton"] = user.PasswordHash is not null || userLogins.Count > 1;
return View(new ManageLoginsViewModel
{
var user = await GetCurrentUserAsync();
if (user is null)
{
return View("Error");
}
var info = await _signInManager.GetExternalLoginInfoAsync(await _userManager.GetUserIdAsync(user));
if (info is null)
{
return RedirectToAction(nameof(ManageLogins), new { Message = ManageMessageId.Error });
}
var result = await _userManager.AddLoginAsync(user, info);
var message = result.Succeeded ? ManageMessageId.AddLoginSuccess : ManageMessageId.Error;
return RedirectToAction(nameof(ManageLogins), new { Message = message });
}
CurrentLogins = userLogins,
OtherLogins = otherLogins
});
}
#region Helpers
//
// POST: /Manage/LinkLogin
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult LinkLogin(string provider)
{
// Request a redirect to the external login provider to link a login for the current user
var redirectUrl = Url.Action("LinkLoginCallback", "Manage");
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, _userManager.GetUserId(User));
return Challenge(properties, provider);
}
private void AddErrors(IdentityResult result)
//
// GET: /Manage/LinkLoginCallback
[HttpGet]
public async Task<ActionResult> LinkLoginCallback()
{
var user = await GetCurrentUserAsync();
if (user is null)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
return View("Error");
}
public enum ManageMessageId
var info = await _signInManager.GetExternalLoginInfoAsync(await _userManager.GetUserIdAsync(user));
if (info is null)
{
AddPhoneSuccess,
AddLoginSuccess,
ChangePasswordSuccess,
SetTwoFactorSuccess,
SetPasswordSuccess,
RemoveLoginSuccess,
RemovePhoneSuccess,
Error
return RedirectToAction(nameof(ManageLogins), new { Message = ManageMessageId.Error });
}
var result = await _userManager.AddLoginAsync(user, info);
var message = result.Succeeded ? ManageMessageId.AddLoginSuccess : ManageMessageId.Error;
return RedirectToAction(nameof(ManageLogins), new { Message = message });
}
#region Helpers
private Task<ApplicationUser> GetCurrentUserAsync()
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
return _userManager.GetUserAsync(User);
ModelState.AddModelError(string.Empty, error.Description);
}
}
public enum ManageMessageId
{
AddPhoneSuccess,
AddLoginSuccess,
ChangePasswordSuccess,
SetTwoFactorSuccess,
SetPasswordSuccess,
RemoveLoginSuccess,
RemovePhoneSuccess,
Error
}
#endregion
private Task<ApplicationUser> GetCurrentUserAsync()
{
return _userManager.GetUserAsync(User);
}
}
#endregion
}

79
samples/Mvc.Server/Controllers/ResourceController.cs

@ -9,50 +9,49 @@ using OpenIddict.Abstractions;
using OpenIddict.Validation.AspNetCore;
using static OpenIddict.Abstractions.OpenIddictConstants;
namespace Mvc.Server.Controllers
namespace Mvc.Server.Controllers;
[Route("api")]
public class ResourceController : Controller
{
[Route("api")]
public class ResourceController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly UserManager<ApplicationUser> _userManager;
public ResourceController(UserManager<ApplicationUser> userManager)
=> _userManager = userManager;
public ResourceController(UserManager<ApplicationUser> userManager)
=> _userManager = userManager;
[Authorize(AuthenticationSchemes = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme)]
[HttpGet("message")]
public async Task<IActionResult> GetMessage()
[Authorize(AuthenticationSchemes = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme)]
[HttpGet("message")]
public async Task<IActionResult> GetMessage()
{
// This demo action requires that the client application be granted the "demo_api" scope.
// If it was not granted, a detailed error is returned to the client application to inform it
// that the authorization process must be restarted with the specified scope to access this API.
if (!User.HasScope("demo_api"))
{
// This demo action requires that the client application be granted the "demo_api" scope.
// If it was not granted, a detailed error is returned to the client application to inform it
// that the authorization process must be restarted with the specified scope to access this API.
if (!User.HasScope("demo_api"))
{
return Forbid(
authenticationSchemes: OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictValidationAspNetCoreConstants.Properties.Scope] = "demo_api",
[OpenIddictValidationAspNetCoreConstants.Properties.Error] = Errors.InsufficientScope,
[OpenIddictValidationAspNetCoreConstants.Properties.ErrorDescription] =
"The 'demo_api' scope is required to perform this action."
}));
}
var user = await _userManager.GetUserAsync(User);
if (user is null)
{
return Challenge(
authenticationSchemes: OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictValidationAspNetCoreConstants.Properties.Error] = Errors.InvalidToken,
[OpenIddictValidationAspNetCoreConstants.Properties.ErrorDescription] =
"The specified access token is bound to an account that no longer exists."
}));
}
return Forbid(
authenticationSchemes: OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictValidationAspNetCoreConstants.Properties.Scope] = "demo_api",
[OpenIddictValidationAspNetCoreConstants.Properties.Error] = Errors.InsufficientScope,
[OpenIddictValidationAspNetCoreConstants.Properties.ErrorDescription] =
"The 'demo_api' scope is required to perform this action."
}));
}
return Content($"{user.UserName} has been successfully authenticated.");
var user = await _userManager.GetUserAsync(User);
if (user is null)
{
return Challenge(
authenticationSchemes: OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictValidationAspNetCoreConstants.Properties.Error] = Errors.InvalidToken,
[OpenIddictValidationAspNetCoreConstants.Properties.ErrorDescription] =
"The specified access token is bound to an account that no longer exists."
}));
}
return Content($"{user.UserName} has been successfully authenticated.");
}
}
}

89
samples/Mvc.Server/Controllers/UserinfoController.cs

@ -10,60 +10,59 @@ using OpenIddict.Abstractions;
using OpenIddict.Server.AspNetCore;
using static OpenIddict.Abstractions.OpenIddictConstants;
namespace Mvc.Server.Controllers
namespace Mvc.Server.Controllers;
public class UserinfoController : Controller
{
public class UserinfoController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly UserManager<ApplicationUser> _userManager;
public UserinfoController(UserManager<ApplicationUser> userManager)
=> _userManager = userManager;
public UserinfoController(UserManager<ApplicationUser> userManager)
=> _userManager = userManager;
[Authorize(AuthenticationSchemes = OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)]
[HttpGet("~/connect/userinfo"), HttpPost("~/connect/userinfo")]
[IgnoreAntiforgeryToken, Produces("application/json")]
public async Task<IActionResult> Userinfo()
[Authorize(AuthenticationSchemes = OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)]
[HttpGet("~/connect/userinfo"), HttpPost("~/connect/userinfo")]
[IgnoreAntiforgeryToken, Produces("application/json")]
public async Task<IActionResult> Userinfo()
{
var user = await _userManager.GetUserAsync(User);
if (user is null)
{
var user = await _userManager.GetUserAsync(User);
if (user is null)
{
return Challenge(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidToken,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
"The specified access token is bound to an account that no longer exists."
}));
}
return Challenge(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidToken,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
"The specified access token is bound to an account that no longer exists."
}));
}
var claims = new Dictionary<string, object>(StringComparer.Ordinal)
{
// Note: the "sub" claim is a mandatory claim and must be included in the JSON response.
[Claims.Subject] = await _userManager.GetUserIdAsync(user)
};
var claims = new Dictionary<string, object>(StringComparer.Ordinal)
{
// Note: the "sub" claim is a mandatory claim and must be included in the JSON response.
[Claims.Subject] = await _userManager.GetUserIdAsync(user)
};
if (User.HasScope(Scopes.Email))
{
claims[Claims.Email] = await _userManager.GetEmailAsync(user);
claims[Claims.EmailVerified] = await _userManager.IsEmailConfirmedAsync(user);
}
if (User.HasScope(Scopes.Email))
{
claims[Claims.Email] = await _userManager.GetEmailAsync(user);
claims[Claims.EmailVerified] = await _userManager.IsEmailConfirmedAsync(user);
}
if (User.HasScope(Scopes.Phone))
{
claims[Claims.PhoneNumber] = await _userManager.GetPhoneNumberAsync(user);
claims[Claims.PhoneNumberVerified] = await _userManager.IsPhoneNumberConfirmedAsync(user);
}
if (User.HasScope(Scopes.Phone))
{
claims[Claims.PhoneNumber] = await _userManager.GetPhoneNumberAsync(user);
claims[Claims.PhoneNumberVerified] = await _userManager.IsPhoneNumberConfirmedAsync(user);
}
if (User.HasScope(Scopes.Roles))
{
claims[Claims.Role] = await _userManager.GetRolesAsync(user);
}
if (User.HasScope(Scopes.Roles))
{
claims[Claims.Role] = 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
// 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 Ok(claims);
}
return Ok(claims);
}
}

33
samples/Mvc.Server/Helpers/AsyncEnumerableExtensions.cs

@ -2,30 +2,29 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Mvc.Server.Helpers
namespace Mvc.Server.Helpers;
public static class AsyncEnumerableExtensions
{
public static class AsyncEnumerableExtensions
public static Task<List<T>> ToListAsync<T>(this IAsyncEnumerable<T> source)
{
public static Task<List<T>> ToListAsync<T>(this IAsyncEnumerable<T> source)
if (source is null)
{
if (source is null)
{
throw new ArgumentNullException(nameof(source));
}
throw new ArgumentNullException(nameof(source));
}
return ExecuteAsync();
return ExecuteAsync();
async Task<List<T>> ExecuteAsync()
{
var list = new List<T>();
await foreach (var element in source)
{
list.Add(element);
}
async Task<List<T>> ExecuteAsync()
{
var list = new List<T>();
return list;
await foreach (var element in source)
{
list.Add(element);
}
return list;
}
}
}

47
samples/Mvc.Server/Helpers/FormValueRequiredAttribute.cs

@ -3,38 +3,37 @@ using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Routing;
namespace Mvc.Server.Helpers
namespace Mvc.Server.Helpers;
public sealed class FormValueRequiredAttribute : ActionMethodSelectorAttribute
{
public sealed class FormValueRequiredAttribute : ActionMethodSelectorAttribute
private readonly string _name;
public FormValueRequiredAttribute(string name)
{
private readonly string _name;
_name = name;
}
public FormValueRequiredAttribute(string name)
public override bool IsValidForRequest(RouteContext routeContext, ActionDescriptor action)
{
if (string.Equals(routeContext.HttpContext.Request.Method, "GET", StringComparison.OrdinalIgnoreCase) ||
string.Equals(routeContext.HttpContext.Request.Method, "HEAD", StringComparison.OrdinalIgnoreCase) ||
string.Equals(routeContext.HttpContext.Request.Method, "DELETE", StringComparison.OrdinalIgnoreCase) ||
string.Equals(routeContext.HttpContext.Request.Method, "TRACE", StringComparison.OrdinalIgnoreCase))
{
_name = name;
return false;
}
public override bool IsValidForRequest(RouteContext routeContext, ActionDescriptor action)
if (string.IsNullOrEmpty(routeContext.HttpContext.Request.ContentType))
{
if (string.Equals(routeContext.HttpContext.Request.Method, "GET", StringComparison.OrdinalIgnoreCase) ||
string.Equals(routeContext.HttpContext.Request.Method, "HEAD", StringComparison.OrdinalIgnoreCase) ||
string.Equals(routeContext.HttpContext.Request.Method, "DELETE", StringComparison.OrdinalIgnoreCase) ||
string.Equals(routeContext.HttpContext.Request.Method, "TRACE", StringComparison.OrdinalIgnoreCase))
{
return false;
}
if (string.IsNullOrEmpty(routeContext.HttpContext.Request.ContentType))
{
return false;
}
if (!routeContext.HttpContext.Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase))
{
return false;
}
return false;
}
return !string.IsNullOrEmpty(routeContext.HttpContext.Request.Form[_name]);
if (!routeContext.HttpContext.Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase))
{
return false;
}
return !string.IsNullOrEmpty(routeContext.HttpContext.Request.Form[_name]);
}
}

23
samples/Mvc.Server/Models/ApplicationDbContext.cs

@ -1,20 +1,19 @@
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace Mvc.Server.Models
namespace Mvc.Server.Models;
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions options)
: base(options) { }
public ApplicationDbContext(DbContextOptions options)
: base(options) { }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}

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

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

17
samples/Mvc.Server/Program.cs

@ -1,15 +1,14 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace Mvc.Server
namespace Mvc.Server;
public static class Program
{
public static class Program
{
public static void Main(string[] args) =>
CreateHostBuilder(args).Build().Run();
public static void Main(string[] args) =>
CreateHostBuilder(args).Build().Run();
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(builder => builder.UseStartup<Startup>());
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(builder => builder.UseStartup<Startup>());
}

9
samples/Mvc.Server/Services/IEmailSender.cs

@ -1,9 +1,8 @@
using System.Threading.Tasks;
namespace Mvc.Server.Services
namespace Mvc.Server.Services;
public interface IEmailSender
{
public interface IEmailSender
{
Task SendEmailAsync(string email, string subject, string message);
}
Task SendEmailAsync(string email, string subject, string message);
}

9
samples/Mvc.Server/Services/ISmsSender.cs

@ -1,9 +1,8 @@
using System.Threading.Tasks;
namespace Mvc.Server.Services
namespace Mvc.Server.Services;
public interface ISmsSender
{
public interface ISmsSender
{
Task SendSmsAsync(string number, string message);
}
Task SendSmsAsync(string number, string message);
}

29
samples/Mvc.Server/Services/MessageServices.cs

@ -1,22 +1,21 @@
using System.Threading.Tasks;
namespace Mvc.Server.Services
namespace Mvc.Server.Services;
// This class is used by the application to send Email and SMS
// when you turn on two-factor authentication in ASP.NET Identity.
// For more details see this link http://go.microsoft.com/fwlink/?LinkID=532713
public class AuthMessageSender : IEmailSender, ISmsSender
{
// This class is used by the application to send Email and SMS
// when you turn on two-factor authentication in ASP.NET Identity.
// For more details see this link http://go.microsoft.com/fwlink/?LinkID=532713
public class AuthMessageSender : IEmailSender, ISmsSender
public Task SendEmailAsync(string email, string subject, string message)
{
public Task SendEmailAsync(string email, string subject, string message)
{
// Plug in your email service here to send an email.
return Task.FromResult(0);
}
// Plug in your email service here to send an email.
return Task.FromResult(0);
}
public Task SendSmsAsync(string number, string message)
{
// Plug in your SMS service here to send a text message.
return Task.FromResult(0);
}
public Task SendSmsAsync(string number, string message)
{
// Plug in your SMS service here to send a text message.
return Task.FromResult(0);
}
}

341
samples/Mvc.Server/Startup.cs

@ -8,193 +8,192 @@ using Mvc.Server.Services;
using Quartz;
using static OpenIddict.Abstractions.OpenIddictConstants;
namespace Mvc.Server
namespace Mvc.Server;
public class Startup
{
public class Startup
{
public Startup(IConfiguration configuration)
=> Configuration = configuration;
public Startup(IConfiguration configuration)
=> Configuration = configuration;
public IConfiguration Configuration { get; }
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddDbContext<ApplicationDbContext>(options =>
{
// Configure the context to use Microsoft SQL Server.
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
services.AddDbContext<ApplicationDbContext>(options =>
{
// Configure the context to use Microsoft SQL Server.
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
// Register the entity sets needed by OpenIddict.
// Note: use the generic overload if you need
// to replace the default OpenIddict entities.
options.UseOpenIddict();
});
// Register the Identity services.
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
// Configure Identity to use the same JWT claims as OpenIddict instead
// of the legacy WS-Federation claims it uses by default (ClaimTypes),
// which saves you from doing the mapping in your authorization controller.
services.Configure<IdentityOptions>(options =>
{
options.ClaimsIdentity.UserNameClaimType = Claims.Name;
options.ClaimsIdentity.UserIdClaimType = Claims.Subject;
options.ClaimsIdentity.RoleClaimType = Claims.Role;
options.ClaimsIdentity.EmailClaimType = Claims.Email;
});
// OpenIddict offers native integration with Quartz.NET to perform scheduled tasks
// (like pruning orphaned authorizations/tokens from the database) at regular intervals.
services.AddQuartz(options =>
{
options.UseMicrosoftDependencyInjectionJobFactory();
options.UseSimpleTypeLoader();
options.UseInMemoryStore();
});
// Register the entity sets needed by OpenIddict.
// Note: use the generic overload if you need
// to replace the default OpenIddict entities.
options.UseOpenIddict();
});
// Register the Quartz.NET service and configure it to block shutdown until jobs are complete.
services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true);
// Register the Identity services.
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddOpenIddict()
// Configure Identity to use the same JWT claims as OpenIddict instead
// of the legacy WS-Federation claims it uses by default (ClaimTypes),
// which saves you from doing the mapping in your authorization controller.
services.Configure<IdentityOptions>(options =>
// Register the OpenIddict core components.
.AddCore(options =>
{
options.ClaimsIdentity.UserNameClaimType = Claims.Name;
options.ClaimsIdentity.UserIdClaimType = Claims.Subject;
options.ClaimsIdentity.RoleClaimType = Claims.Role;
options.ClaimsIdentity.EmailClaimType = Claims.Email;
});
// OpenIddict offers native integration with Quartz.NET to perform scheduled tasks
// (like pruning orphaned authorizations/tokens from the database) at regular intervals.
services.AddQuartz(options =>
// Configure OpenIddict to use the Entity Framework Core stores and models.
// Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities.
options.UseEntityFrameworkCore()
.UseDbContext<ApplicationDbContext>();
// Developers who prefer using MongoDB can remove the previous lines
// and configure OpenIddict to use the specified MongoDB database:
// options.UseMongoDb()
// .UseDatabase(new MongoClient().GetDatabase("openiddict"));
// Enable Quartz.NET integration.
options.UseQuartz();
})
// Register the OpenIddict server components.
.AddServer(options =>
{
// Enable the authorization, device, logout, token, userinfo and verification endpoints.
options.SetAuthorizationEndpointUris("/connect/authorize")
.SetDeviceEndpointUris("/connect/device")
.SetLogoutEndpointUris("/connect/logout")
.SetTokenEndpointUris("/connect/token")
.SetUserinfoEndpointUris("/connect/userinfo")
.SetVerificationEndpointUris("/connect/verify");
// Note: this sample uses the code, device code, password and refresh token flows, but you
// can enable the other flows if you need to support implicit or client credentials.
options.AllowAuthorizationCodeFlow()
.AllowDeviceCodeFlow()
.AllowPasswordFlow()
.AllowRefreshTokenFlow();
// Mark the "email", "profile", "roles" and "demo_api" scopes as supported scopes.
options.RegisterScopes(Scopes.Email, Scopes.Profile, Scopes.Roles, "demo_api");
// Register the signing and encryption credentials.
options.AddDevelopmentEncryptionCertificate()
.AddDevelopmentSigningCertificate();
// Force client applications to use Proof Key for Code Exchange (PKCE).
options.RequireProofKeyForCodeExchange();
// Register the ASP.NET Core host and configure the ASP.NET Core-specific options.
options.UseAspNetCore()
.EnableStatusCodePagesIntegration()
.EnableAuthorizationEndpointPassthrough()
.EnableLogoutEndpointPassthrough()
.EnableTokenEndpointPassthrough()
.EnableUserinfoEndpointPassthrough()
.EnableVerificationEndpointPassthrough()
.DisableTransportSecurityRequirement(); // During development, you can disable the HTTPS requirement.
// Note: if you don't want to specify a client_id when sending
// a token or revocation request, uncomment the following line:
//
// options.AcceptAnonymousClients();
// Note: if you want to process authorization and token requests
// that specify non-registered scopes, uncomment the following line:
//
// options.DisableScopeValidation();
// Note: if you don't want to use permissions, you can disable
// permission enforcement by uncommenting the following lines:
//
// options.IgnoreEndpointPermissions()
// .IgnoreGrantTypePermissions()
// .IgnoreResponseTypePermissions()
// .IgnoreScopePermissions();
// Note: when issuing access tokens used by third-party APIs
// you don't own, you can disable access token encryption:
//
// options.DisableAccessTokenEncryption();
})
// Register the OpenIddict validation components.
.AddValidation(options =>
{
options.UseMicrosoftDependencyInjectionJobFactory();
options.UseSimpleTypeLoader();
options.UseInMemoryStore();
// Configure the audience accepted by this resource server.
// The value MUST match the audience associated with the
// "demo_api" scope, which is used by ResourceController.
options.AddAudiences("resource_server");
// Import the configuration from the local OpenIddict server instance.
options.UseLocalServer();
// Register the ASP.NET Core host.
options.UseAspNetCore();
// For applications that need immediate access token or authorization
// revocation, the database entry of the received tokens and their
// associated authorizations can be validated for each API call.
// Enabling these options may have a negative impact on performance.
//
// options.EnableAuthorizationEntryValidation();
// options.EnableTokenEntryValidation();
});
// Register the Quartz.NET service and configure it to block shutdown until jobs are complete.
services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true);
services.AddOpenIddict()
// Register the OpenIddict core components.
.AddCore(options =>
{
// Configure OpenIddict to use the Entity Framework Core stores and models.
// Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities.
options.UseEntityFrameworkCore()
.UseDbContext<ApplicationDbContext>();
// Developers who prefer using MongoDB can remove the previous lines
// and configure OpenIddict to use the specified MongoDB database:
// options.UseMongoDb()
// .UseDatabase(new MongoClient().GetDatabase("openiddict"));
// Enable Quartz.NET integration.
options.UseQuartz();
})
// Register the OpenIddict server components.
.AddServer(options =>
{
// Enable the authorization, device, logout, token, userinfo and verification endpoints.
options.SetAuthorizationEndpointUris("/connect/authorize")
.SetDeviceEndpointUris("/connect/device")
.SetLogoutEndpointUris("/connect/logout")
.SetTokenEndpointUris("/connect/token")
.SetUserinfoEndpointUris("/connect/userinfo")
.SetVerificationEndpointUris("/connect/verify");
// Note: this sample uses the code, device code, password and refresh token flows, but you
// can enable the other flows if you need to support implicit or client credentials.
options.AllowAuthorizationCodeFlow()
.AllowDeviceCodeFlow()
.AllowPasswordFlow()
.AllowRefreshTokenFlow();
// Mark the "email", "profile", "roles" and "demo_api" scopes as supported scopes.
options.RegisterScopes(Scopes.Email, Scopes.Profile, Scopes.Roles, "demo_api");
// Register the signing and encryption credentials.
options.AddDevelopmentEncryptionCertificate()
.AddDevelopmentSigningCertificate();
// Force client applications to use Proof Key for Code Exchange (PKCE).
options.RequireProofKeyForCodeExchange();
// Register the ASP.NET Core host and configure the ASP.NET Core-specific options.
options.UseAspNetCore()
.EnableStatusCodePagesIntegration()
.EnableAuthorizationEndpointPassthrough()
.EnableLogoutEndpointPassthrough()
.EnableTokenEndpointPassthrough()
.EnableUserinfoEndpointPassthrough()
.EnableVerificationEndpointPassthrough()
.DisableTransportSecurityRequirement(); // During development, you can disable the HTTPS requirement.
// Note: if you don't want to specify a client_id when sending
// a token or revocation request, uncomment the following line:
//
// options.AcceptAnonymousClients();
// Note: if you want to process authorization and token requests
// that specify non-registered scopes, uncomment the following line:
//
// options.DisableScopeValidation();
// Note: if you don't want to use permissions, you can disable
// permission enforcement by uncommenting the following lines:
//
// options.IgnoreEndpointPermissions()
// .IgnoreGrantTypePermissions()
// .IgnoreResponseTypePermissions()
// .IgnoreScopePermissions();
// Note: when issuing access tokens used by third-party APIs
// you don't own, you can disable access token encryption:
//
// options.DisableAccessTokenEncryption();
})
// Register the OpenIddict validation components.
.AddValidation(options =>
{
// Configure the audience accepted by this resource server.
// The value MUST match the audience associated with the
// "demo_api" scope, which is used by ResourceController.
options.AddAudiences("resource_server");
// Import the configuration from the local OpenIddict server instance.
options.UseLocalServer();
// Register the ASP.NET Core host.
options.UseAspNetCore();
// For applications that need immediate access token or authorization
// revocation, the database entry of the received tokens and their
// associated authorizations can be validated for each API call.
// Enabling these options may have a negative impact on performance.
//
// options.EnableAuthorizationEntryValidation();
// options.EnableTokenEntryValidation();
});
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
// Register the worker responsible of seeding the database with the sample clients.
// Note: in a real world application, this step should be part of a setup script.
services.AddHostedService<Worker>();
}
public void Configure(IApplicationBuilder app)
{
app.UseDeveloperExceptionPage();
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
// Register the worker responsible of seeding the database with the sample clients.
// Note: in a real world application, this step should be part of a setup script.
services.AddHostedService<Worker>();
}
app.UseStaticFiles();
public void Configure(IApplicationBuilder app)
{
app.UseDeveloperExceptionPage();
app.UseStatusCodePagesWithReExecute("/error");
app.UseStaticFiles();
app.UseRouting();
app.UseStatusCodePagesWithReExecute("/error");
app.UseRequestLocalization(options =>
{
options.AddSupportedCultures("en-US", "fr-FR");
options.AddSupportedUICultures("en-US", "fr-FR");
options.SetDefaultCulture("en-US");
});
app.UseRouting();
app.UseRequestLocalization(options =>
{
options.AddSupportedCultures("en-US", "fr-FR");
options.AddSupportedUICultures("en-US", "fr-FR");
options.SetDefaultCulture("en-US");
});
app.UseAuthentication();
app.UseAuthorization();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(options => options.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}"));
}
app.UseEndpoints(options => options.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}"));
}
}

13
samples/Mvc.Server/ViewModels/Account/ExternalLoginConfirmationViewModel.cs

@ -1,11 +1,10 @@
using System.ComponentModel.DataAnnotations;
namespace Mvc.Server.ViewModels.Account
namespace Mvc.Server.ViewModels.Account;
public class ExternalLoginConfirmationViewModel
{
public class ExternalLoginConfirmationViewModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
}
[Required]
[EmailAddress]
public string Email { get; set; }
}

13
samples/Mvc.Server/ViewModels/Account/ForgotPasswordViewModel.cs

@ -1,11 +1,10 @@
using System.ComponentModel.DataAnnotations;
namespace Mvc.Server.ViewModels.Account
namespace Mvc.Server.ViewModels.Account;
public class ForgotPasswordViewModel
{
public class ForgotPasswordViewModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
}
[Required]
[EmailAddress]
public string Email { get; set; }
}

23
samples/Mvc.Server/ViewModels/Account/LoginViewModel.cs

@ -1,18 +1,17 @@
using System.ComponentModel.DataAnnotations;
namespace Mvc.Server.ViewModels.Account
namespace Mvc.Server.ViewModels.Account;
public class LoginViewModel
{
public class LoginViewModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}

33
samples/Mvc.Server/ViewModels/Account/RegisterViewModel.cs

@ -1,23 +1,22 @@
using System.ComponentModel.DataAnnotations;
namespace Mvc.Server.ViewModels.Account
namespace Mvc.Server.ViewModels.Account;
public class RegisterViewModel
{
public class RegisterViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}

31
samples/Mvc.Server/ViewModels/Account/ResetPasswordViewModel.cs

@ -1,23 +1,22 @@
using System.ComponentModel.DataAnnotations;
namespace Mvc.Server.ViewModels.Account
namespace Mvc.Server.ViewModels.Account;
public class ResetPasswordViewModel
{
public class ResetPasswordViewModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
public string Password { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
public string Code { get; set; }
}
public string Code { get; set; }
}

15
samples/Mvc.Server/ViewModels/Account/SendCodeViewModel.cs

@ -1,16 +1,15 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace Mvc.Server.ViewModels.Account
namespace Mvc.Server.ViewModels.Account;
public class SendCodeViewModel
{
public class SendCodeViewModel
{
public string SelectedProvider { get; set; }
public string SelectedProvider { get; set; }
public ICollection<SelectListItem> Providers { get; set; }
public ICollection<SelectListItem> Providers { get; set; }
public string ReturnUrl { get; set; }
public string ReturnUrl { get; set; }
public bool RememberMe { get; set; }
}
public bool RememberMe { get; set; }
}

25
samples/Mvc.Server/ViewModels/Account/VerifyCodeViewModel.cs

@ -1,21 +1,20 @@
using System.ComponentModel.DataAnnotations;
namespace Mvc.Server.ViewModels.Account
namespace Mvc.Server.ViewModels.Account;
public class VerifyCodeViewModel
{
public class VerifyCodeViewModel
{
[Required]
public string Provider { get; set; }
[Required]
public string Provider { get; set; }
[Required]
public string Code { get; set; }
[Required]
public string Code { get; set; }
public string ReturnUrl { get; set; }
public string ReturnUrl { get; set; }
[Display(Name = "Remember this browser?")]
public bool RememberBrowser { get; set; }
[Display(Name = "Remember this browser?")]
public bool RememberBrowser { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}

15
samples/Mvc.Server/ViewModels/Authorization/AuthorizeViewModel.cs

@ -1,13 +1,12 @@
using System.ComponentModel.DataAnnotations;
namespace Mvc.Server.ViewModels.Authorization
namespace Mvc.Server.ViewModels.Authorization;
public class AuthorizeViewModel
{
public class AuthorizeViewModel
{
[Display(Name = "Application")]
public string ApplicationName { get; set; }
[Display(Name = "Application")]
public string ApplicationName { get; set; }
[Display(Name = "Scope")]
public string Scope { get; set; }
}
[Display(Name = "Scope")]
public string Scope { get; set; }
}

29
samples/Mvc.Server/ViewModels/Authorization/VerifyViewModel.cs

@ -3,24 +3,23 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using OpenIddict.Abstractions;
namespace Mvc.Server.ViewModels.Authorization
namespace Mvc.Server.ViewModels.Authorization;
public class VerifyViewModel
{
public class VerifyViewModel
{
[Display(Name = "Application")]
public string ApplicationName { get; set; }
[Display(Name = "Application")]
public string ApplicationName { get; set; }
[BindNever, Display(Name = "Error")]
public string Error { get; set; }
[BindNever, Display(Name = "Error")]
public string Error { get; set; }
[BindNever, Display(Name = "Error description")]
public string ErrorDescription { get; set; }
[BindNever, Display(Name = "Error description")]
public string ErrorDescription { get; set; }
[Display(Name = "Scope")]
public string Scope { get; set; }
[Display(Name = "Scope")]
public string Scope { get; set; }
[FromQuery(Name = OpenIddictConstants.Parameters.UserCode)]
[Display(Name = "User code")]
public string UserCode { get; set; }
}
[FromQuery(Name = OpenIddictConstants.Parameters.UserCode)]
[Display(Name = "User code")]
public string UserCode { get; set; }
}

15
samples/Mvc.Server/ViewModels/Manage/AddPhoneNumberViewModel.cs

@ -1,12 +1,11 @@
using System.ComponentModel.DataAnnotations;
namespace Mvc.Server.ViewModels.Manage
namespace Mvc.Server.ViewModels.Manage;
public class AddPhoneNumberViewModel
{
public class AddPhoneNumberViewModel
{
[Required]
[Phone]
[Display(Name = "Phone number")]
public string PhoneNumber { get; set; }
}
[Required]
[Phone]
[Display(Name = "Phone number")]
public string PhoneNumber { get; set; }
}

33
samples/Mvc.Server/ViewModels/Manage/ChangePasswordViewModel.cs

@ -1,23 +1,22 @@
using System.ComponentModel.DataAnnotations;
namespace Mvc.Server.ViewModels.Manage
namespace Mvc.Server.ViewModels.Manage;
public class ChangePasswordViewModel
{
public class ChangePasswordViewModel
{
[Required]
[DataType(DataType.Password)]
[Display(Name = "Current password")]
public string OldPassword { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Current password")]
public string OldPassword { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}

11
samples/Mvc.Server/ViewModels/Manage/ConfigureTwoFactorViewModel.cs

@ -1,12 +1,11 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace Mvc.Server.ViewModels.Manage
namespace Mvc.Server.ViewModels.Manage;
public class ConfigureTwoFactorViewModel
{
public class ConfigureTwoFactorViewModel
{
public string SelectedProvider { get; set; }
public string SelectedProvider { get; set; }
public ICollection<SelectListItem> Providers { get; set; }
}
public ICollection<SelectListItem> Providers { get; set; }
}

9
samples/Mvc.Server/ViewModels/Manage/FactorViewModel.cs

@ -1,7 +1,6 @@
namespace Mvc.Server.ViewModels.Manage
namespace Mvc.Server.ViewModels.Manage;
public class FactorViewModel
{
public class FactorViewModel
{
public string Purpose { get; set; }
}
public string Purpose { get; set; }
}

17
samples/Mvc.Server/ViewModels/Manage/IndexViewModel.cs

@ -1,18 +1,17 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Identity;
namespace Mvc.Server.ViewModels.Manage
namespace Mvc.Server.ViewModels.Manage;
public class IndexViewModel
{
public class IndexViewModel
{
public bool HasPassword { get; set; }
public bool HasPassword { get; set; }
public IList<UserLoginInfo> Logins { get; set; }
public IList<UserLoginInfo> Logins { get; set; }
public string PhoneNumber { get; set; }
public string PhoneNumber { get; set; }
public bool TwoFactor { get; set; }
public bool TwoFactor { get; set; }
public bool BrowserRemembered { get; set; }
}
public bool BrowserRemembered { get; set; }
}

11
samples/Mvc.Server/ViewModels/Manage/ManageLoginsViewModel.cs

@ -2,12 +2,11 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
namespace Mvc.Server.ViewModels.Manage
namespace Mvc.Server.ViewModels.Manage;
public class ManageLoginsViewModel
{
public class ManageLoginsViewModel
{
public IList<UserLoginInfo> CurrentLogins { get; set; }
public IList<UserLoginInfo> CurrentLogins { get; set; }
public IList<AuthenticationScheme> OtherLogins { get; set; }
}
public IList<AuthenticationScheme> OtherLogins { get; set; }
}

13
samples/Mvc.Server/ViewModels/Manage/RemoveLoginViewModel.cs

@ -1,8 +1,7 @@
namespace Mvc.Server.ViewModels.Manage
namespace Mvc.Server.ViewModels.Manage;
public class RemoveLoginViewModel
{
public class RemoveLoginViewModel
{
public string LoginProvider { get; set; }
public string ProviderKey { get; set; }
}
}
public string LoginProvider { get; set; }
public string ProviderKey { get; set; }
}

25
samples/Mvc.Server/ViewModels/Manage/SetPasswordViewModel.cs

@ -1,18 +1,17 @@
using System.ComponentModel.DataAnnotations;
namespace Mvc.Server.ViewModels.Manage
namespace Mvc.Server.ViewModels.Manage;
public class SetPasswordViewModel
{
public class SetPasswordViewModel
{
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}

19
samples/Mvc.Server/ViewModels/Manage/VerifyPhoneNumberViewModel.cs

@ -1,15 +1,14 @@
using System.ComponentModel.DataAnnotations;
namespace Mvc.Server.ViewModels.Manage
namespace Mvc.Server.ViewModels.Manage;
public class VerifyPhoneNumberViewModel
{
public class VerifyPhoneNumberViewModel
{
[Required]
public string Code { get; set; }
[Required]
public string Code { get; set; }
[Required]
[Phone]
[Display(Name = "Phone number")]
public string PhoneNumber { get; set; }
}
[Required]
[Phone]
[Display(Name = "Phone number")]
public string PhoneNumber { get; set; }
}

15
samples/Mvc.Server/ViewModels/Shared/ErrorViewModel.cs

@ -1,13 +1,12 @@
using System.ComponentModel.DataAnnotations;
namespace Mvc.Server.ViewModels.Shared
namespace Mvc.Server.ViewModels.Shared;
public class ErrorViewModel
{
public class ErrorViewModel
{
[Display(Name = "Error")]
public string Error { get; set; }
[Display(Name = "Error")]
public string Error { get; set; }
[Display(Name = "Description")]
public string ErrorDescription { get; set; }
}
[Display(Name = "Description")]
public string ErrorDescription { get; set; }
}

211
samples/Mvc.Server/Worker.cs

@ -8,130 +8,129 @@ using Mvc.Server.Models;
using OpenIddict.Abstractions;
using static OpenIddict.Abstractions.OpenIddictConstants;
namespace Mvc.Server
namespace Mvc.Server;
public class Worker : IHostedService
{
public class Worker : IHostedService
{
private readonly IServiceProvider _serviceProvider;
private readonly IServiceProvider _serviceProvider;
public Worker(IServiceProvider serviceProvider)
=> _serviceProvider = serviceProvider;
public Worker(IServiceProvider serviceProvider)
=> _serviceProvider = serviceProvider;
public async Task StartAsync(CancellationToken cancellationToken)
{
using var scope = _serviceProvider.CreateScope();
public async Task StartAsync(CancellationToken cancellationToken)
{
using var scope = _serviceProvider.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
await context.Database.EnsureCreatedAsync(cancellationToken);
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
await context.Database.EnsureCreatedAsync(cancellationToken);
await RegisterApplicationsAsync(scope.ServiceProvider);
await RegisterScopesAsync(scope.ServiceProvider);
await RegisterApplicationsAsync(scope.ServiceProvider);
await RegisterScopesAsync(scope.ServiceProvider);
static async Task RegisterApplicationsAsync(IServiceProvider provider)
{
var manager = provider.GetRequiredService<IOpenIddictApplicationManager>();
static async Task RegisterApplicationsAsync(IServiceProvider provider)
{
var manager = provider.GetRequiredService<IOpenIddictApplicationManager>();
if (await manager.FindByClientIdAsync("mvc") is null)
if (await manager.FindByClientIdAsync("mvc") is null)
{
await manager.CreateAsync(new OpenIddictApplicationDescriptor
{
await manager.CreateAsync(new OpenIddictApplicationDescriptor
ClientId = "mvc",
ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654",
ConsentType = ConsentTypes.Explicit,
DisplayName = "MVC client application",
DisplayNames =
{
[CultureInfo.GetCultureInfo("fr-FR")] = "Application cliente MVC"
},
PostLogoutRedirectUris =
{
new Uri("https://localhost:44381/signout-callback-oidc")
},
RedirectUris =
{
new Uri("https://localhost:44381/signin-oidc")
},
Permissions =
{
Permissions.Endpoints.Authorization,
Permissions.Endpoints.Logout,
Permissions.Endpoints.Token,
Permissions.GrantTypes.AuthorizationCode,
Permissions.GrantTypes.RefreshToken,
Permissions.ResponseTypes.Code,
Permissions.Scopes.Email,
Permissions.Scopes.Profile,
Permissions.Scopes.Roles,
Permissions.Prefixes.Scope + "demo_api"
},
Requirements =
{
ClientId = "mvc",
ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654",
ConsentType = ConsentTypes.Explicit,
DisplayName = "MVC client application",
DisplayNames =
{
[CultureInfo.GetCultureInfo("fr-FR")] = "Application cliente MVC"
},
PostLogoutRedirectUris =
{
new Uri("https://localhost:44381/signout-callback-oidc")
},
RedirectUris =
{
new Uri("https://localhost:44381/signin-oidc")
},
Permissions =
{
Permissions.Endpoints.Authorization,
Permissions.Endpoints.Logout,
Permissions.Endpoints.Token,
Permissions.GrantTypes.AuthorizationCode,
Permissions.GrantTypes.RefreshToken,
Permissions.ResponseTypes.Code,
Permissions.Scopes.Email,
Permissions.Scopes.Profile,
Permissions.Scopes.Roles,
Permissions.Prefixes.Scope + "demo_api"
},
Requirements =
{
Requirements.Features.ProofKeyForCodeExchange
}
});
}
Requirements.Features.ProofKeyForCodeExchange
}
});
}
// To test this sample with Postman, use the following settings:
//
// * Authorization URL: https://localhost:44395/connect/authorize
// * Access token URL: https://localhost:44395/connect/token
// * Client ID: postman
// * Client secret: [blank] (not used with public clients)
// * Scope: openid email profile roles
// * Grant type: authorization code
// * Request access token locally: yes
if (await manager.FindByClientIdAsync("postman") is null)
// To test this sample with Postman, use the following settings:
//
// * Authorization URL: https://localhost:44395/connect/authorize
// * Access token URL: https://localhost:44395/connect/token
// * Client ID: postman
// * Client secret: [blank] (not used with public clients)
// * Scope: openid email profile roles
// * Grant type: authorization code
// * Request access token locally: yes
if (await manager.FindByClientIdAsync("postman") is null)
{
await manager.CreateAsync(new OpenIddictApplicationDescriptor
{
await manager.CreateAsync(new OpenIddictApplicationDescriptor
ClientId = "postman",
ConsentType = ConsentTypes.Systematic,
DisplayName = "Postman",
RedirectUris =
{
new Uri("urn:postman")
},
Permissions =
{
ClientId = "postman",
ConsentType = ConsentTypes.Systematic,
DisplayName = "Postman",
RedirectUris =
{
new Uri("urn:postman")
},
Permissions =
{
Permissions.Endpoints.Authorization,
Permissions.Endpoints.Device,
Permissions.Endpoints.Token,
Permissions.GrantTypes.AuthorizationCode,
Permissions.GrantTypes.DeviceCode,
Permissions.GrantTypes.Password,
Permissions.GrantTypes.RefreshToken,
Permissions.ResponseTypes.Code,
Permissions.Scopes.Email,
Permissions.Scopes.Profile,
Permissions.Scopes.Roles
}
});
}
Permissions.Endpoints.Authorization,
Permissions.Endpoints.Device,
Permissions.Endpoints.Token,
Permissions.GrantTypes.AuthorizationCode,
Permissions.GrantTypes.DeviceCode,
Permissions.GrantTypes.Password,
Permissions.GrantTypes.RefreshToken,
Permissions.ResponseTypes.Code,
Permissions.Scopes.Email,
Permissions.Scopes.Profile,
Permissions.Scopes.Roles
}
});
}
}
static async Task RegisterScopesAsync(IServiceProvider provider)
{
var manager = provider.GetRequiredService<IOpenIddictScopeManager>();
static async Task RegisterScopesAsync(IServiceProvider provider)
{
var manager = provider.GetRequiredService<IOpenIddictScopeManager>();
if (await manager.FindByNameAsync("demo_api") is null)
if (await manager.FindByNameAsync("demo_api") is null)
{
await manager.CreateAsync(new OpenIddictScopeDescriptor
{
await manager.CreateAsync(new OpenIddictScopeDescriptor
DisplayName = "Demo API access",
DisplayNames =
{
[CultureInfo.GetCultureInfo("fr-FR")] = "Accès à l'API de démo"
},
Name = "demo_api",
Resources =
{
DisplayName = "Demo API access",
DisplayNames =
{
[CultureInfo.GetCultureInfo("fr-FR")] = "Accès à l'API de démo"
},
Name = "demo_api",
Resources =
{
"resource_server"
}
});
}
"resource_server"
}
});
}
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}

101
shared/OpenIddict.Extensions/Helpers/OpenIddictHelpers.cs

@ -3,74 +3,73 @@ using System.Collections.Generic;
using System.Linq;
using SR = OpenIddict.Abstractions.OpenIddictResources;
namespace OpenIddict.Extensions
namespace OpenIddict.Extensions;
/// <summary>
/// Exposes common helpers used by the OpenIddict assemblies.
/// </summary>
internal static class OpenIddictHelpers
{
/// <summary>
/// Exposes common helpers used by the OpenIddict assemblies.
/// Finds the first base type that matches the specified generic type definition.
/// </summary>
internal static class OpenIddictHelpers
{
/// <summary>
/// Finds the first base type that matches the specified generic type definition.
/// </summary>
/// <param name="type">The type to introspect.</param>
/// <param name="definition">The generic type definition.</param>
/// <returns>A <see cref="Type"/> instance if the base type was found, <c>null</c> otherwise.</returns>
public static Type? FindGenericBaseType(Type type, Type definition)
=> FindGenericBaseTypes(type, definition).FirstOrDefault();
/// <param name="type">The type to introspect.</param>
/// <param name="definition">The generic type definition.</param>
/// <returns>A <see cref="Type"/> instance if the base type was found, <c>null</c> otherwise.</returns>
public static Type? FindGenericBaseType(Type type, Type definition)
=> FindGenericBaseTypes(type, definition).FirstOrDefault();
/// <summary>
/// Finds all the base types that matches the specified generic type definition.
/// </summary>
/// <param name="type">The type to introspect.</param>
/// <param name="definition">The generic type definition.</param>
/// <returns>A <see cref="Type"/> instance if the base type was found, <c>null</c> otherwise.</returns>
public static IEnumerable<Type> FindGenericBaseTypes(Type type, Type definition)
/// <summary>
/// Finds all the base types that matches the specified generic type definition.
/// </summary>
/// <param name="type">The type to introspect.</param>
/// <param name="definition">The generic type definition.</param>
/// <returns>A <see cref="Type"/> instance if the base type was found, <c>null</c> otherwise.</returns>
public static IEnumerable<Type> FindGenericBaseTypes(Type type, Type definition)
{
if (type is null)
{
if (type is null)
{
throw new ArgumentNullException(nameof(type));
}
throw new ArgumentNullException(nameof(type));
}
if (definition is null)
{
throw new ArgumentNullException(nameof(definition));
}
if (definition is null)
{
throw new ArgumentNullException(nameof(definition));
}
if (!definition.IsGenericTypeDefinition)
{
throw new ArgumentException(SR.GetResourceString(SR.ID0263), nameof(definition));
}
if (!definition.IsGenericTypeDefinition)
{
throw new ArgumentException(SR.GetResourceString(SR.ID0263), nameof(definition));
}
if (definition.IsInterface)
if (definition.IsInterface)
{
foreach (var contract in type.GetInterfaces())
{
foreach (var contract in type.GetInterfaces())
if (!contract.IsGenericType && !contract.IsConstructedGenericType)
{
if (!contract.IsGenericType && !contract.IsConstructedGenericType)
{
continue;
}
continue;
}
if (contract.GetGenericTypeDefinition() == definition)
{
yield return contract;
}
if (contract.GetGenericTypeDefinition() == definition)
{
yield return contract;
}
}
}
else
else
{
for (var candidate = type; candidate is not null; candidate = candidate.BaseType)
{
for (var candidate = type; candidate is not null; candidate = candidate.BaseType)
if (!candidate.IsGenericType && !candidate.IsConstructedGenericType)
{
if (!candidate.IsGenericType && !candidate.IsConstructedGenericType)
{
continue;
}
continue;
}
if (candidate.GetGenericTypeDefinition() == definition)
{
yield return candidate;
}
if (candidate.GetGenericTypeDefinition() == definition)
{
yield return candidate;
}
}
}

107
src/OpenIddict.Abstractions/Caches/IOpenIddictApplicationCache.cs

@ -8,66 +8,65 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace OpenIddict.Abstractions
namespace OpenIddict.Abstractions;
/// <summary>
/// Provides methods allowing to cache applications after retrieving them from the store.
/// </summary>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
public interface IOpenIddictApplicationCache<TApplication> where TApplication : class
{
/// <summary>
/// Provides methods allowing to cache applications after retrieving them from the store.
/// Add the specified application to the cache.
/// </summary>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
public interface IOpenIddictApplicationCache<TApplication> where TApplication : class
{
/// <summary>
/// Add the specified application to the cache.
/// </summary>
/// <param name="application">The application to add to the cache.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask AddAsync(TApplication application, CancellationToken cancellationToken);
/// <param name="application">The application to add to the cache.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask AddAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Retrieves an application using its client identifier.
/// </summary>
/// <param name="identifier">The client 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client application corresponding to the identifier.
/// </returns>
ValueTask<TApplication?> FindByClientIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves an application using its client identifier.
/// </summary>
/// <param name="identifier">The client 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client application corresponding to the identifier.
/// </returns>
ValueTask<TApplication?> FindByClientIdAsync(string identifier, 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client application corresponding to the identifier.
/// </returns>
ValueTask<TApplication?> FindByIdAsync(string identifier, 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client application corresponding to the identifier.
/// </returns>
ValueTask<TApplication?> FindByIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves all the applications associated with the specified redirect_uri.
/// </summary>
/// <param name="address">The redirect_uri associated with the applications.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The client applications corresponding to the specified redirect_uri.</returns>
IAsyncEnumerable<TApplication> FindByPostLogoutRedirectUriAsync(string address, CancellationToken cancellationToken);
/// <summary>
/// Retrieves all the applications associated with the specified redirect_uri.
/// </summary>
/// <param name="address">The redirect_uri associated with the applications.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The client applications corresponding to the specified redirect_uri.</returns>
IAsyncEnumerable<TApplication> FindByPostLogoutRedirectUriAsync(string address, CancellationToken cancellationToken);
/// <summary>
/// Retrieves all the applications associated with the specified redirect_uri.
/// </summary>
/// <param name="address">The redirect_uri associated with the applications.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The client applications corresponding to the specified redirect_uri.</returns>
IAsyncEnumerable<TApplication> FindByRedirectUriAsync(string address, CancellationToken cancellationToken);
/// <summary>
/// Retrieves all the applications associated with the specified redirect_uri.
/// </summary>
/// <param name="address">The redirect_uri associated with the applications.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The client applications corresponding to the specified redirect_uri.</returns>
IAsyncEnumerable<TApplication> FindByRedirectUriAsync(string address, CancellationToken cancellationToken);
/// <summary>
/// Removes the specified application from the cache.
/// </summary>
/// <param name="application">The application to remove from the cache.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask RemoveAsync(TApplication application, CancellationToken cancellationToken);
}
/// <summary>
/// Removes the specified application from the cache.
/// </summary>
/// <param name="application">The application to remove from the cache.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask RemoveAsync(TApplication application, CancellationToken cancellationToken);
}

173
src/OpenIddict.Abstractions/Caches/IOpenIddictAuthorizationCache.cs

@ -9,102 +9,101 @@ using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
namespace OpenIddict.Abstractions
namespace OpenIddict.Abstractions;
/// <summary>
/// Provides methods allowing to cache authorizations after retrieving them from the store.
/// </summary>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
public interface IOpenIddictAuthorizationCache<TAuthorization> where TAuthorization : class
{
/// <summary>
/// Provides methods allowing to cache authorizations after retrieving them from the store.
/// Add the specified authorization to the cache.
/// </summary>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
public interface IOpenIddictAuthorizationCache<TAuthorization> where TAuthorization : class
{
/// <summary>
/// Add the specified authorization to the cache.
/// </summary>
/// <param name="authorization">The authorization to add to the cache.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask AddAsync(TAuthorization authorization, CancellationToken cancellationToken);
/// <param name="authorization">The authorization to add to the cache.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask AddAsync(TAuthorization authorization, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the authorizations corresponding to the specified
/// subject and associated with the application identifier.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the subject/client.</returns>
IAsyncEnumerable<TAuthorization> FindAsync(string subject, string client, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the authorizations corresponding to the specified
/// subject and associated with the application identifier.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the subject/client.</returns>
IAsyncEnumerable<TAuthorization> FindAsync(string subject, string client, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the authorizations matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="status">The authorization status.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the criteria.</returns>
IAsyncEnumerable<TAuthorization> FindAsync(string subject, string client, string status, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the authorizations matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="status">The authorization status.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the criteria.</returns>
IAsyncEnumerable<TAuthorization> FindAsync(string subject, string client, string status, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the authorizations matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="status">The authorization status.</param>
/// <param name="type">The authorization type.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the criteria.</returns>
IAsyncEnumerable<TAuthorization> FindAsync(
string subject, string client, string status,
string type, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the authorizations matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="status">The authorization status.</param>
/// <param name="type">The authorization type.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the criteria.</returns>
IAsyncEnumerable<TAuthorization> FindAsync(
string subject, string client, string status,
string type, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the authorizations matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="status">The authorization status.</param>
/// <param name="type">The authorization type.</param>
/// <param name="scopes">The minimal scopes associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the criteria.</returns>
IAsyncEnumerable<TAuthorization> FindAsync(
string subject, string client, string status,
string type, ImmutableArray<string> scopes, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the authorizations matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="status">The authorization status.</param>
/// <param name="type">The authorization type.</param>
/// <param name="scopes">The minimal scopes associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the criteria.</returns>
IAsyncEnumerable<TAuthorization> FindAsync(
string subject, string client, string status,
string type, ImmutableArray<string> scopes, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the list of authorizations corresponding to the specified application identifier.
/// </summary>
/// <param name="identifier">The application identifier associated with the authorizations.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the specified application.</returns>
IAsyncEnumerable<TAuthorization> FindByApplicationIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the list of authorizations corresponding to the specified application identifier.
/// </summary>
/// <param name="identifier">The application identifier associated with the authorizations.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the specified application.</returns>
IAsyncEnumerable<TAuthorization> FindByApplicationIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves an authorization using its unique identifier.
/// </summary>
/// <param name="identifier">The unique identifier associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the authorization corresponding to the identifier.
/// </returns>
ValueTask<TAuthorization?> FindByIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves an authorization using its unique identifier.
/// </summary>
/// <param name="identifier">The unique identifier associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the authorization corresponding to the identifier.
/// </returns>
ValueTask<TAuthorization?> FindByIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves all the authorizations corresponding to the specified subject.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the specified subject.</returns>
IAsyncEnumerable<TAuthorization> FindBySubjectAsync(string subject, CancellationToken cancellationToken);
/// <summary>
/// Retrieves all the authorizations corresponding to the specified subject.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the specified subject.</returns>
IAsyncEnumerable<TAuthorization> FindBySubjectAsync(string subject, CancellationToken cancellationToken);
/// <summary>
/// Removes the specified authorization from the cache.
/// </summary>
/// <param name="authorization">The authorization to remove from the cache.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask RemoveAsync(TAuthorization authorization, CancellationToken cancellationToken);
}
/// <summary>
/// Removes the specified authorization from the cache.
/// </summary>
/// <param name="authorization">The authorization to remove from the cache.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask RemoveAsync(TAuthorization authorization, CancellationToken cancellationToken);
}

107
src/OpenIddict.Abstractions/Caches/IOpenIddictScopeCache.cs

@ -9,66 +9,65 @@ using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
namespace OpenIddict.Abstractions
namespace OpenIddict.Abstractions;
/// <summary>
/// Provides methods allowing to cache scopes after retrieving them from the store.
/// </summary>
/// <typeparam name="TScope">The type of the Scope entity.</typeparam>
public interface IOpenIddictScopeCache<TScope> where TScope : class
{
/// <summary>
/// Provides methods allowing to cache scopes after retrieving them from the store.
/// Add the specified scope to the cache.
/// </summary>
/// <typeparam name="TScope">The type of the Scope entity.</typeparam>
public interface IOpenIddictScopeCache<TScope> where TScope : class
{
/// <summary>
/// Add the specified scope to the cache.
/// </summary>
/// <param name="scope">The scope to add to the cache.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask AddAsync(TScope scope, CancellationToken cancellationToken);
/// <param name="scope">The scope to add to the cache.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask AddAsync(TScope scope, CancellationToken cancellationToken);
/// <summary>
/// Retrieves a scope using its unique identifier.
/// </summary>
/// <param name="identifier">The unique identifier associated with the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the scope corresponding to the identifier.
/// </returns>
ValueTask<TScope?> FindByIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves a scope using its unique identifier.
/// </summary>
/// <param name="identifier">The unique identifier associated with the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the scope corresponding to the identifier.
/// </returns>
ValueTask<TScope?> FindByIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves a scope using its name.
/// </summary>
/// <param name="name">The name associated with the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the scope corresponding to the specified name.
/// </returns>
ValueTask<TScope?> FindByNameAsync(string name, CancellationToken cancellationToken);
/// <summary>
/// Retrieves a scope using its name.
/// </summary>
/// <param name="name">The name associated with the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the scope corresponding to the specified name.
/// </returns>
ValueTask<TScope?> FindByNameAsync(string name, CancellationToken cancellationToken);
/// <summary>
/// Retrieves a list of scopes using their name.
/// </summary>
/// <param name="names">The names associated with the scopes.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The scopes corresponding to the specified names.</returns>
IAsyncEnumerable<TScope> FindByNamesAsync(ImmutableArray<string> names, CancellationToken cancellationToken);
/// <summary>
/// Retrieves a list of scopes using their name.
/// </summary>
/// <param name="names">The names associated with the scopes.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The scopes corresponding to the specified names.</returns>
IAsyncEnumerable<TScope> FindByNamesAsync(ImmutableArray<string> names, CancellationToken cancellationToken);
/// <summary>
/// Retrieves all the scopes that contain the specified resource.
/// </summary>
/// <param name="resource">The resource associated with the scopes.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The scopes associated with the specified resource.</returns>
IAsyncEnumerable<TScope> FindByResourceAsync(string resource, CancellationToken cancellationToken);
/// <summary>
/// Retrieves all the scopes that contain the specified resource.
/// </summary>
/// <param name="resource">The resource associated with the scopes.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The scopes associated with the specified resource.</returns>
IAsyncEnumerable<TScope> FindByResourceAsync(string resource, CancellationToken cancellationToken);
/// <summary>
/// Removes the specified scope from the cache.
/// </summary>
/// <param name="scope">The scope to remove from the cache.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask RemoveAsync(TScope scope, CancellationToken cancellationToken);
}
/// <summary>
/// Removes the specified scope from the cache.
/// </summary>
/// <param name="scope">The scope to remove from the cache.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask RemoveAsync(TScope scope, CancellationToken cancellationToken);
}

183
src/OpenIddict.Abstractions/Caches/IOpenIddictTokenCache.cs

@ -8,108 +8,107 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace OpenIddict.Abstractions
namespace OpenIddict.Abstractions;
/// <summary>
/// Provides methods allowing to cache tokens after retrieving them from the store.
/// </summary>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
public interface IOpenIddictTokenCache<TToken> where TToken : class
{
/// <summary>
/// Provides methods allowing to cache tokens after retrieving them from the store.
/// Add the specified token to the cache.
/// </summary>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
public interface IOpenIddictTokenCache<TToken> where TToken : class
{
/// <summary>
/// Add the specified token to the cache.
/// </summary>
/// <param name="token">The token to add to the cache.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask AddAsync(TToken token, CancellationToken cancellationToken);
/// <param name="token">The token to add to the cache.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask AddAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the tokens corresponding to the specified
/// subject and associated with the application identifier.
/// </summary>
/// <param name="subject">The subject associated with the token.</param>
/// <param name="client">The client associated with the token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the subject/client.</returns>
IAsyncEnumerable<TToken> FindAsync(string subject, string client, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the tokens corresponding to the specified
/// subject and associated with the application identifier.
/// </summary>
/// <param name="subject">The subject associated with the token.</param>
/// <param name="client">The client associated with the token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the subject/client.</returns>
IAsyncEnumerable<TToken> FindAsync(string subject, string client, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the tokens matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the token.</param>
/// <param name="client">The client associated with the token.</param>
/// <param name="status">The token status.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the criteria.</returns>
IAsyncEnumerable<TToken> FindAsync(string subject, string client, string status, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the tokens matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the token.</param>
/// <param name="client">The client associated with the token.</param>
/// <param name="status">The token status.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the criteria.</returns>
IAsyncEnumerable<TToken> FindAsync(string subject, string client, string status, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the tokens matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the token.</param>
/// <param name="client">The client associated with the token.</param>
/// <param name="status">The token status.</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>The tokens corresponding to the criteria.</returns>
IAsyncEnumerable<TToken> FindAsync(
string subject, string client,
string status, string type, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the tokens matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the token.</param>
/// <param name="client">The client associated with the token.</param>
/// <param name="status">The token status.</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>The tokens corresponding to the criteria.</returns>
IAsyncEnumerable<TToken> FindAsync(
string subject, string client,
string status, string type, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the list of tokens corresponding to the specified application identifier.
/// </summary>
/// <param name="identifier">The application identifier associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the specified application.</returns>
IAsyncEnumerable<TToken> FindByApplicationIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the list of tokens corresponding to the specified application identifier.
/// </summary>
/// <param name="identifier">The application identifier associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the specified application.</returns>
IAsyncEnumerable<TToken> FindByApplicationIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the list of tokens corresponding to the specified authorization identifier.
/// </summary>
/// <param name="identifier">The authorization identifier associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the specified authorization.</returns>
IAsyncEnumerable<TToken> FindByAuthorizationIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the list of tokens corresponding to the specified authorization identifier.
/// </summary>
/// <param name="identifier">The authorization identifier associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the specified authorization.</returns>
IAsyncEnumerable<TToken> FindByAuthorizationIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves a 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the token corresponding to the unique identifier.
/// </returns>
ValueTask<TToken?> FindByIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves a 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the token corresponding to the unique identifier.
/// </returns>
ValueTask<TToken?> FindByIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the list of tokens corresponding to the specified reference identifier.
/// Note: the reference identifier may be hashed or encrypted for security reasons.
/// </summary>
/// <param name="identifier">The reference identifier associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the tokens corresponding to the specified reference identifier.
/// </returns>
ValueTask<TToken?> FindByReferenceIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the list of tokens corresponding to the specified reference identifier.
/// Note: the reference identifier may be hashed or encrypted for security reasons.
/// </summary>
/// <param name="identifier">The reference identifier associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the tokens corresponding to the specified reference identifier.
/// </returns>
ValueTask<TToken?> FindByReferenceIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the list of tokens corresponding to the specified subject.
/// </summary>
/// <param name="subject">The subject associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the specified subject.</returns>
IAsyncEnumerable<TToken> FindBySubjectAsync(string subject, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the list of tokens corresponding to the specified subject.
/// </summary>
/// <param name="subject">The subject associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the specified subject.</returns>
IAsyncEnumerable<TToken> FindBySubjectAsync(string subject, CancellationToken cancellationToken);
/// <summary>
/// Removes the specified token from the cache.
/// </summary>
/// <param name="token">The token to remove from the cache.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask RemoveAsync(TToken token, CancellationToken cancellationToken);
}
/// <summary>
/// Removes the specified token from the cache.
/// </summary>
/// <param name="token">The token to remove from the cache.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask RemoveAsync(TToken token, CancellationToken cancellationToken);
}

101
src/OpenIddict.Abstractions/Descriptors/OpenIddictApplicationDescriptor.cs

@ -3,68 +3,67 @@ using System.Collections.Generic;
using System.Globalization;
using System.Text.Json;
namespace OpenIddict.Abstractions
namespace OpenIddict.Abstractions;
/// <summary>
/// Represents an OpenIddict application descriptor.
/// </summary>
public class OpenIddictApplicationDescriptor
{
/// <summary>
/// Represents an OpenIddict application descriptor.
/// Gets or sets the client identifier associated with the application.
/// </summary>
public class OpenIddictApplicationDescriptor
{
/// <summary>
/// Gets or sets the client identifier associated with the application.
/// </summary>
public string? ClientId { get; set; }
public string? ClientId { get; set; }
/// <summary>
/// Gets or sets the client secret associated with the application.
/// Note: depending on the application manager used when creating it,
/// this property may be hashed or encrypted for security reasons.
/// </summary>
public string? ClientSecret { get; set; }
/// <summary>
/// Gets or sets the client secret associated with the application.
/// Note: depending on the application manager used when creating it,
/// this property may be hashed or encrypted for security reasons.
/// </summary>
public string? ClientSecret { get; set; }
/// <summary>
/// Gets or sets the consent type associated with the application.
/// </summary>
public string? ConsentType { get; set; }
/// <summary>
/// Gets or sets the consent type associated with the application.
/// </summary>
public string? ConsentType { get; set; }
/// <summary>
/// Gets or sets the display name associated with the application.
/// </summary>
public string? DisplayName { get; set; }
/// <summary>
/// Gets or sets the display name associated with the application.
/// </summary>
public string? DisplayName { get; set; }
/// <summary>
/// Gets the localized display names associated with the application.
/// </summary>
public Dictionary<CultureInfo, string> DisplayNames { get; } = new();
/// <summary>
/// Gets the localized display names associated with the application.
/// </summary>
public Dictionary<CultureInfo, string> DisplayNames { get; } = new();
/// <summary>
/// Gets the permissions associated with the application.
/// </summary>
public HashSet<string> Permissions { get; } = new(StringComparer.Ordinal);
/// <summary>
/// Gets the permissions associated with the application.
/// </summary>
public HashSet<string> Permissions { get; } = new(StringComparer.Ordinal);
/// <summary>
/// Gets the logout callback URLs associated with the application.
/// </summary>
public HashSet<Uri> PostLogoutRedirectUris { get; } = new();
/// <summary>
/// Gets the logout callback URLs associated with the application.
/// </summary>
public HashSet<Uri> PostLogoutRedirectUris { get; } = new();
/// <summary>
/// Gets the additional properties associated with the application.
/// </summary>
public Dictionary<string, JsonElement> Properties { get; } = new(StringComparer.Ordinal);
/// <summary>
/// Gets the additional properties associated with the application.
/// </summary>
public Dictionary<string, JsonElement> Properties { get; } = new(StringComparer.Ordinal);
/// <summary>
/// Gets the callback URLs associated with the application.
/// </summary>
public HashSet<Uri> RedirectUris { get; } = new();
/// <summary>
/// Gets the callback URLs associated with the application.
/// </summary>
public HashSet<Uri> RedirectUris { get; } = new();
/// <summary>
/// Gets the requirements associated with the application.
/// </summary>
public HashSet<string> Requirements { get; } = new(StringComparer.Ordinal);
/// <summary>
/// Gets the requirements associated with the application.
/// </summary>
public HashSet<string> Requirements { get; } = new(StringComparer.Ordinal);
/// <summary>
/// Gets or sets the application type associated with the application.
/// </summary>
public string? Type { get; set; }
}
/// <summary>
/// Gets or sets the application type associated with the application.
/// </summary>
public string? Type { get; set; }
}

91
src/OpenIddict.Abstractions/Descriptors/OpenIddictAuthorizationDescriptor.cs

@ -3,52 +3,51 @@ using System.Collections.Generic;
using System.Security.Claims;
using System.Text.Json;
namespace OpenIddict.Abstractions
namespace OpenIddict.Abstractions;
/// <summary>
/// Represents an OpenIddict authorization descriptor.
/// </summary>
public class OpenIddictAuthorizationDescriptor
{
/// <summary>
/// Represents an OpenIddict authorization descriptor.
/// </summary>
public class OpenIddictAuthorizationDescriptor
{
/// <summary>
/// Gets or sets the application identifier associated with the authorization.
/// </summary>
public string? ApplicationId { get; set; }
/// <summary>
/// Gets or sets the creation date associated with the authorization.
/// </summary>
public DateTimeOffset? CreationDate { get; set; }
/// <summary>
/// Gets or sets the optional principal associated with the authorization.
/// Note: this property is not stored by the default authorization stores.
/// </summary>
public ClaimsPrincipal? Principal { get; set; }
/// <summary>
/// Gets the additional properties associated with the authorization.
/// </summary>
public Dictionary<string, JsonElement> Properties { get; } = new(StringComparer.Ordinal);
/// <summary>
/// Gets the scopes associated with the authorization.
/// </summary>
public HashSet<string> Scopes { get; } = new(StringComparer.Ordinal);
/// <summary>
/// Gets or sets the status associated with the authorization.
/// </summary>
public string? Status { get; set; }
/// <summary>
/// Gets or sets the subject associated with the authorization.
/// </summary>
public string? Subject { get; set; }
/// <summary>
/// Gets or sets the type of the authorization.
/// </summary>
public string? Type { get; set; }
}
/// Gets or sets the application identifier associated with the authorization.
/// </summary>
public string? ApplicationId { get; set; }
/// <summary>
/// Gets or sets the creation date associated with the authorization.
/// </summary>
public DateTimeOffset? CreationDate { get; set; }
/// <summary>
/// Gets or sets the optional principal associated with the authorization.
/// Note: this property is not stored by the default authorization stores.
/// </summary>
public ClaimsPrincipal? Principal { get; set; }
/// <summary>
/// Gets the additional properties associated with the authorization.
/// </summary>
public Dictionary<string, JsonElement> Properties { get; } = new(StringComparer.Ordinal);
/// <summary>
/// Gets the scopes associated with the authorization.
/// </summary>
public HashSet<string> Scopes { get; } = new(StringComparer.Ordinal);
/// <summary>
/// Gets or sets the status associated with the authorization.
/// </summary>
public string? Status { get; set; }
/// <summary>
/// Gets or sets the subject associated with the authorization.
/// </summary>
public string? Subject { get; set; }
/// <summary>
/// Gets or sets the type of the authorization.
/// </summary>
public string? Type { get; set; }
}

77
src/OpenIddict.Abstractions/Descriptors/OpenIddictScopeDescriptor.cs

@ -3,46 +3,45 @@ using System.Collections.Generic;
using System.Globalization;
using System.Text.Json;
namespace OpenIddict.Abstractions
namespace OpenIddict.Abstractions;
/// <summary>
/// Represents an OpenIddict scope descriptor.
/// </summary>
public class OpenIddictScopeDescriptor
{
/// <summary>
/// Represents an OpenIddict scope descriptor.
/// Gets or sets the description associated with the scope.
/// </summary>
public string? Description { get; set; }
/// <summary>
/// Gets the localized descriptions associated with the scope.
/// </summary>
public Dictionary<CultureInfo, string> Descriptions { get; } = new();
/// <summary>
/// Gets or sets the display name associated with the scope.
/// </summary>
public string? DisplayName { get; set; }
/// <summary>
/// Gets the localized display names associated with the scope.
/// </summary>
public Dictionary<CultureInfo, string> DisplayNames { get; } = new();
/// <summary>
/// Gets or sets the unique name associated with the scope.
/// </summary>
public string? Name { get; set; }
/// <summary>
/// Gets the additional properties associated with the scope.
/// </summary>
public Dictionary<string, JsonElement> Properties { get; } = new(StringComparer.Ordinal);
/// <summary>
/// Gets the resources associated with the scope.
/// </summary>
public class OpenIddictScopeDescriptor
{
/// <summary>
/// Gets or sets the description associated with the scope.
/// </summary>
public string? Description { get; set; }
/// <summary>
/// Gets the localized descriptions associated with the scope.
/// </summary>
public Dictionary<CultureInfo, string> Descriptions { get; } = new();
/// <summary>
/// Gets or sets the display name associated with the scope.
/// </summary>
public string? DisplayName { get; set; }
/// <summary>
/// Gets the localized display names associated with the scope.
/// </summary>
public Dictionary<CultureInfo, string> DisplayNames { get; } = new();
/// <summary>
/// Gets or sets the unique name associated with the scope.
/// </summary>
public string? Name { get; set; }
/// <summary>
/// Gets the additional properties associated with the scope.
/// </summary>
public Dictionary<string, JsonElement> Properties { get; } = new(StringComparer.Ordinal);
/// <summary>
/// Gets the resources associated with the scope.
/// </summary>
public HashSet<string> Resources { get; } = new(StringComparer.Ordinal);
}
public HashSet<string> Resources { get; } = new(StringComparer.Ordinal);
}

111
src/OpenIddict.Abstractions/Descriptors/OpenIddictTokenDescriptor.cs

@ -3,74 +3,73 @@ using System.Collections.Generic;
using System.Security.Claims;
using System.Text.Json;
namespace OpenIddict.Abstractions
namespace OpenIddict.Abstractions;
/// <summary>
/// Represents an OpenIddict token descriptor.
/// </summary>
public class OpenIddictTokenDescriptor
{
/// <summary>
/// Represents an OpenIddict token descriptor.
/// Gets or sets the application identifier associated with the token.
/// </summary>
public class OpenIddictTokenDescriptor
{
/// <summary>
/// Gets or sets the application identifier associated with the token.
/// </summary>
public string? ApplicationId { get; set; }
public string? ApplicationId { get; set; }
/// <summary>
/// Gets or sets the authorization identifier associated with the token.
/// </summary>
public string? AuthorizationId { get; set; }
/// <summary>
/// Gets or sets the authorization identifier associated with the token.
/// </summary>
public string? AuthorizationId { get; set; }
/// <summary>
/// Gets or sets the creation date associated with the token.
/// </summary>
public DateTimeOffset? CreationDate { get; set; }
/// <summary>
/// Gets or sets the creation date associated with the token.
/// </summary>
public DateTimeOffset? CreationDate { get; set; }
/// <summary>
/// Gets or sets the expiration date associated with the token.
/// </summary>
public DateTimeOffset? ExpirationDate { get; set; }
/// <summary>
/// Gets or sets the expiration date associated with the token.
/// </summary>
public DateTimeOffset? ExpirationDate { get; set; }
/// <summary>
/// Gets or sets the payload associated with the token.
/// </summary>
public string? Payload { get; set; }
/// <summary>
/// Gets or sets the payload associated with the token.
/// </summary>
public string? Payload { get; set; }
/// <summary>
/// Gets or sets the optional principal associated with the token.
/// Note: this property is not stored by the default token stores.
/// </summary>
public ClaimsPrincipal? Principal { get; set; }
/// <summary>
/// Gets or sets the optional principal associated with the token.
/// Note: this property is not stored by the default token stores.
/// </summary>
public ClaimsPrincipal? Principal { get; set; }
/// <summary>
/// Gets the additional properties associated with the token.
/// </summary>
public Dictionary<string, JsonElement> Properties { get; } = new(StringComparer.Ordinal);
/// <summary>
/// Gets the additional properties associated with the token.
/// </summary>
public Dictionary<string, JsonElement> Properties { get; } = new(StringComparer.Ordinal);
/// <summary>
/// Gets or sets the redemption date associated with the token.
/// </summary>
public DateTimeOffset? RedemptionDate { get; set; }
/// <summary>
/// Gets or sets the redemption date associated with the token.
/// </summary>
public DateTimeOffset? RedemptionDate { get; set; }
/// <summary>
/// Gets or sets the reference identifier associated with the token.
/// Note: depending on the application manager used when creating it,
/// this property may be hashed or encrypted for security reasons.
/// </summary>
public string? ReferenceId { get; set; }
/// <summary>
/// Gets or sets the reference identifier associated with the token.
/// Note: depending on the application manager used when creating it,
/// this property may be hashed or encrypted for security reasons.
/// </summary>
public string? ReferenceId { get; set; }
/// <summary>
/// Gets or sets the status associated with the token.
/// </summary>
public string? Status { get; set; }
/// <summary>
/// Gets or sets the status associated with the token.
/// </summary>
public string? Status { get; set; }
/// <summary>
/// Gets or sets the subject associated with the token.
/// </summary>
public string? Subject { get; set; }
/// <summary>
/// Gets or sets the subject associated with the token.
/// </summary>
public string? Subject { get; set; }
/// <summary>
/// Gets or sets the token type.
/// </summary>
public string? Type { get; set; }
}
/// <summary>
/// Gets or sets the token type.
/// </summary>
public string? Type { get; set; }
}

901
src/OpenIddict.Abstractions/Managers/IOpenIddictApplicationManager.cs

@ -14,457 +14,456 @@ using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
namespace OpenIddict.Abstractions
namespace OpenIddict.Abstractions;
/// <summary>
/// Provides methods allowing to manage the applications stored in the store.
/// Note: this interface is not meant to be implemented by custom managers,
/// that should inherit from the generic OpenIddictApplicationManager class.
/// It is primarily intended to be used by services that cannot easily depend
/// on the generic application manager. The actual application entity type
/// is automatically determined at runtime based on the OpenIddict core options.
/// </summary>
public interface IOpenIddictApplicationManager
{
/// <summary>
/// Provides methods allowing to manage the applications stored in the store.
/// Note: this interface is not meant to be implemented by custom managers,
/// that should inherit from the generic OpenIddictApplicationManager class.
/// It is primarily intended to be used by services that cannot easily depend
/// on the generic application manager. The actual application entity type
/// is automatically determined at runtime based on the OpenIddict core options.
/// </summary>
public interface IOpenIddictApplicationManager
{
/// <summary>
/// Determines the number of applications that exist in the database.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of applications in the database.
/// </returns>
ValueTask<long> CountAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Determines the number of applications that match the specified query.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of applications that match the specified query.
/// </returns>
ValueTask<long> CountAsync<TResult>(Func<IQueryable<object>, IQueryable<TResult>> query, CancellationToken cancellationToken = default);
/// <summary>
/// Creates a new application based on the specified descriptor.
/// Note: the default implementation automatically hashes the client
/// secret before storing it in the database, for security reasons.
/// </summary>
/// <param name="descriptor">The application descriptor.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the application.
/// </returns>
ValueTask<object> CreateAsync(OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken = default);
/// <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="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask CreateAsync(object application, CancellationToken cancellationToken = default);
/// <summary>
/// Creates a new application.
/// Note: the default implementation automatically hashes the client
/// secret before storing it in the database, for security reasons.
/// </summary>
/// <param name="application">The application to create.</param>
/// <param name="secret">The client secret associated with the application, if applicable.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask CreateAsync(object application, string? secret, CancellationToken cancellationToken = default);
/// <summary>
/// Removes an existing application.
/// </summary>
/// <param name="application">The application to delete.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask DeleteAsync(object application, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves an application using its client identifier.
/// </summary>
/// <param name="identifier">The client 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="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client application corresponding to the identifier.
/// </returns>
ValueTask<object?> FindByClientIdAsync(string identifier, CancellationToken cancellationToken = default);
/// <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="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client application corresponding to the identifier.
/// </returns>
ValueTask<object?> FindByIdAsync(string identifier, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves all the applications associated with the specified post_logout_redirect_uri.
/// </summary>
/// <param name="address">The post_logout_redirect_uri associated with the applications.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The client applications corresponding to the specified post_logout_redirect_uri.</returns>
IAsyncEnumerable<object> FindByPostLogoutRedirectUriAsync(string address, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves all the applications associated with the specified redirect_uri.
/// </summary>
/// <param name="address">The redirect_uri associated with the applications.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The client applications corresponding to the specified redirect_uri.</returns>
IAsyncEnumerable<object> FindByRedirectUriAsync(string address, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the first element returned when executing the query.
/// </returns>
ValueTask<TResult?> GetAsync<TResult>(
Func<IQueryable<object>, IQueryable<TResult>> query, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the first element returned when executing the query.
/// </returns>
ValueTask<TResult?> GetAsync<TState, TResult>(
Func<IQueryable<object>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the client identifier 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client identifier associated with the application.
/// </returns>
ValueTask<string?> GetClientIdAsync(object application, CancellationToken cancellationToken = default);
/// <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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client type of the application (by default, "public").
/// </returns>
ValueTask<string?> GetClientTypeAsync(object application, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the consent 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the consent type of the application (by default, "explicit").
/// </returns>
ValueTask<string?> GetConsentTypeAsync(object application, CancellationToken cancellationToken = default);
/// <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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the display name associated with the application.
/// </returns>
ValueTask<string?> GetDisplayNameAsync(object application, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the localized display names 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the localized display names associated with the application.
/// </returns>
ValueTask<ImmutableDictionary<CultureInfo, string>> GetDisplayNamesAsync(object application, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the unique identifier 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the application.
/// </returns>
ValueTask<string?> GetIdAsync(object application, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the localized display name associated with an application
/// and corresponding to the current UI culture or one of its parents.
/// If no matching value can be found, the non-localized value is returned.
/// </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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the matching localized display name associated with the application.
/// </returns>
ValueTask<string?> GetLocalizedDisplayNameAsync(object application, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the localized display name associated with an application
/// and corresponding to the specified culture or one of its parents.
/// If no matching value can be found, the non-localized value is returned.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="culture">The culture (typically <see cref="CultureInfo.CurrentUICulture"/>).</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the matching localized display name associated with the application.
/// </returns>
ValueTask<string?> GetLocalizedDisplayNameAsync(object application, CultureInfo culture, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the permissions 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the permissions associated with the application.
/// </returns>
ValueTask<ImmutableArray<string>> GetPermissionsAsync(object application, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the logout callback addresses 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the post_logout_redirect_uri associated with the application.
/// </returns>
ValueTask<ImmutableArray<string>> GetPostLogoutRedirectUrisAsync(object application, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the additional properties 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the application.
/// </returns>
ValueTask<ImmutableDictionary<string, JsonElement>> GetPropertiesAsync(object application, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the callback addresses 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the redirect_uri associated with the application.
/// </returns>
ValueTask<ImmutableArray<string>> GetRedirectUrisAsync(object application, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the requirements 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the requirements associated with the application.
/// </returns>
ValueTask<ImmutableArray<string>> GetRequirementsAsync(object application, CancellationToken cancellationToken = default);
/// <summary>
/// Determines whether a given application has the specified client type.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="type">The expected client type.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the application has the specified client type, <c>false</c> otherwise.</returns>
ValueTask<bool> HasClientTypeAsync(object application, string type, CancellationToken cancellationToken = default);
/// <summary>
/// Determines whether a given application has the specified consent type.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="type">The expected consent type.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the application has the specified consent type, <c>false</c> otherwise.</returns>
ValueTask<bool> HasConsentTypeAsync(object application, string type, CancellationToken cancellationToken = default);
/// <summary>
/// Determines whether the specified permission has been granted to the application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="permission">The permission.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the application has been granted the specified permission, <c>false</c> otherwise.</returns>
ValueTask<bool> HasPermissionAsync(object application, string permission, CancellationToken cancellationToken = default);
/// <summary>
/// Determines whether the specified requirement has been enforced for the specified application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="requirement">The requirement.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the requirement has been enforced for the specified application, <c>false</c> otherwise.</returns>
ValueTask<bool> HasRequirementAsync(object application, string requirement, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <param name="count">The number of results to return.</param>
/// <param name="offset">The number of results to skip.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<object> ListAsync(
int? count = null, int? offset = null, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TResult> ListAsync<TResult>(
Func<IQueryable<object>, IQueryable<TResult>> query, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TResult> ListAsync<TState, TResult>(
Func<IQueryable<object>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken = default);
/// <summary>
/// Populates the specified descriptor using the properties exposed by the application.
/// </summary>
/// <param name="descriptor">The descriptor.</param>
/// <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="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask PopulateAsync(OpenIddictApplicationDescriptor descriptor, object application, CancellationToken cancellationToken = default);
/// <summary>
/// Populates the application using the specified descriptor.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="descriptor">The descriptor.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask PopulateAsync(object application, OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken = default);
/// <summary>
/// Updates an existing application.
/// </summary>
/// <param name="application">The application to update.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask UpdateAsync(object application, CancellationToken cancellationToken = default);
/// <summary>
/// Updates an existing application.
/// </summary>
/// <param name="application">The application to update.</param>
/// <param name="descriptor">The descriptor used to update the application.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask UpdateAsync(object application, OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken = default);
/// <summary>
/// Updates an existing application and replaces the existing secret.
/// Note: the default implementation automatically hashes the client
/// secret before storing it in the database, for security reasons.
/// </summary>
/// <param name="application">The application to update.</param>
/// <param name="secret">The client secret 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="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask UpdateAsync(object application, string secret, CancellationToken cancellationToken = default);
/// <summary>
/// Validates the application to ensure it's in a consistent state.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The validation error encountered when validating the application.</returns>
IAsyncEnumerable<ValidationResult> ValidateAsync(object application, CancellationToken cancellationToken = default);
/// <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>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns a boolean indicating whether the client secret was valid.
/// </returns>
ValueTask<bool> ValidateClientSecretAsync(object application, string secret, CancellationToken cancellationToken = default);
/// <summary>
/// Validates the redirect_uri to ensure it's associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="address">The address that should be compared to one of the redirect_uri stored in the database.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns a boolean indicating whether the redirect_uri was valid.
/// </returns>
ValueTask<bool> ValidateRedirectUriAsync(object application, string address, CancellationToken cancellationToken = default);
}
/// Determines the number of applications that exist in the database.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of applications in the database.
/// </returns>
ValueTask<long> CountAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Determines the number of applications that match the specified query.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of applications that match the specified query.
/// </returns>
ValueTask<long> CountAsync<TResult>(Func<IQueryable<object>, IQueryable<TResult>> query, CancellationToken cancellationToken = default);
/// <summary>
/// Creates a new application based on the specified descriptor.
/// Note: the default implementation automatically hashes the client
/// secret before storing it in the database, for security reasons.
/// </summary>
/// <param name="descriptor">The application descriptor.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the application.
/// </returns>
ValueTask<object> CreateAsync(OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken = default);
/// <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="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask CreateAsync(object application, CancellationToken cancellationToken = default);
/// <summary>
/// Creates a new application.
/// Note: the default implementation automatically hashes the client
/// secret before storing it in the database, for security reasons.
/// </summary>
/// <param name="application">The application to create.</param>
/// <param name="secret">The client secret associated with the application, if applicable.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask CreateAsync(object application, string? secret, CancellationToken cancellationToken = default);
/// <summary>
/// Removes an existing application.
/// </summary>
/// <param name="application">The application to delete.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask DeleteAsync(object application, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves an application using its client identifier.
/// </summary>
/// <param name="identifier">The client 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="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client application corresponding to the identifier.
/// </returns>
ValueTask<object?> FindByClientIdAsync(string identifier, CancellationToken cancellationToken = default);
/// <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="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client application corresponding to the identifier.
/// </returns>
ValueTask<object?> FindByIdAsync(string identifier, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves all the applications associated with the specified post_logout_redirect_uri.
/// </summary>
/// <param name="address">The post_logout_redirect_uri associated with the applications.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The client applications corresponding to the specified post_logout_redirect_uri.</returns>
IAsyncEnumerable<object> FindByPostLogoutRedirectUriAsync(string address, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves all the applications associated with the specified redirect_uri.
/// </summary>
/// <param name="address">The redirect_uri associated with the applications.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The client applications corresponding to the specified redirect_uri.</returns>
IAsyncEnumerable<object> FindByRedirectUriAsync(string address, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the first element returned when executing the query.
/// </returns>
ValueTask<TResult?> GetAsync<TResult>(
Func<IQueryable<object>, IQueryable<TResult>> query, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the first element returned when executing the query.
/// </returns>
ValueTask<TResult?> GetAsync<TState, TResult>(
Func<IQueryable<object>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the client identifier 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client identifier associated with the application.
/// </returns>
ValueTask<string?> GetClientIdAsync(object application, CancellationToken cancellationToken = default);
/// <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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client type of the application (by default, "public").
/// </returns>
ValueTask<string?> GetClientTypeAsync(object application, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the consent 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the consent type of the application (by default, "explicit").
/// </returns>
ValueTask<string?> GetConsentTypeAsync(object application, CancellationToken cancellationToken = default);
/// <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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the display name associated with the application.
/// </returns>
ValueTask<string?> GetDisplayNameAsync(object application, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the localized display names 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the localized display names associated with the application.
/// </returns>
ValueTask<ImmutableDictionary<CultureInfo, string>> GetDisplayNamesAsync(object application, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the unique identifier 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the application.
/// </returns>
ValueTask<string?> GetIdAsync(object application, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the localized display name associated with an application
/// and corresponding to the current UI culture or one of its parents.
/// If no matching value can be found, the non-localized value is returned.
/// </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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the matching localized display name associated with the application.
/// </returns>
ValueTask<string?> GetLocalizedDisplayNameAsync(object application, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the localized display name associated with an application
/// and corresponding to the specified culture or one of its parents.
/// If no matching value can be found, the non-localized value is returned.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="culture">The culture (typically <see cref="CultureInfo.CurrentUICulture"/>).</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the matching localized display name associated with the application.
/// </returns>
ValueTask<string?> GetLocalizedDisplayNameAsync(object application, CultureInfo culture, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the permissions 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the permissions associated with the application.
/// </returns>
ValueTask<ImmutableArray<string>> GetPermissionsAsync(object application, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the logout callback addresses 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the post_logout_redirect_uri associated with the application.
/// </returns>
ValueTask<ImmutableArray<string>> GetPostLogoutRedirectUrisAsync(object application, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the additional properties 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the application.
/// </returns>
ValueTask<ImmutableDictionary<string, JsonElement>> GetPropertiesAsync(object application, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the callback addresses 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the redirect_uri associated with the application.
/// </returns>
ValueTask<ImmutableArray<string>> GetRedirectUrisAsync(object application, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the requirements 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the requirements associated with the application.
/// </returns>
ValueTask<ImmutableArray<string>> GetRequirementsAsync(object application, CancellationToken cancellationToken = default);
/// <summary>
/// Determines whether a given application has the specified client type.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="type">The expected client type.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the application has the specified client type, <c>false</c> otherwise.</returns>
ValueTask<bool> HasClientTypeAsync(object application, string type, CancellationToken cancellationToken = default);
/// <summary>
/// Determines whether a given application has the specified consent type.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="type">The expected consent type.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the application has the specified consent type, <c>false</c> otherwise.</returns>
ValueTask<bool> HasConsentTypeAsync(object application, string type, CancellationToken cancellationToken = default);
/// <summary>
/// Determines whether the specified permission has been granted to the application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="permission">The permission.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the application has been granted the specified permission, <c>false</c> otherwise.</returns>
ValueTask<bool> HasPermissionAsync(object application, string permission, CancellationToken cancellationToken = default);
/// <summary>
/// Determines whether the specified requirement has been enforced for the specified application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="requirement">The requirement.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the requirement has been enforced for the specified application, <c>false</c> otherwise.</returns>
ValueTask<bool> HasRequirementAsync(object application, string requirement, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <param name="count">The number of results to return.</param>
/// <param name="offset">The number of results to skip.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<object> ListAsync(
int? count = null, int? offset = null, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TResult> ListAsync<TResult>(
Func<IQueryable<object>, IQueryable<TResult>> query, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TResult> ListAsync<TState, TResult>(
Func<IQueryable<object>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken = default);
/// <summary>
/// Populates the specified descriptor using the properties exposed by the application.
/// </summary>
/// <param name="descriptor">The descriptor.</param>
/// <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="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask PopulateAsync(OpenIddictApplicationDescriptor descriptor, object application, CancellationToken cancellationToken = default);
/// <summary>
/// Populates the application using the specified descriptor.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="descriptor">The descriptor.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask PopulateAsync(object application, OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken = default);
/// <summary>
/// Updates an existing application.
/// </summary>
/// <param name="application">The application to update.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask UpdateAsync(object application, CancellationToken cancellationToken = default);
/// <summary>
/// Updates an existing application.
/// </summary>
/// <param name="application">The application to update.</param>
/// <param name="descriptor">The descriptor used to update the application.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask UpdateAsync(object application, OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken = default);
/// <summary>
/// Updates an existing application and replaces the existing secret.
/// Note: the default implementation automatically hashes the client
/// secret before storing it in the database, for security reasons.
/// </summary>
/// <param name="application">The application to update.</param>
/// <param name="secret">The client secret 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="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask UpdateAsync(object application, string secret, CancellationToken cancellationToken = default);
/// <summary>
/// Validates the application to ensure it's in a consistent state.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The validation error encountered when validating the application.</returns>
IAsyncEnumerable<ValidationResult> ValidateAsync(object application, CancellationToken cancellationToken = default);
/// <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>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns a boolean indicating whether the client secret was valid.
/// </returns>
ValueTask<bool> ValidateClientSecretAsync(object application, string secret, CancellationToken cancellationToken = default);
/// <summary>
/// Validates the redirect_uri to ensure it's associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="address">The address that should be compared to one of the redirect_uri stored in the database.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns a boolean indicating whether the redirect_uri was valid.
/// </returns>
ValueTask<bool> ValidateRedirectUriAsync(object application, string address, CancellationToken cancellationToken = default);
}

811
src/OpenIddict.Abstractions/Managers/IOpenIddictAuthorizationManager.cs

@ -14,412 +14,411 @@ using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
namespace OpenIddict.Abstractions
namespace OpenIddict.Abstractions;
/// <summary>
/// Provides methods allowing to manage the authorizations stored in the store.
/// Note: this interface is not meant to be implemented by custom managers,
/// that should inherit from the generic OpenIddictAuthorizationManager class.
/// It is primarily intended to be used by services that cannot easily depend
/// on the generic authorization manager. The actual authorization entity type
/// is automatically determined at runtime based on the OpenIddict core options.
/// </summary>
public interface IOpenIddictAuthorizationManager
{
/// <summary>
/// Provides methods allowing to manage the authorizations stored in the store.
/// Note: this interface is not meant to be implemented by custom managers,
/// that should inherit from the generic OpenIddictAuthorizationManager class.
/// It is primarily intended to be used by services that cannot easily depend
/// on the generic authorization manager. The actual authorization entity type
/// is automatically determined at runtime based on the OpenIddict core options.
/// </summary>
public interface IOpenIddictAuthorizationManager
{
/// <summary>
/// Determines the number of authorizations that exist in the database.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of authorizations in the database.
/// </returns>
ValueTask<long> CountAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Determines the number of authorizations that match the specified query.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of authorizations that match the specified query.
/// </returns>
ValueTask<long> CountAsync<TResult>(
Func<IQueryable<object>, IQueryable<TResult>> query, CancellationToken cancellationToken = default);
/// <summary>
/// Creates a new permanent authorization based on the specified parameters.
/// </summary>
/// <param name="principal">The principal associated with the authorization.</param>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="type">The authorization type.</param>
/// <param name="scopes">The minimal scopes associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation, whose result returns the authorization.
/// </returns>
ValueTask<object> CreateAsync(
ClaimsPrincipal principal, string subject, string client,
string type, ImmutableArray<string> scopes, CancellationToken cancellationToken = default);
/// <summary>
/// Creates a new authorization based on the specified descriptor.
/// </summary>
/// <param name="descriptor">The authorization descriptor.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation, whose result returns the authorization.
/// </returns>
ValueTask<object> CreateAsync(OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken = default);
/// <summary>
/// Creates a new authorization.
/// </summary>
/// <param name="authorization">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="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask CreateAsync(object authorization, CancellationToken cancellationToken = default);
/// <summary>
/// Removes an existing authorization.
/// </summary>
/// <param name="authorization">The authorization to delete.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask DeleteAsync(object authorization, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the authorizations corresponding to the specified
/// subject and associated with the application identifier.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the subject/client.</returns>
IAsyncEnumerable<object> FindAsync(string subject, string client, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the authorizations matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="status">The authorization status.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the criteria.</returns>
IAsyncEnumerable<object> FindAsync(
string subject, string client,
string status, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the authorizations matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="status">The authorization status.</param>
/// <param name="type">The authorization type.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the criteria.</returns>
IAsyncEnumerable<object> FindAsync(
string subject, string client,
string status, string type, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the authorizations matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="status">The authorization status.</param>
/// <param name="type">The authorization type.</param>
/// <param name="scopes">The minimal scopes associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the criteria.</returns>
IAsyncEnumerable<object> FindAsync(
string subject, string client, string status,
string type, ImmutableArray<string> scopes, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the list of authorizations corresponding to the specified application identifier.
/// </summary>
/// <param name="identifier">The application identifier associated with the authorizations.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the specified application.</returns>
IAsyncEnumerable<object> FindByApplicationIdAsync(string identifier, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves an authorization using its unique identifier.
/// </summary>
/// <param name="identifier">The unique identifier associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the authorization corresponding to the identifier.
/// </returns>
ValueTask<object?> FindByIdAsync(string identifier, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves all the authorizations corresponding to the specified subject.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the specified subject.</returns>
IAsyncEnumerable<object> FindBySubjectAsync(string subject, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the optional application identifier associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the application identifier associated with the authorization.
/// </returns>
ValueTask<string?> GetApplicationIdAsync(object authorization, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the first element returned when executing the query.
/// </returns>
ValueTask<TResult?> GetAsync<TResult>(
Func<IQueryable<object>, IQueryable<TResult>> query, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the first element returned when executing the query.
/// </returns>
ValueTask<TResult?> GetAsync<TState, TResult>(
Func<IQueryable<object>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the creation date associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the creation date associated with the specified authorization.
/// </returns>
ValueTask<DateTimeOffset?> GetCreationDateAsync(object authorization, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the unique identifier associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the authorization.
/// </returns>
ValueTask<string?> GetIdAsync(object authorization, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the additional properties associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the authorization.
/// </returns>
ValueTask<ImmutableDictionary<string, JsonElement>> GetPropertiesAsync(object authorization, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the scopes associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the scopes associated with the specified authorization.
/// </returns>
ValueTask<ImmutableArray<string>> GetScopesAsync(object authorization, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the status associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the status associated with the specified authorization.
/// </returns>
ValueTask<string?> GetStatusAsync(object authorization, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the subject associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the subject associated with the specified authorization.
/// </returns>
ValueTask<string?> GetSubjectAsync(object authorization, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the type associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the type associated with the specified authorization.
/// </returns>
ValueTask<string?> GetTypeAsync(object authorization, CancellationToken cancellationToken = default);
/// <summary>
/// Determines whether the specified scopes are included in the authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="scopes">The scopes.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the scopes are included in the authorization, <c>false</c> otherwise.</returns>
ValueTask<bool> HasScopesAsync(object authorization, ImmutableArray<string> scopes, CancellationToken cancellationToken = default);
/// <summary>
/// Determines whether a given authorization has the specified status.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="status">The expected status.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the authorization has the specified status, <c>false</c> otherwise.</returns>
ValueTask<bool> HasStatusAsync(object authorization, string status, CancellationToken cancellationToken = default);
/// <summary>
/// Determines whether a given authorization has the specified type.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="type">The expected type.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the authorization has the specified type, <c>false</c> otherwise.</returns>
ValueTask<bool> HasTypeAsync(object authorization, string type, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <param name="count">The number of results to return.</param>
/// <param name="offset">The number of results to skip.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<object> ListAsync(
int? count = null, int? offset = null, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TResult> ListAsync<TResult>(
Func<IQueryable<object>, IQueryable<TResult>> query, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TResult> ListAsync<TState, TResult>(
Func<IQueryable<object>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken = default);
/// <summary>
/// Populates the specified descriptor using the properties exposed by the authorization.
/// </summary>
/// <param name="descriptor">The descriptor.</param>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask PopulateAsync(OpenIddictAuthorizationDescriptor descriptor, object authorization, CancellationToken cancellationToken = default);
/// <summary>
/// Populates the authorization using the specified descriptor.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="descriptor">The descriptor.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask PopulateAsync(object authorization, OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken = default);
/// <summary>
/// Removes the authorizations that are marked as invalid and the ad-hoc ones that have no token attached.
/// Only authorizations created before the specified <paramref name="threshold"/> are removed.
/// </summary>
/// <remarks>
/// To ensure ad-hoc authorizations that no longer have any valid/non-expired token
/// attached are correctly removed, the tokens should always be pruned first.
/// </remarks>
/// <param name="threshold">The date before which authorizations are not pruned.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask PruneAsync(DateTimeOffset threshold, CancellationToken cancellationToken = default);
/// <summary>
/// Tries to revoke an authorization.
/// </summary>
/// <param name="authorization">The authorization to revoke.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the authorization was successfully revoked, <c>false</c> otherwise.</returns>
ValueTask<bool> TryRevokeAsync(object authorization, CancellationToken cancellationToken = default);
/// <summary>
/// Updates an existing authorization.
/// </summary>
/// <param name="authorization">The authorization to update.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask UpdateAsync(object authorization, CancellationToken cancellationToken = default);
/// <summary>
/// Updates an existing authorization.
/// </summary>
/// <param name="authorization">The authorization to update.</param>
/// <param name="descriptor">The descriptor used to update the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask UpdateAsync(object authorization, OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken = default);
/// <summary>
/// Validates the authorization to ensure it's in a consistent state.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The validation error encountered when validating the authorization.</returns>
IAsyncEnumerable<ValidationResult> ValidateAsync(object authorization, CancellationToken cancellationToken = default);
}
/// Determines the number of authorizations that exist in the database.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of authorizations in the database.
/// </returns>
ValueTask<long> CountAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Determines the number of authorizations that match the specified query.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of authorizations that match the specified query.
/// </returns>
ValueTask<long> CountAsync<TResult>(
Func<IQueryable<object>, IQueryable<TResult>> query, CancellationToken cancellationToken = default);
/// <summary>
/// Creates a new permanent authorization based on the specified parameters.
/// </summary>
/// <param name="principal">The principal associated with the authorization.</param>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="type">The authorization type.</param>
/// <param name="scopes">The minimal scopes associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation, whose result returns the authorization.
/// </returns>
ValueTask<object> CreateAsync(
ClaimsPrincipal principal, string subject, string client,
string type, ImmutableArray<string> scopes, CancellationToken cancellationToken = default);
/// <summary>
/// Creates a new authorization based on the specified descriptor.
/// </summary>
/// <param name="descriptor">The authorization descriptor.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation, whose result returns the authorization.
/// </returns>
ValueTask<object> CreateAsync(OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken = default);
/// <summary>
/// Creates a new authorization.
/// </summary>
/// <param name="authorization">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="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask CreateAsync(object authorization, CancellationToken cancellationToken = default);
/// <summary>
/// Removes an existing authorization.
/// </summary>
/// <param name="authorization">The authorization to delete.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask DeleteAsync(object authorization, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the authorizations corresponding to the specified
/// subject and associated with the application identifier.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the subject/client.</returns>
IAsyncEnumerable<object> FindAsync(string subject, string client, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the authorizations matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="status">The authorization status.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the criteria.</returns>
IAsyncEnumerable<object> FindAsync(
string subject, string client,
string status, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the authorizations matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="status">The authorization status.</param>
/// <param name="type">The authorization type.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the criteria.</returns>
IAsyncEnumerable<object> FindAsync(
string subject, string client,
string status, string type, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the authorizations matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="status">The authorization status.</param>
/// <param name="type">The authorization type.</param>
/// <param name="scopes">The minimal scopes associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the criteria.</returns>
IAsyncEnumerable<object> FindAsync(
string subject, string client, string status,
string type, ImmutableArray<string> scopes, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the list of authorizations corresponding to the specified application identifier.
/// </summary>
/// <param name="identifier">The application identifier associated with the authorizations.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the specified application.</returns>
IAsyncEnumerable<object> FindByApplicationIdAsync(string identifier, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves an authorization using its unique identifier.
/// </summary>
/// <param name="identifier">The unique identifier associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the authorization corresponding to the identifier.
/// </returns>
ValueTask<object?> FindByIdAsync(string identifier, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves all the authorizations corresponding to the specified subject.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the specified subject.</returns>
IAsyncEnumerable<object> FindBySubjectAsync(string subject, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the optional application identifier associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the application identifier associated with the authorization.
/// </returns>
ValueTask<string?> GetApplicationIdAsync(object authorization, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the first element returned when executing the query.
/// </returns>
ValueTask<TResult?> GetAsync<TResult>(
Func<IQueryable<object>, IQueryable<TResult>> query, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the first element returned when executing the query.
/// </returns>
ValueTask<TResult?> GetAsync<TState, TResult>(
Func<IQueryable<object>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the creation date associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the creation date associated with the specified authorization.
/// </returns>
ValueTask<DateTimeOffset?> GetCreationDateAsync(object authorization, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the unique identifier associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the authorization.
/// </returns>
ValueTask<string?> GetIdAsync(object authorization, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the additional properties associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the authorization.
/// </returns>
ValueTask<ImmutableDictionary<string, JsonElement>> GetPropertiesAsync(object authorization, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the scopes associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the scopes associated with the specified authorization.
/// </returns>
ValueTask<ImmutableArray<string>> GetScopesAsync(object authorization, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the status associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the status associated with the specified authorization.
/// </returns>
ValueTask<string?> GetStatusAsync(object authorization, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the subject associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the subject associated with the specified authorization.
/// </returns>
ValueTask<string?> GetSubjectAsync(object authorization, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the type associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the type associated with the specified authorization.
/// </returns>
ValueTask<string?> GetTypeAsync(object authorization, CancellationToken cancellationToken = default);
/// <summary>
/// Determines whether the specified scopes are included in the authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="scopes">The scopes.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the scopes are included in the authorization, <c>false</c> otherwise.</returns>
ValueTask<bool> HasScopesAsync(object authorization, ImmutableArray<string> scopes, CancellationToken cancellationToken = default);
/// <summary>
/// Determines whether a given authorization has the specified status.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="status">The expected status.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the authorization has the specified status, <c>false</c> otherwise.</returns>
ValueTask<bool> HasStatusAsync(object authorization, string status, CancellationToken cancellationToken = default);
/// <summary>
/// Determines whether a given authorization has the specified type.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="type">The expected type.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the authorization has the specified type, <c>false</c> otherwise.</returns>
ValueTask<bool> HasTypeAsync(object authorization, string type, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <param name="count">The number of results to return.</param>
/// <param name="offset">The number of results to skip.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<object> ListAsync(
int? count = null, int? offset = null, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TResult> ListAsync<TResult>(
Func<IQueryable<object>, IQueryable<TResult>> query, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TResult> ListAsync<TState, TResult>(
Func<IQueryable<object>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken = default);
/// <summary>
/// Populates the specified descriptor using the properties exposed by the authorization.
/// </summary>
/// <param name="descriptor">The descriptor.</param>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask PopulateAsync(OpenIddictAuthorizationDescriptor descriptor, object authorization, CancellationToken cancellationToken = default);
/// <summary>
/// Populates the authorization using the specified descriptor.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="descriptor">The descriptor.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask PopulateAsync(object authorization, OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken = default);
/// <summary>
/// Removes the authorizations that are marked as invalid and the ad-hoc ones that have no token attached.
/// Only authorizations created before the specified <paramref name="threshold"/> are removed.
/// </summary>
/// <remarks>
/// To ensure ad-hoc authorizations that no longer have any valid/non-expired token
/// attached are correctly removed, the tokens should always be pruned first.
/// </remarks>
/// <param name="threshold">The date before which authorizations are not pruned.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask PruneAsync(DateTimeOffset threshold, CancellationToken cancellationToken = default);
/// <summary>
/// Tries to revoke an authorization.
/// </summary>
/// <param name="authorization">The authorization to revoke.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the authorization was successfully revoked, <c>false</c> otherwise.</returns>
ValueTask<bool> TryRevokeAsync(object authorization, CancellationToken cancellationToken = default);
/// <summary>
/// Updates an existing authorization.
/// </summary>
/// <param name="authorization">The authorization to update.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask UpdateAsync(object authorization, CancellationToken cancellationToken = default);
/// <summary>
/// Updates an existing authorization.
/// </summary>
/// <param name="authorization">The authorization to update.</param>
/// <param name="descriptor">The descriptor used to update the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask UpdateAsync(object authorization, OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken = default);
/// <summary>
/// Validates the authorization to ensure it's in a consistent state.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The validation error encountered when validating the authorization.</returns>
IAsyncEnumerable<ValidationResult> ValidateAsync(object authorization, CancellationToken cancellationToken = default);
}

725
src/OpenIddict.Abstractions/Managers/IOpenIddictScopeManager.cs

@ -14,369 +14,368 @@ using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
namespace OpenIddict.Abstractions
namespace OpenIddict.Abstractions;
/// <summary>
/// Provides methods allowing to manage the scopes stored in the store.
/// Note: this interface is not meant to be implemented by custom managers,
/// that should inherit from the generic OpenIddictScopeManager class.
/// It is primarily intended to be used by services that cannot easily
/// depend on the generic scope manager. The actual scope entity type is
/// automatically determined at runtime based on the OpenIddict core options.
/// </summary>
public interface IOpenIddictScopeManager
{
/// <summary>
/// Provides methods allowing to manage the scopes stored in the store.
/// Note: this interface is not meant to be implemented by custom managers,
/// that should inherit from the generic OpenIddictScopeManager class.
/// It is primarily intended to be used by services that cannot easily
/// depend on the generic scope manager. The actual scope entity type is
/// automatically determined at runtime based on the OpenIddict core options.
/// </summary>
public interface IOpenIddictScopeManager
{
/// <summary>
/// Determines the number of scopes that exist in the database.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of scopes in the database.
/// </returns>
ValueTask<long> CountAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Determines the number of scopes that match the specified query.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of scopes that match the specified query.
/// </returns>
ValueTask<long> CountAsync<TResult>(Func<IQueryable<object>, IQueryable<TResult>> query, CancellationToken cancellationToken = default);
/// <summary>
/// Creates a new scope based on the specified descriptor.
/// </summary>
/// <param name="descriptor">The scope descriptor.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation, whose result returns the scope.
/// </returns>
ValueTask<object> CreateAsync(OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken = default);
/// <summary>
/// Creates a new scope.
/// </summary>
/// <param name="scope">The scope to create.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask CreateAsync(object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Removes an existing scope.
/// </summary>
/// <param name="scope">The scope to delete.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask DeleteAsync(object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves a scope using its unique identifier.
/// </summary>
/// <param name="identifier">The unique identifier associated with the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the scope corresponding to the identifier.
/// </returns>
ValueTask<object?> FindByIdAsync(string identifier, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves a scope using its name.
/// </summary>
/// <param name="name">The name associated with the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the scope corresponding to the specified name.
/// </returns>
ValueTask<object?> FindByNameAsync(string name, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves a list of scopes using their name.
/// </summary>
/// <param name="names">The names associated with the scopes.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The scopes corresponding to the specified names.</returns>
IAsyncEnumerable<object> FindByNamesAsync(ImmutableArray<string> names, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves all the scopes that contain the specified resource.
/// </summary>
/// <param name="resource">The resource associated with the scopes.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The scopes associated with the specified resource.</returns>
IAsyncEnumerable<object> FindByResourceAsync(string resource, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the first element returned when executing the query.
/// </returns>
ValueTask<TResult?> GetAsync<TResult>(
Func<IQueryable<object>, IQueryable<TResult>> query, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the first element returned when executing the query.
/// </returns>
ValueTask<TResult?> GetAsync<TState, TResult>(
Func<IQueryable<object>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the description associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the description associated with the specified scope.
/// </returns>
ValueTask<string?> GetDescriptionAsync(object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the localized descriptions associated with an scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the localized descriptions associated with the scope.
/// </returns>
ValueTask<ImmutableDictionary<CultureInfo, string>> GetDescriptionsAsync(object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the display name associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the display name associated with the scope.
/// </returns>
ValueTask<string?> GetDisplayNameAsync(object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the localized display names associated with an scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the localized display names associated with the scope.
/// </returns>
ValueTask<ImmutableDictionary<CultureInfo, string>> GetDisplayNamesAsync(object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the unique identifier associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the scope.
/// </returns>
ValueTask<string?> GetIdAsync(object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the localized description associated with an scope
/// and corresponding to the current UI culture or one of its parents.
/// If no matching value can be found, the non-localized value is returned.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the matching localized description associated with the scope.
/// </returns>
ValueTask<string?> GetLocalizedDescriptionAsync(object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the localized description associated with an scope
/// and corresponding to the specified culture or one of its parents.
/// If no matching value can be found, the non-localized value is returned.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="culture">The culture (typically <see cref="CultureInfo.CurrentUICulture"/>).</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the matching localized description associated with the scope.
/// </returns>
ValueTask<string?> GetLocalizedDescriptionAsync(object scope, CultureInfo culture, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the localized display name associated with an scope
/// and corresponding to the current UI culture or one of its parents.
/// If no matching value can be found, the non-localized value is returned.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the display name associated with the scope.
/// </returns>
ValueTask<string?> GetLocalizedDisplayNameAsync(object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the localized display name associated with an scope
/// and corresponding to the specified culture or one of its parents.
/// If no matching value can be found, the non-localized value is returned.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="culture">The culture (typically <see cref="CultureInfo.CurrentUICulture"/>).</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the display name associated with the scope.
/// </returns>
ValueTask<string?> GetLocalizedDisplayNameAsync(object scope, CultureInfo culture, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the name associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the name associated with the specified scope.
/// </returns>
ValueTask<string?> GetNameAsync(object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the additional properties associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the scope.
/// </returns>
ValueTask<ImmutableDictionary<string, JsonElement>> GetPropertiesAsync(object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the resources associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the resources associated with the scope.
/// </returns>
ValueTask<ImmutableArray<string>> GetResourcesAsync(object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <param name="count">The number of results to return.</param>
/// <param name="offset">The number of results to skip.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<object> ListAsync(
int? count = null, int? offset = null, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TResult> ListAsync<TResult>(
Func<IQueryable<object>, IQueryable<TResult>> query, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TResult> ListAsync<TState, TResult>(
Func<IQueryable<object>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken = default);
/// <summary>
/// Lists all the resources associated with the specified scopes.
/// </summary>
/// <param name="scopes">The scopes.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the resources associated with the specified scopes.</returns>
IAsyncEnumerable<string> ListResourcesAsync(ImmutableArray<string> scopes, CancellationToken cancellationToken = default);
/// <summary>
/// Populates the specified descriptor using the properties exposed by the scope.
/// </summary>
/// <param name="descriptor">The descriptor.</param>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask PopulateAsync(OpenIddictScopeDescriptor descriptor, object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Populates the scope using the specified descriptor.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="descriptor">The descriptor.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask PopulateAsync(object scope, OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken = default);
/// <summary>
/// Updates an existing scope.
/// </summary>
/// <param name="scope">The scope to update.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask UpdateAsync(object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Updates an existing scope.
/// </summary>
/// <param name="scope">The scope to update.</param>
/// <param name="descriptor">The descriptor used to update the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask UpdateAsync(object scope, OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken = default);
/// <summary>
/// Validates the scope to ensure it's in a consistent state.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The validation error encountered when validating the scope.</returns>
IAsyncEnumerable<ValidationResult> ValidateAsync(object scope, CancellationToken cancellationToken = default);
}
/// Determines the number of scopes that exist in the database.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of scopes in the database.
/// </returns>
ValueTask<long> CountAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Determines the number of scopes that match the specified query.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of scopes that match the specified query.
/// </returns>
ValueTask<long> CountAsync<TResult>(Func<IQueryable<object>, IQueryable<TResult>> query, CancellationToken cancellationToken = default);
/// <summary>
/// Creates a new scope based on the specified descriptor.
/// </summary>
/// <param name="descriptor">The scope descriptor.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation, whose result returns the scope.
/// </returns>
ValueTask<object> CreateAsync(OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken = default);
/// <summary>
/// Creates a new scope.
/// </summary>
/// <param name="scope">The scope to create.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask CreateAsync(object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Removes an existing scope.
/// </summary>
/// <param name="scope">The scope to delete.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask DeleteAsync(object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves a scope using its unique identifier.
/// </summary>
/// <param name="identifier">The unique identifier associated with the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the scope corresponding to the identifier.
/// </returns>
ValueTask<object?> FindByIdAsync(string identifier, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves a scope using its name.
/// </summary>
/// <param name="name">The name associated with the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the scope corresponding to the specified name.
/// </returns>
ValueTask<object?> FindByNameAsync(string name, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves a list of scopes using their name.
/// </summary>
/// <param name="names">The names associated with the scopes.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The scopes corresponding to the specified names.</returns>
IAsyncEnumerable<object> FindByNamesAsync(ImmutableArray<string> names, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves all the scopes that contain the specified resource.
/// </summary>
/// <param name="resource">The resource associated with the scopes.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The scopes associated with the specified resource.</returns>
IAsyncEnumerable<object> FindByResourceAsync(string resource, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the first element returned when executing the query.
/// </returns>
ValueTask<TResult?> GetAsync<TResult>(
Func<IQueryable<object>, IQueryable<TResult>> query, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the first element returned when executing the query.
/// </returns>
ValueTask<TResult?> GetAsync<TState, TResult>(
Func<IQueryable<object>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the description associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the description associated with the specified scope.
/// </returns>
ValueTask<string?> GetDescriptionAsync(object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the localized descriptions associated with an scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the localized descriptions associated with the scope.
/// </returns>
ValueTask<ImmutableDictionary<CultureInfo, string>> GetDescriptionsAsync(object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the display name associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the display name associated with the scope.
/// </returns>
ValueTask<string?> GetDisplayNameAsync(object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the localized display names associated with an scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the localized display names associated with the scope.
/// </returns>
ValueTask<ImmutableDictionary<CultureInfo, string>> GetDisplayNamesAsync(object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the unique identifier associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the scope.
/// </returns>
ValueTask<string?> GetIdAsync(object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the localized description associated with an scope
/// and corresponding to the current UI culture or one of its parents.
/// If no matching value can be found, the non-localized value is returned.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the matching localized description associated with the scope.
/// </returns>
ValueTask<string?> GetLocalizedDescriptionAsync(object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the localized description associated with an scope
/// and corresponding to the specified culture or one of its parents.
/// If no matching value can be found, the non-localized value is returned.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="culture">The culture (typically <see cref="CultureInfo.CurrentUICulture"/>).</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the matching localized description associated with the scope.
/// </returns>
ValueTask<string?> GetLocalizedDescriptionAsync(object scope, CultureInfo culture, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the localized display name associated with an scope
/// and corresponding to the current UI culture or one of its parents.
/// If no matching value can be found, the non-localized value is returned.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the display name associated with the scope.
/// </returns>
ValueTask<string?> GetLocalizedDisplayNameAsync(object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the localized display name associated with an scope
/// and corresponding to the specified culture or one of its parents.
/// If no matching value can be found, the non-localized value is returned.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="culture">The culture (typically <see cref="CultureInfo.CurrentUICulture"/>).</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the display name associated with the scope.
/// </returns>
ValueTask<string?> GetLocalizedDisplayNameAsync(object scope, CultureInfo culture, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the name associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the name associated with the specified scope.
/// </returns>
ValueTask<string?> GetNameAsync(object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the additional properties associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the scope.
/// </returns>
ValueTask<ImmutableDictionary<string, JsonElement>> GetPropertiesAsync(object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the resources associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the resources associated with the scope.
/// </returns>
ValueTask<ImmutableArray<string>> GetResourcesAsync(object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <param name="count">The number of results to return.</param>
/// <param name="offset">The number of results to skip.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<object> ListAsync(
int? count = null, int? offset = null, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TResult> ListAsync<TResult>(
Func<IQueryable<object>, IQueryable<TResult>> query, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TResult> ListAsync<TState, TResult>(
Func<IQueryable<object>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken = default);
/// <summary>
/// Lists all the resources associated with the specified scopes.
/// </summary>
/// <param name="scopes">The scopes.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the resources associated with the specified scopes.</returns>
IAsyncEnumerable<string> ListResourcesAsync(ImmutableArray<string> scopes, CancellationToken cancellationToken = default);
/// <summary>
/// Populates the specified descriptor using the properties exposed by the scope.
/// </summary>
/// <param name="descriptor">The descriptor.</param>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask PopulateAsync(OpenIddictScopeDescriptor descriptor, object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Populates the scope using the specified descriptor.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="descriptor">The descriptor.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask PopulateAsync(object scope, OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken = default);
/// <summary>
/// Updates an existing scope.
/// </summary>
/// <param name="scope">The scope to update.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask UpdateAsync(object scope, CancellationToken cancellationToken = default);
/// <summary>
/// Updates an existing scope.
/// </summary>
/// <param name="scope">The scope to update.</param>
/// <param name="descriptor">The descriptor used to update the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask UpdateAsync(object scope, OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken = default);
/// <summary>
/// Validates the scope to ensure it's in a consistent state.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The validation error encountered when validating the scope.</returns>
IAsyncEnumerable<ValidationResult> ValidateAsync(object scope, CancellationToken cancellationToken = default);
}

907
src/OpenIddict.Abstractions/Managers/IOpenIddictTokenManager.cs

@ -13,460 +13,459 @@ using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
namespace OpenIddict.Abstractions
namespace OpenIddict.Abstractions;
/// <summary>
/// Provides methods allowing to manage the tokens stored in the store.
/// Note: this interface is not meant to be implemented by custom managers,
/// that should inherit from the generic OpenIddictTokenManager class.
/// It is primarily intended to be used by services that cannot easily
/// depend on the generic token manager. The actual token entity type is
/// automatically determined at runtime based on the OpenIddict core options.
/// </summary>
public interface IOpenIddictTokenManager
{
/// <summary>
/// Provides methods allowing to manage the tokens stored in the store.
/// Note: this interface is not meant to be implemented by custom managers,
/// that should inherit from the generic OpenIddictTokenManager class.
/// It is primarily intended to be used by services that cannot easily
/// depend on the generic token manager. The actual token entity type is
/// automatically determined at runtime based on the OpenIddict core options.
/// </summary>
public interface IOpenIddictTokenManager
{
/// <summary>
/// Determines the number of tokens that exist in the database.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of tokens in the database.
/// </returns>
ValueTask<long> CountAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Determines the number of tokens that match the specified query.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of tokens that match the specified query.
/// </returns>
ValueTask<long> CountAsync<TResult>(Func<IQueryable<object>, IQueryable<TResult>> query, CancellationToken cancellationToken = default);
/// <summary>
/// Creates a new token based on the specified descriptor.
/// </summary>
/// <param name="descriptor">The token descriptor.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation, whose result returns the token.
/// </returns>
ValueTask<object> CreateAsync(OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken = default);
/// <summary>
/// Creates a new token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask CreateAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Removes an existing token.
/// </summary>
/// <param name="token">The token to delete.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask DeleteAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the tokens corresponding to the specified
/// subject and associated with the application identifier.
/// </summary>
/// <param name="subject">The subject associated with the token.</param>
/// <param name="client">The client associated with the token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the subject/client.</returns>
IAsyncEnumerable<object> FindAsync(string subject,
string client, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the tokens matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the token.</param>
/// <param name="client">The client associated with the token.</param>
/// <param name="status">The token status.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the criteria.</returns>
IAsyncEnumerable<object> FindAsync(
string subject, string client,
string status, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the tokens matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the token.</param>
/// <param name="client">The client associated with the token.</param>
/// <param name="status">The token status.</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>The tokens corresponding to the criteria.</returns>
IAsyncEnumerable<object> FindAsync(
string subject, string client,
string status, string type, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the list of tokens corresponding to the specified application identifier.
/// </summary>
/// <param name="identifier">The application identifier associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the specified application.</returns>
IAsyncEnumerable<object> FindByApplicationIdAsync(string identifier, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the list of tokens corresponding to the specified authorization identifier.
/// </summary>
/// <param name="identifier">The authorization identifier associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the specified authorization.</returns>
IAsyncEnumerable<object> FindByAuthorizationIdAsync(string identifier, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves a 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="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the token corresponding to the unique identifier.
/// </returns>
ValueTask<object?> FindByIdAsync(string identifier, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the list of tokens corresponding to the specified reference identifier.
/// Note: the reference identifier may be hashed or encrypted for security reasons.
/// </summary>
/// <param name="identifier">The reference identifier associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the tokens corresponding to the specified reference identifier.
/// </returns>
ValueTask<object?> FindByReferenceIdAsync(string identifier, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the list of tokens corresponding to the specified subject.
/// </summary>
/// <param name="subject">The subject associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the specified subject.</returns>
IAsyncEnumerable<object> FindBySubjectAsync(string subject, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the optional application identifier associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the application identifier associated with the token.
/// </returns>
ValueTask<string?> GetApplicationIdAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the first element returned when executing the query.
/// </returns>
ValueTask<TResult?> GetAsync<TResult>(
Func<IQueryable<object>, IQueryable<TResult>> query, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the first element returned when executing the query.
/// </returns>
ValueTask<TResult?> GetAsync<TState, TResult>(
Func<IQueryable<object>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the optional authorization identifier associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the authorization identifier associated with the token.
/// </returns>
ValueTask<string?> GetAuthorizationIdAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the creation date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the creation date associated with the specified token.
/// </returns>
ValueTask<DateTimeOffset?> GetCreationDateAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the expiration date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the expiration date associated with the specified token.
/// </returns>
ValueTask<DateTimeOffset?> GetExpirationDateAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the unique identifier associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the token.
/// </returns>
ValueTask<string?> GetIdAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the payload associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the payload associated with the specified token.
/// </returns>
ValueTask<string?> GetPayloadAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the additional properties associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the token.
/// </returns>
ValueTask<ImmutableDictionary<string, JsonElement>> GetPropertiesAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the redemption date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the redemption date associated with the specified token.
/// </returns>
ValueTask<DateTimeOffset?> GetRedemptionDateAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the reference identifier associated with a token.
/// Note: depending on the manager used to create the token,
/// the reference identifier may be hashed for security reasons.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the reference identifier associated with the specified token.
/// </returns>
ValueTask<string?> GetReferenceIdAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the status associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the status associated with the specified token.
/// </returns>
ValueTask<string?> GetStatusAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the subject associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the subject associated with the specified token.
/// </returns>
ValueTask<string?> GetSubjectAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the token type associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the token type associated with the specified token.
/// </returns>
ValueTask<string?> GetTypeAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Determines whether a given token has the specified status.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="status">The expected status.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the token has the specified status, <c>false</c> otherwise.</returns>
ValueTask<bool> HasStatusAsync(object token, string status, CancellationToken cancellationToken = default);
/// <summary>
/// Determines whether a given token has the specified type.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="type">The expected type.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the token has the specified type, <c>false</c> otherwise.</returns>
ValueTask<bool> HasTypeAsync(object token, string type, CancellationToken cancellationToken = default);
/// <summary>
/// Determines whether a given token has any of the specified types.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="types">The expected types.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the token has any of the specified types, <c>false</c> otherwise.</returns>
ValueTask<bool> HasTypeAsync(object token, ImmutableArray<string> types, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <param name="count">The number of results to return.</param>
/// <param name="offset">The number of results to skip.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<object> ListAsync(
int? count = null, int? offset = null, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TResult> ListAsync<TResult>(
Func<IQueryable<object>, IQueryable<TResult>> query, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TResult> ListAsync<TState, TResult>(
Func<IQueryable<object>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken = default);
/// <summary>
/// Populates the specified descriptor using the properties exposed by the token.
/// </summary>
/// <param name="descriptor">The descriptor.</param>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask PopulateAsync(OpenIddictTokenDescriptor descriptor, object token, CancellationToken cancellationToken = default);
/// <summary>
/// Populates the token using the specified descriptor.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="descriptor">The descriptor.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask PopulateAsync(object token, OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken = default);
/// <summary>
/// Removes the tokens that are marked as invalid or whose attached authorization is no longer valid.
/// Only tokens created before the specified <paramref name="threshold"/> are removed.
/// </summary>
/// <param name="threshold">The date before which tokens are not pruned.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask PruneAsync(DateTimeOffset threshold, CancellationToken cancellationToken = default);
/// <summary>
/// Tries to redeem a token.
/// </summary>
/// <param name="token">The token to redeem.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the token was successfully redemeed, <c>false</c> otherwise.</returns>
ValueTask<bool> TryRedeemAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Tries to reject a token.
/// </summary>
/// <param name="token">The token to reject.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the token was successfully redemeed, <c>false</c> otherwise.</returns>
ValueTask<bool> TryRejectAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Tries to revoke 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><c>true</c> if the token was successfully revoked, <c>false</c> otherwise.</returns>
ValueTask<bool> TryRevokeAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Updates an existing token.
/// </summary>
/// <param name="token">The token to update.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask UpdateAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Updates an existing token.
/// </summary>
/// <param name="token">The token to update.</param>
/// <param name="descriptor">The descriptor used to update the token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask UpdateAsync(object token, OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken = default);
/// <summary>
/// Validates the token to ensure it's in a consistent state.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The validation error encountered when validating the token.</returns>
IAsyncEnumerable<ValidationResult> ValidateAsync(object token, CancellationToken cancellationToken = default);
}
/// Determines the number of tokens that exist in the database.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of tokens in the database.
/// </returns>
ValueTask<long> CountAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Determines the number of tokens that match the specified query.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of tokens that match the specified query.
/// </returns>
ValueTask<long> CountAsync<TResult>(Func<IQueryable<object>, IQueryable<TResult>> query, CancellationToken cancellationToken = default);
/// <summary>
/// Creates a new token based on the specified descriptor.
/// </summary>
/// <param name="descriptor">The token descriptor.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation, whose result returns the token.
/// </returns>
ValueTask<object> CreateAsync(OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken = default);
/// <summary>
/// Creates a new token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask CreateAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Removes an existing token.
/// </summary>
/// <param name="token">The token to delete.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask DeleteAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the tokens corresponding to the specified
/// subject and associated with the application identifier.
/// </summary>
/// <param name="subject">The subject associated with the token.</param>
/// <param name="client">The client associated with the token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the subject/client.</returns>
IAsyncEnumerable<object> FindAsync(string subject,
string client, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the tokens matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the token.</param>
/// <param name="client">The client associated with the token.</param>
/// <param name="status">The token status.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the criteria.</returns>
IAsyncEnumerable<object> FindAsync(
string subject, string client,
string status, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the tokens matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the token.</param>
/// <param name="client">The client associated with the token.</param>
/// <param name="status">The token status.</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>The tokens corresponding to the criteria.</returns>
IAsyncEnumerable<object> FindAsync(
string subject, string client,
string status, string type, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the list of tokens corresponding to the specified application identifier.
/// </summary>
/// <param name="identifier">The application identifier associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the specified application.</returns>
IAsyncEnumerable<object> FindByApplicationIdAsync(string identifier, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the list of tokens corresponding to the specified authorization identifier.
/// </summary>
/// <param name="identifier">The authorization identifier associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the specified authorization.</returns>
IAsyncEnumerable<object> FindByAuthorizationIdAsync(string identifier, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves a 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="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the token corresponding to the unique identifier.
/// </returns>
ValueTask<object?> FindByIdAsync(string identifier, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the list of tokens corresponding to the specified reference identifier.
/// Note: the reference identifier may be hashed or encrypted for security reasons.
/// </summary>
/// <param name="identifier">The reference identifier associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the tokens corresponding to the specified reference identifier.
/// </returns>
ValueTask<object?> FindByReferenceIdAsync(string identifier, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the list of tokens corresponding to the specified subject.
/// </summary>
/// <param name="subject">The subject associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the specified subject.</returns>
IAsyncEnumerable<object> FindBySubjectAsync(string subject, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the optional application identifier associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the application identifier associated with the token.
/// </returns>
ValueTask<string?> GetApplicationIdAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the first element returned when executing the query.
/// </returns>
ValueTask<TResult?> GetAsync<TResult>(
Func<IQueryable<object>, IQueryable<TResult>> query, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the first element returned when executing the query.
/// </returns>
ValueTask<TResult?> GetAsync<TState, TResult>(
Func<IQueryable<object>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the optional authorization identifier associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the authorization identifier associated with the token.
/// </returns>
ValueTask<string?> GetAuthorizationIdAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the creation date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the creation date associated with the specified token.
/// </returns>
ValueTask<DateTimeOffset?> GetCreationDateAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the expiration date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the expiration date associated with the specified token.
/// </returns>
ValueTask<DateTimeOffset?> GetExpirationDateAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the unique identifier associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the token.
/// </returns>
ValueTask<string?> GetIdAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the payload associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the payload associated with the specified token.
/// </returns>
ValueTask<string?> GetPayloadAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the additional properties associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the token.
/// </returns>
ValueTask<ImmutableDictionary<string, JsonElement>> GetPropertiesAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the redemption date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the redemption date associated with the specified token.
/// </returns>
ValueTask<DateTimeOffset?> GetRedemptionDateAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the reference identifier associated with a token.
/// Note: depending on the manager used to create the token,
/// the reference identifier may be hashed for security reasons.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the reference identifier associated with the specified token.
/// </returns>
ValueTask<string?> GetReferenceIdAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the status associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the status associated with the specified token.
/// </returns>
ValueTask<string?> GetStatusAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the subject associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the subject associated with the specified token.
/// </returns>
ValueTask<string?> GetSubjectAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves the token type associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the token type associated with the specified token.
/// </returns>
ValueTask<string?> GetTypeAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Determines whether a given token has the specified status.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="status">The expected status.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the token has the specified status, <c>false</c> otherwise.</returns>
ValueTask<bool> HasStatusAsync(object token, string status, CancellationToken cancellationToken = default);
/// <summary>
/// Determines whether a given token has the specified type.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="type">The expected type.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the token has the specified type, <c>false</c> otherwise.</returns>
ValueTask<bool> HasTypeAsync(object token, string type, CancellationToken cancellationToken = default);
/// <summary>
/// Determines whether a given token has any of the specified types.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="types">The expected types.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the token has any of the specified types, <c>false</c> otherwise.</returns>
ValueTask<bool> HasTypeAsync(object token, ImmutableArray<string> types, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <param name="count">The number of results to return.</param>
/// <param name="offset">The number of results to skip.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<object> ListAsync(
int? count = null, int? offset = null, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TResult> ListAsync<TResult>(
Func<IQueryable<object>, IQueryable<TResult>> query, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TResult> ListAsync<TState, TResult>(
Func<IQueryable<object>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken = default);
/// <summary>
/// Populates the specified descriptor using the properties exposed by the token.
/// </summary>
/// <param name="descriptor">The descriptor.</param>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask PopulateAsync(OpenIddictTokenDescriptor descriptor, object token, CancellationToken cancellationToken = default);
/// <summary>
/// Populates the token using the specified descriptor.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="descriptor">The descriptor.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask PopulateAsync(object token, OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken = default);
/// <summary>
/// Removes the tokens that are marked as invalid or whose attached authorization is no longer valid.
/// Only tokens created before the specified <paramref name="threshold"/> are removed.
/// </summary>
/// <param name="threshold">The date before which tokens are not pruned.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask PruneAsync(DateTimeOffset threshold, CancellationToken cancellationToken = default);
/// <summary>
/// Tries to redeem a token.
/// </summary>
/// <param name="token">The token to redeem.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the token was successfully redemeed, <c>false</c> otherwise.</returns>
ValueTask<bool> TryRedeemAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Tries to reject a token.
/// </summary>
/// <param name="token">The token to reject.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the token was successfully redemeed, <c>false</c> otherwise.</returns>
ValueTask<bool> TryRejectAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Tries to revoke 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><c>true</c> if the token was successfully revoked, <c>false</c> otherwise.</returns>
ValueTask<bool> TryRevokeAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Updates an existing token.
/// </summary>
/// <param name="token">The token to update.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask UpdateAsync(object token, CancellationToken cancellationToken = default);
/// <summary>
/// Updates an existing token.
/// </summary>
/// <param name="token">The token to update.</param>
/// <param name="descriptor">The descriptor used to update the token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
ValueTask UpdateAsync(object token, OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken = default);
/// <summary>
/// Validates the token to ensure it's in a consistent state.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The validation error encountered when validating the token.</returns>
IAsyncEnumerable<ValidationResult> ValidateAsync(object token, CancellationToken cancellationToken = default);
}

49
src/OpenIddict.Abstractions/OpenIddictBuilder.cs

@ -7,36 +7,35 @@
using System;
using System.ComponentModel;
namespace Microsoft.Extensions.DependencyInjection
namespace Microsoft.Extensions.DependencyInjection;
/// <summary>
/// Provides a shared entry point allowing to configure the OpenIddict services.
/// </summary>
public class OpenIddictBuilder
{
/// <summary>
/// Provides a shared entry point allowing to configure the OpenIddict services.
/// Initializes a new instance of <see cref="OpenIddictBuilder"/>.
/// </summary>
public class OpenIddictBuilder
{
/// <summary>
/// Initializes a new instance of <see cref="OpenIddictBuilder"/>.
/// </summary>
/// <param name="services">The services collection.</param>
public OpenIddictBuilder(IServiceCollection services)
=> Services = services ?? throw new ArgumentNullException(nameof(services));
/// <param name="services">The services collection.</param>
public OpenIddictBuilder(IServiceCollection services)
=> Services = services ?? throw new ArgumentNullException(nameof(services));
/// <summary>
/// Gets the services collection.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public IServiceCollection Services { get; }
/// <summary>
/// Gets the services collection.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public IServiceCollection Services { get; }
/// <inheritdoc/>
[EditorBrowsable(EditorBrowsableState.Never)]
public override bool Equals(object? obj) => base.Equals(obj);
/// <inheritdoc/>
[EditorBrowsable(EditorBrowsableState.Never)]
public override bool Equals(object? obj) => base.Equals(obj);
/// <inheritdoc/>
[EditorBrowsable(EditorBrowsableState.Never)]
public override int GetHashCode() => base.GetHashCode();
/// <inheritdoc/>
[EditorBrowsable(EditorBrowsableState.Never)]
public override int GetHashCode() => base.GetHashCode();
/// <inheritdoc/>
[EditorBrowsable(EditorBrowsableState.Never)]
public override string? ToString() => base.ToString();
}
/// <inheritdoc/>
[EditorBrowsable(EditorBrowsableState.Never)]
public override string? ToString() => base.ToString();
}

841
src/OpenIddict.Abstractions/OpenIddictConstants.cs

@ -4,477 +4,476 @@
* the license and the contributors participating to this project.
*/
namespace OpenIddict.Abstractions
namespace OpenIddict.Abstractions;
public static class OpenIddictConstants
{
public static class OpenIddictConstants
public static class Algorithms
{
public static class Algorithms
{
public const string EcdsaSha256 = "ES256";
public const string EcdsaSha384 = "ES384";
public const string EcdsaSha512 = "ES512";
public const string HmacSha256 = "HS256";
public const string HmacSha384 = "HS384";
public const string HmacSha512 = "HS512";
public const string RsaSha256 = "RS256";
public const string RsaSha384 = "RS384";
public const string RsaSha512 = "RS512";
public const string RsaSsaPssSha256 = "PS256";
public const string RsaSsaPssSha384 = "PS384";
public const string RsaSsaPssSha512 = "PS512";
}
public const string EcdsaSha256 = "ES256";
public const string EcdsaSha384 = "ES384";
public const string EcdsaSha512 = "ES512";
public const string HmacSha256 = "HS256";
public const string HmacSha384 = "HS384";
public const string HmacSha512 = "HS512";
public const string RsaSha256 = "RS256";
public const string RsaSha384 = "RS384";
public const string RsaSha512 = "RS512";
public const string RsaSsaPssSha256 = "PS256";
public const string RsaSsaPssSha384 = "PS384";
public const string RsaSsaPssSha512 = "PS512";
}
public static class AuthenticationMethodReferences
{
public const string Face = "face";
public const string Fingerprint = "fpt";
public const string Geolocation = "geo";
public const string HardwareKey = "hwk";
public const string Iris = "iris";
public const string KnowledgeBased = "kba";
public const string MultiChannel = "mca";
public const string MultiFactor = "mfa";
public const string OneTimePassword = "otp";
public const string Password = "pwd";
public const string PersonalIdentificationNumber = "pin";
public const string Retina = "retina";
public const string RiskBased = "rba";
public const string ShortMessageService = "sms";
public const string SmartCard = "sc";
public const string SoftwareKey = "swk";
public const string Telephone = "tel";
public const string UserPresence = "user";
public const string Voiceprint = "vbm";
public const string WindowsIntegratedAuthentication = "wia";
}
public static class AuthenticationMethodReferences
{
public const string Face = "face";
public const string Fingerprint = "fpt";
public const string Geolocation = "geo";
public const string HardwareKey = "hwk";
public const string Iris = "iris";
public const string KnowledgeBased = "kba";
public const string MultiChannel = "mca";
public const string MultiFactor = "mfa";
public const string OneTimePassword = "otp";
public const string Password = "pwd";
public const string PersonalIdentificationNumber = "pin";
public const string Retina = "retina";
public const string RiskBased = "rba";
public const string ShortMessageService = "sms";
public const string SmartCard = "sc";
public const string SoftwareKey = "swk";
public const string Telephone = "tel";
public const string UserPresence = "user";
public const string Voiceprint = "vbm";
public const string WindowsIntegratedAuthentication = "wia";
}
public static class AuthorizationTypes
{
public const string AdHoc = "ad-hoc";
public const string Permanent = "permanent";
}
public static class AuthorizationTypes
{
public const string AdHoc = "ad-hoc";
public const string Permanent = "permanent";
}
public static class Claims
public static class Claims
{
public const string AccessTokenHash = "at_hash";
public const string Active = "active";
public const string Address = "address";
public const string Audience = "aud";
public const string AuthenticationContextReference = "acr";
public const string AuthenticationMethodReference = "amr";
public const string AuthenticationTime = "auth_time";
public const string AuthorizedParty = "azp";
public const string Birthdate = "birthdate";
public const string ClientId = "client_id";
public const string CodeHash = "c_hash";
public const string Country = "country";
public const string Email = "email";
public const string EmailVerified = "email_verified";
public const string ExpiresAt = "exp";
public const string FamilyName = "family_name";
public const string Formatted = "formatted";
public const string Gender = "gender";
public const string GivenName = "given_name";
public const string IssuedAt = "iat";
public const string Issuer = "iss";
public const string Locale = "locale";
public const string Locality = "locality";
public const string JwtId = "jti";
public const string KeyId = "kid";
public const string MiddleName = "middle_name";
public const string Name = "name";
public const string Nickname = "nickname";
public const string Nonce = "nonce";
public const string NotBefore = "nbf";
public const string PhoneNumber = "phone_number";
public const string PhoneNumberVerified = "phone_number_verified";
public const string Picture = "picture";
public const string PostalCode = "postal_code";
public const string PreferredUsername = "preferred_username";
public const string Profile = "profile";
public const string Region = "region";
public const string Role = "role";
public const string Scope = "scope";
public const string StreetAddress = "street_address";
public const string Subject = "sub";
public const string TokenType = "token_type";
public const string TokenUsage = "token_usage";
public const string UpdatedAt = "updated_at";
public const string Username = "username";
public const string Website = "website";
public const string Zoneinfo = "zoneinfo";
public static class Prefixes
{
public const string AccessTokenHash = "at_hash";
public const string Active = "active";
public const string Address = "address";
public const string Audience = "aud";
public const string AuthenticationContextReference = "acr";
public const string AuthenticationMethodReference = "amr";
public const string AuthenticationTime = "auth_time";
public const string AuthorizedParty = "azp";
public const string Birthdate = "birthdate";
public const string ClientId = "client_id";
public const string CodeHash = "c_hash";
public const string Country = "country";
public const string Email = "email";
public const string EmailVerified = "email_verified";
public const string ExpiresAt = "exp";
public const string FamilyName = "family_name";
public const string Formatted = "formatted";
public const string Gender = "gender";
public const string GivenName = "given_name";
public const string IssuedAt = "iat";
public const string Issuer = "iss";
public const string Locale = "locale";
public const string Locality = "locality";
public const string JwtId = "jti";
public const string KeyId = "kid";
public const string MiddleName = "middle_name";
public const string Name = "name";
public const string Nickname = "nickname";
public const string Nonce = "nonce";
public const string NotBefore = "nbf";
public const string PhoneNumber = "phone_number";
public const string PhoneNumberVerified = "phone_number_verified";
public const string Picture = "picture";
public const string PostalCode = "postal_code";
public const string PreferredUsername = "preferred_username";
public const string Profile = "profile";
public const string Region = "region";
public const string Role = "role";
public const string Scope = "scope";
public const string StreetAddress = "street_address";
public const string Subject = "sub";
public const string TokenType = "token_type";
public const string TokenUsage = "token_usage";
public const string UpdatedAt = "updated_at";
public const string Username = "username";
public const string Website = "website";
public const string Zoneinfo = "zoneinfo";
public static class Prefixes
{
public const string Private = "oi_";
}
public static class Private
{
public const string AccessTokenLifetime = "oi_act_lft";
public const string Audience = "oi_aud";
public const string AuthorizationCodeLifetime = "oi_auc_lft";
public const string AuthorizationId = "oi_au_id";
public const string ClaimDestinationsMap = "oi_cl_dstn";
public const string CodeChallenge = "oi_cd_chlg";
public const string CodeChallengeMethod = "oi_cd_chlg_meth";
public const string CreationDate = "oi_crt_dt";
public const string DeviceCodeId = "oi_dvc_id";
public const string DeviceCodeLifetime = "oi_dvc_lft";
public const string ExpirationDate = "oi_exp_dt";
public const string IdentityTokenLifetime = "oi_idt_lft";
public const string Nonce = "oi_nce";
public const string Presenter = "oi_prst";
public const string RedirectUri = "oi_reduri";
public const string RefreshTokenLifetime = "oi_reft_lft";
public const string Resource = "oi_rsrc";
public const string Scope = "oi_scp";
public const string TokenId = "oi_tkn_id";
public const string TokenType = "oi_tkn_typ";
public const string UserCodeLifetime = "oi_usrc_lft";
}
public const string Private = "oi_";
}
public static class ClientAuthenticationMethods
public static class Private
{
public const string ClientSecretBasic = "client_secret_basic";
public const string ClientSecretPost = "client_secret_post";
public const string AccessTokenLifetime = "oi_act_lft";
public const string Audience = "oi_aud";
public const string AuthorizationCodeLifetime = "oi_auc_lft";
public const string AuthorizationId = "oi_au_id";
public const string ClaimDestinationsMap = "oi_cl_dstn";
public const string CodeChallenge = "oi_cd_chlg";
public const string CodeChallengeMethod = "oi_cd_chlg_meth";
public const string CreationDate = "oi_crt_dt";
public const string DeviceCodeId = "oi_dvc_id";
public const string DeviceCodeLifetime = "oi_dvc_lft";
public const string ExpirationDate = "oi_exp_dt";
public const string IdentityTokenLifetime = "oi_idt_lft";
public const string Nonce = "oi_nce";
public const string Presenter = "oi_prst";
public const string RedirectUri = "oi_reduri";
public const string RefreshTokenLifetime = "oi_reft_lft";
public const string Resource = "oi_rsrc";
public const string Scope = "oi_scp";
public const string TokenId = "oi_tkn_id";
public const string TokenType = "oi_tkn_typ";
public const string UserCodeLifetime = "oi_usrc_lft";
}
}
public static class ClientTypes
{
public const string Confidential = "confidential";
public const string Public = "public";
}
public static class ClientAuthenticationMethods
{
public const string ClientSecretBasic = "client_secret_basic";
public const string ClientSecretPost = "client_secret_post";
}
public static class CodeChallengeMethods
{
public const string Plain = "plain";
public const string Sha256 = "S256";
}
public static class ClientTypes
{
public const string Confidential = "confidential";
public const string Public = "public";
}
public static class ConsentTypes
{
public const string Explicit = "explicit";
public const string External = "external";
public const string Implicit = "implicit";
public const string Systematic = "systematic";
}
public static class CodeChallengeMethods
{
public const string Plain = "plain";
public const string Sha256 = "S256";
}
public static class Destinations
{
public const string AccessToken = "access_token";
public const string IdentityToken = "id_token";
}
public static class ConsentTypes
{
public const string Explicit = "explicit";
public const string External = "external";
public const string Implicit = "implicit";
public const string Systematic = "systematic";
}
public static class Errors
{
public const string AccessDenied = "access_denied";
public const string AccountSelectionRequired = "account_selection_required";
public const string AuthorizationPending = "authorization_pending";
public const string ConsentRequired = "consent_required";
public const string ExpiredToken = "expired_token";
public const string InsufficientAccess = "insufficient_access";
public const string InsufficientScope = "insufficient_scope";
public const string InteractionRequired = "interaction_required";
public const string InvalidClient = "invalid_client";
public const string InvalidGrant = "invalid_grant";
public const string InvalidRequest = "invalid_request";
public const string InvalidRequestObject = "invalid_request_object";
public const string InvalidRequestUri = "invalid_request_uri";
public const string InvalidScope = "invalid_scope";
public const string InvalidToken = "invalid_token";
public const string LoginRequired = "login_required";
public const string MissingToken = "missing_token";
public const string RegistrationNotSupported = "registration_not_supported";
public const string RequestNotSupported = "request_not_supported";
public const string RequestUriNotSupported = "request_uri_not_supported";
public const string ServerError = "server_error";
public const string SlowDown = "slow_down";
public const string TemporarilyUnavailable = "temporarily_unavailable";
public const string UnauthorizedClient = "unauthorized_client";
public const string UnsupportedGrantType = "unsupported_grant_type";
public const string UnsupportedResponseType = "unsupported_response_type";
public const string UnsupportedTokenType = "unsupported_token_type";
}
public static class Destinations
{
public const string AccessToken = "access_token";
public const string IdentityToken = "id_token";
}
public static class GrantTypes
{
public const string AuthorizationCode = "authorization_code";
public const string ClientCredentials = "client_credentials";
public const string DeviceCode = "urn:ietf:params:oauth:grant-type:device_code";
public const string Implicit = "implicit";
public const string Password = "password";
public const string RefreshToken = "refresh_token";
}
public static class Errors
{
public const string AccessDenied = "access_denied";
public const string AccountSelectionRequired = "account_selection_required";
public const string AuthorizationPending = "authorization_pending";
public const string ConsentRequired = "consent_required";
public const string ExpiredToken = "expired_token";
public const string InsufficientAccess = "insufficient_access";
public const string InsufficientScope = "insufficient_scope";
public const string InteractionRequired = "interaction_required";
public const string InvalidClient = "invalid_client";
public const string InvalidGrant = "invalid_grant";
public const string InvalidRequest = "invalid_request";
public const string InvalidRequestObject = "invalid_request_object";
public const string InvalidRequestUri = "invalid_request_uri";
public const string InvalidScope = "invalid_scope";
public const string InvalidToken = "invalid_token";
public const string LoginRequired = "login_required";
public const string MissingToken = "missing_token";
public const string RegistrationNotSupported = "registration_not_supported";
public const string RequestNotSupported = "request_not_supported";
public const string RequestUriNotSupported = "request_uri_not_supported";
public const string ServerError = "server_error";
public const string SlowDown = "slow_down";
public const string TemporarilyUnavailable = "temporarily_unavailable";
public const string UnauthorizedClient = "unauthorized_client";
public const string UnsupportedGrantType = "unsupported_grant_type";
public const string UnsupportedResponseType = "unsupported_response_type";
public const string UnsupportedTokenType = "unsupported_token_type";
}
public static class JsonWebTokenTypes
{
public const string AccessToken = "at+jwt";
public const string IdentityToken = "JWT";
public static class Prefixes
{
public const string Application = "application/";
}
public static class Private
{
public const string AuthorizationCode = "oi_auc+jwt";
public const string DeviceCode = "oi_dvc+jwt";
public const string RefreshToken = "oi_reft+jwt";
public const string UserCode = "oi_usrc+jwt";
}
}
public static class GrantTypes
{
public const string AuthorizationCode = "authorization_code";
public const string ClientCredentials = "client_credentials";
public const string DeviceCode = "urn:ietf:params:oauth:grant-type:device_code";
public const string Implicit = "implicit";
public const string Password = "password";
public const string RefreshToken = "refresh_token";
}
public static class Metadata
{
public const string AcrValuesSupported = "acr_values_supported";
public const string AuthorizationEndpoint = "authorization_endpoint";
public const string ClaimsLocalesSupported = "claims_locales_supported";
public const string ClaimsParameterSupported = "claims_parameter_supported";
public const string ClaimsSupported = "claims_supported";
public const string ClaimTypesSupported = "claim_types_supported";
public const string CodeChallengeMethodsSupported = "code_challenge_methods_supported";
public const string DeviceAuthorizationEndpoint = "device_authorization_endpoint";
public const string DisplayValuesSupported = "display_values_supported";
public const string EndSessionEndpoint = "end_session_endpoint";
public const string GrantTypesSupported = "grant_types_supported";
public const string IdTokenEncryptionAlgValuesSupported = "id_token_encryption_alg_values_supported";
public const string IdTokenEncryptionEncValuesSupported = "id_token_encryption_enc_values_supported";
public const string IdTokenSigningAlgValuesSupported = "id_token_signing_alg_values_supported";
public const string IntrospectionEndpoint = "introspection_endpoint";
public const string IntrospectionEndpointAuthMethodsSupported = "introspection_endpoint_auth_methods_supported";
public const string IntrospectionEndpointAuthSigningAlgValuesSupported = "introspection_endpoint_auth_signing_alg_values_supported";
public const string Issuer = "issuer";
public const string JwksUri = "jwks_uri";
public const string OpPolicyUri = "op_policy_uri";
public const string OpTosUri = "op_tos_uri";
public const string RequestObjectEncryptionAlgValuesSupported = "request_object_encryption_alg_values_supported";
public const string RequestObjectEncryptionEncValuesSupported = "request_object_encryption_enc_values_supported";
public const string RequestObjectSigningAlgValuesSupported = "request_object_signing_alg_values_supported";
public const string RequestParameterSupported = "request_parameter_supported";
public const string RequestUriParameterSupported = "request_uri_parameter_supported";
public const string RequireRequestUriRegistration = "require_request_uri_registration";
public const string ResponseModesSupported = "response_modes_supported";
public const string ResponseTypesSupported = "response_types_supported";
public const string RevocationEndpoint = "revocation_endpoint";
public const string RevocationEndpointAuthMethodsSupported = "revocation_endpoint_auth_methods_supported";
public const string RevocationEndpointAuthSigningAlgValuesSupported = "revocation_endpoint_auth_signing_alg_values_supported";
public const string ScopesSupported = "scopes_supported";
public const string ServiceDocumentation = "service_documentation";
public const string SubjectTypesSupported = "subject_types_supported";
public const string TokenEndpoint = "token_endpoint";
public const string TokenEndpointAuthMethodsSupported = "token_endpoint_auth_methods_supported";
public const string TokenEndpointAuthSigningAlgValuesSupported = "token_endpoint_auth_signing_alg_values_supported";
public const string UiLocalesSupported = "ui_locales_supported";
public const string UserinfoEncryptionAlgValuesSupported = "userinfo_encryption_alg_values_supported";
public const string UserinfoEncryptionEncValuesSupported = "userinfo_encryption_enc_values_supported";
public const string UserinfoEndpoint = "userinfo_endpoint";
public const string UserinfoSigningAlgValuesSupported = "userinfo_signing_alg_values_supported";
}
public static class JsonWebTokenTypes
{
public const string AccessToken = "at+jwt";
public const string IdentityToken = "JWT";
public static class Parameters
public static class Prefixes
{
public const string AccessToken = "access_token";
public const string Active = "active";
public const string AcrValues = "acr_values";
public const string Assertion = "assertion";
public const string Audience = "audience";
public const string Claims = "claims";
public const string ClaimsLocales = "claims_locales";
public const string ClientAssertion = "client_assertion";
public const string ClientAssertionType = "client_assertion_type";
public const string ClientId = "client_id";
public const string ClientSecret = "client_secret";
public const string Code = "code";
public const string CodeChallenge = "code_challenge";
public const string CodeChallengeMethod = "code_challenge_method";
public const string CodeVerifier = "code_verifier";
public const string DeviceCode = "device_code";
public const string Display = "display";
public const string Error = "error";
public const string ErrorDescription = "error_description";
public const string ErrorUri = "error_uri";
public const string ExpiresIn = "expires_in";
public const string GrantType = "grant_type";
public const string IdentityProvider = "identity_provider";
public const string IdToken = "id_token";
public const string IdTokenHint = "id_token_hint";
public const string LoginHint = "login_hint";
public const string Keys = "keys";
public const string MaxAge = "max_age";
public const string Nonce = "nonce";
public const string Password = "password";
public const string PostLogoutRedirectUri = "post_logout_redirect_uri";
public const string Prompt = "prompt";
public const string Realm = "realm";
public const string RedirectUri = "redirect_uri";
public const string RefreshToken = "refresh_token";
public const string Registration = "registration";
public const string Request = "request";
public const string RequestId = "request_id";
public const string RequestUri = "request_uri";
public const string Resource = "resource";
public const string ResponseMode = "response_mode";
public const string ResponseType = "response_type";
public const string Scope = "scope";
public const string State = "state";
public const string Token = "token";
public const string TokenType = "token_type";
public const string TokenTypeHint = "token_type_hint";
public const string UiLocales = "ui_locales";
public const string UserCode = "user_code";
public const string Username = "username";
public const string VerificationUri = "verification_uri";
public const string VerificationUriComplete = "verification_uri_complete";
public const string Application = "application/";
}
public static class Permissions
public static class Private
{
public static class Endpoints
{
public const string Authorization = "ept:authorization";
public const string Device = "ept:device";
public const string Introspection = "ept:introspection";
public const string Logout = "ept:logout";
public const string Revocation = "ept:revocation";
public const string Token = "ept:token";
}
public static class GrantTypes
{
public const string AuthorizationCode = "gt:authorization_code";
public const string ClientCredentials = "gt:client_credentials";
public const string DeviceCode = "gt:urn:ietf:params:oauth:grant-type:device_code";
public const string Implicit = "gt:implicit";
public const string Password = "gt:password";
public const string RefreshToken = "gt:refresh_token";
}
public static class Prefixes
{
public const string Endpoint = "ept:";
public const string GrantType = "gt:";
public const string ResponseType = "rst:";
public const string Scope = "scp:";
}
public static class ResponseTypes
{
public const string Code = "rst:code";
public const string CodeIdToken = "rst:code id_token";
public const string CodeIdTokenToken = "rst:code id_token token";
public const string CodeToken = "rst:code token";
public const string IdToken = "rst:id_token";
public const string IdTokenToken = "rst:id_token token";
public const string None = "rst:none";
public const string Token = "rst:token";
}
public static class Scopes
{
public const string Address = "scp:address";
public const string Email = "scp:email";
public const string Phone = "scp:phone";
public const string Profile = "scp:profile";
public const string Roles = "scp:roles";
}
public const string AuthorizationCode = "oi_auc+jwt";
public const string DeviceCode = "oi_dvc+jwt";
public const string RefreshToken = "oi_reft+jwt";
public const string UserCode = "oi_usrc+jwt";
}
}
public static class Prompts
{
public const string Consent = "consent";
public const string Login = "login";
public const string None = "none";
public const string SelectAccount = "select_account";
}
public static class Metadata
{
public const string AcrValuesSupported = "acr_values_supported";
public const string AuthorizationEndpoint = "authorization_endpoint";
public const string ClaimsLocalesSupported = "claims_locales_supported";
public const string ClaimsParameterSupported = "claims_parameter_supported";
public const string ClaimsSupported = "claims_supported";
public const string ClaimTypesSupported = "claim_types_supported";
public const string CodeChallengeMethodsSupported = "code_challenge_methods_supported";
public const string DeviceAuthorizationEndpoint = "device_authorization_endpoint";
public const string DisplayValuesSupported = "display_values_supported";
public const string EndSessionEndpoint = "end_session_endpoint";
public const string GrantTypesSupported = "grant_types_supported";
public const string IdTokenEncryptionAlgValuesSupported = "id_token_encryption_alg_values_supported";
public const string IdTokenEncryptionEncValuesSupported = "id_token_encryption_enc_values_supported";
public const string IdTokenSigningAlgValuesSupported = "id_token_signing_alg_values_supported";
public const string IntrospectionEndpoint = "introspection_endpoint";
public const string IntrospectionEndpointAuthMethodsSupported = "introspection_endpoint_auth_methods_supported";
public const string IntrospectionEndpointAuthSigningAlgValuesSupported = "introspection_endpoint_auth_signing_alg_values_supported";
public const string Issuer = "issuer";
public const string JwksUri = "jwks_uri";
public const string OpPolicyUri = "op_policy_uri";
public const string OpTosUri = "op_tos_uri";
public const string RequestObjectEncryptionAlgValuesSupported = "request_object_encryption_alg_values_supported";
public const string RequestObjectEncryptionEncValuesSupported = "request_object_encryption_enc_values_supported";
public const string RequestObjectSigningAlgValuesSupported = "request_object_signing_alg_values_supported";
public const string RequestParameterSupported = "request_parameter_supported";
public const string RequestUriParameterSupported = "request_uri_parameter_supported";
public const string RequireRequestUriRegistration = "require_request_uri_registration";
public const string ResponseModesSupported = "response_modes_supported";
public const string ResponseTypesSupported = "response_types_supported";
public const string RevocationEndpoint = "revocation_endpoint";
public const string RevocationEndpointAuthMethodsSupported = "revocation_endpoint_auth_methods_supported";
public const string RevocationEndpointAuthSigningAlgValuesSupported = "revocation_endpoint_auth_signing_alg_values_supported";
public const string ScopesSupported = "scopes_supported";
public const string ServiceDocumentation = "service_documentation";
public const string SubjectTypesSupported = "subject_types_supported";
public const string TokenEndpoint = "token_endpoint";
public const string TokenEndpointAuthMethodsSupported = "token_endpoint_auth_methods_supported";
public const string TokenEndpointAuthSigningAlgValuesSupported = "token_endpoint_auth_signing_alg_values_supported";
public const string UiLocalesSupported = "ui_locales_supported";
public const string UserinfoEncryptionAlgValuesSupported = "userinfo_encryption_alg_values_supported";
public const string UserinfoEncryptionEncValuesSupported = "userinfo_encryption_enc_values_supported";
public const string UserinfoEndpoint = "userinfo_endpoint";
public const string UserinfoSigningAlgValuesSupported = "userinfo_signing_alg_values_supported";
}
public static class Parameters
{
public const string AccessToken = "access_token";
public const string Active = "active";
public const string AcrValues = "acr_values";
public const string Assertion = "assertion";
public const string Audience = "audience";
public const string Claims = "claims";
public const string ClaimsLocales = "claims_locales";
public const string ClientAssertion = "client_assertion";
public const string ClientAssertionType = "client_assertion_type";
public const string ClientId = "client_id";
public const string ClientSecret = "client_secret";
public const string Code = "code";
public const string CodeChallenge = "code_challenge";
public const string CodeChallengeMethod = "code_challenge_method";
public const string CodeVerifier = "code_verifier";
public const string DeviceCode = "device_code";
public const string Display = "display";
public const string Error = "error";
public const string ErrorDescription = "error_description";
public const string ErrorUri = "error_uri";
public const string ExpiresIn = "expires_in";
public const string GrantType = "grant_type";
public const string IdentityProvider = "identity_provider";
public const string IdToken = "id_token";
public const string IdTokenHint = "id_token_hint";
public const string LoginHint = "login_hint";
public const string Keys = "keys";
public const string MaxAge = "max_age";
public const string Nonce = "nonce";
public const string Password = "password";
public const string PostLogoutRedirectUri = "post_logout_redirect_uri";
public const string Prompt = "prompt";
public const string Realm = "realm";
public const string RedirectUri = "redirect_uri";
public const string RefreshToken = "refresh_token";
public const string Registration = "registration";
public const string Request = "request";
public const string RequestId = "request_id";
public const string RequestUri = "request_uri";
public const string Resource = "resource";
public const string ResponseMode = "response_mode";
public const string ResponseType = "response_type";
public const string Scope = "scope";
public const string State = "state";
public const string Token = "token";
public const string TokenType = "token_type";
public const string TokenTypeHint = "token_type_hint";
public const string UiLocales = "ui_locales";
public const string UserCode = "user_code";
public const string Username = "username";
public const string VerificationUri = "verification_uri";
public const string VerificationUriComplete = "verification_uri_complete";
}
public static class Properties
public static class Permissions
{
public static class Endpoints
{
public const string Destinations = ".destinations";
public const string Authorization = "ept:authorization";
public const string Device = "ept:device";
public const string Introspection = "ept:introspection";
public const string Logout = "ept:logout";
public const string Revocation = "ept:revocation";
public const string Token = "ept:token";
}
public static class Requirements
public static class GrantTypes
{
public static class Features
{
public const string ProofKeyForCodeExchange = "ft:pkce";
}
public static class Prefixes
{
public const string Feature = "ft:";
}
public const string AuthorizationCode = "gt:authorization_code";
public const string ClientCredentials = "gt:client_credentials";
public const string DeviceCode = "gt:urn:ietf:params:oauth:grant-type:device_code";
public const string Implicit = "gt:implicit";
public const string Password = "gt:password";
public const string RefreshToken = "gt:refresh_token";
}
public static class ResponseModes
public static class Prefixes
{
public const string FormPost = "form_post";
public const string Fragment = "fragment";
public const string Query = "query";
public const string Endpoint = "ept:";
public const string GrantType = "gt:";
public const string ResponseType = "rst:";
public const string Scope = "scp:";
}
public static class ResponseTypes
{
public const string Code = "code";
public const string IdToken = "id_token";
public const string None = "none";
public const string Token = "token";
public const string Code = "rst:code";
public const string CodeIdToken = "rst:code id_token";
public const string CodeIdTokenToken = "rst:code id_token token";
public const string CodeToken = "rst:code token";
public const string IdToken = "rst:id_token";
public const string IdTokenToken = "rst:id_token token";
public const string None = "rst:none";
public const string Token = "rst:token";
}
public static class Separators
public static class Scopes
{
public static readonly char[] Ampersand = { '&' };
public static readonly char[] Dash = { '-' };
public static readonly char[] Space = { ' ' };
public const string Address = "scp:address";
public const string Email = "scp:email";
public const string Phone = "scp:phone";
public const string Profile = "scp:profile";
public const string Roles = "scp:roles";
}
}
public static class Schemes
{
public const string Basic = "Basic";
public const string Bearer = "Bearer";
}
public static class Prompts
{
public const string Consent = "consent";
public const string Login = "login";
public const string None = "none";
public const string SelectAccount = "select_account";
}
public static class Scopes
{
public const string Address = "address";
public const string Email = "email";
public const string OfflineAccess = "offline_access";
public const string OpenId = "openid";
public const string Phone = "phone";
public const string Profile = "profile";
public const string Roles = "roles";
}
public static class Properties
{
public const string Destinations = ".destinations";
}
public static class Statuses
public static class Requirements
{
public static class Features
{
public const string Inactive = "inactive";
public const string Redeemed = "redeemed";
public const string Rejected = "rejected";
public const string Revoked = "revoked";
public const string Valid = "valid";
public const string ProofKeyForCodeExchange = "ft:pkce";
}
public static class SubjectTypes
public static class Prefixes
{
public const string Pairwise = "pairwise";
public const string Public = "public";
public const string Feature = "ft:";
}
}
public static class TokenTypeHints
{
public const string AccessToken = "access_token";
public const string AuthorizationCode = "authorization_code";
public const string DeviceCode = "device_code";
public const string IdToken = "id_token";
public const string RefreshToken = "refresh_token";
public const string UserCode = "user_code";
}
public static class ResponseModes
{
public const string FormPost = "form_post";
public const string Fragment = "fragment";
public const string Query = "query";
}
public static class TokenTypes
{
public const string Bearer = "Bearer";
}
public static class ResponseTypes
{
public const string Code = "code";
public const string IdToken = "id_token";
public const string None = "none";
public const string Token = "token";
}
public static class Separators
{
public static readonly char[] Ampersand = { '&' };
public static readonly char[] Dash = { '-' };
public static readonly char[] Space = { ' ' };
}
public static class Schemes
{
public const string Basic = "Basic";
public const string Bearer = "Bearer";
}
public static class Scopes
{
public const string Address = "address";
public const string Email = "email";
public const string OfflineAccess = "offline_access";
public const string OpenId = "openid";
public const string Phone = "phone";
public const string Profile = "profile";
public const string Roles = "roles";
}
public static class Statuses
{
public const string Inactive = "inactive";
public const string Redeemed = "redeemed";
public const string Rejected = "rejected";
public const string Revoked = "revoked";
public const string Valid = "valid";
}
public static class SubjectTypes
{
public const string Pairwise = "pairwise";
public const string Public = "public";
}
public static class TokenTypeHints
{
public const string AccessToken = "access_token";
public const string AuthorizationCode = "authorization_code";
public const string DeviceCode = "device_code";
public const string IdToken = "id_token";
public const string RefreshToken = "refresh_token";
public const string UserCode = "user_code";
}
public static class TokenTypes
{
public const string Bearer = "Bearer";
}
}

225
src/OpenIddict.Abstractions/OpenIddictExceptions.cs

@ -2,144 +2,143 @@
using System.Collections.Immutable;
using System.ComponentModel.DataAnnotations;
namespace OpenIddict.Abstractions
namespace OpenIddict.Abstractions;
/// <summary>
/// Exposes common exceptions thrown by OpenIddict.
/// </summary>
public static class OpenIddictExceptions
{
/// <summary>
/// Exposes common exceptions thrown by OpenIddict.
/// Represents an OpenIddict concurrency exception.
/// </summary>
public static class OpenIddictExceptions
public class ConcurrencyException : Exception
{
/// <summary>
/// Represents an OpenIddict concurrency exception.
/// Creates a new <see cref="ConcurrencyException"/>.
/// </summary>
public class ConcurrencyException : Exception
/// <param name="message">The exception message.</param>
public ConcurrencyException(string? message)
: this(message, exception: null)
{
/// <summary>
/// Creates a new <see cref="ConcurrencyException"/>.
/// </summary>
/// <param name="message">The exception message.</param>
public ConcurrencyException(string? message)
: this(message, exception: null)
{
}
}
/// <summary>
/// Creates a new <see cref="ConcurrencyException"/>.
/// </summary>
/// <param name="message">The exception message.</param>
/// <param name="exception">The inner exception.</param>
public ConcurrencyException(string? message, Exception? exception)
: base(message, exception)
{
}
/// <summary>
/// Creates a new <see cref="ConcurrencyException"/>.
/// </summary>
/// <param name="message">The exception message.</param>
/// <param name="exception">The inner exception.</param>
public ConcurrencyException(string? message, Exception? exception)
: base(message, exception)
{
}
}
/// <summary>
/// Represents a generic OpenIddict exception.
/// </summary>
public class GenericException : Exception
{
/// <summary>
/// Represents a generic OpenIddict exception.
/// Creates a new <see cref="GenericException"/>.
/// </summary>
public class GenericException : Exception
/// <param name="message">The exception message.</param>
public GenericException(string? message)
: this(message, null)
{
/// <summary>
/// Creates a new <see cref="GenericException"/>.
/// </summary>
/// <param name="message">The exception message.</param>
public GenericException(string? message)
: this(message, null)
{
}
}
/// <summary>
/// Creates a new <see cref="GenericException"/>.
/// </summary>
/// <param name="message">The exception message.</param>
/// <param name="error">The error type.</param>
public GenericException(string? message, string? error)
: this(message, error, description: null)
{
}
/// <summary>
/// Creates a new <see cref="GenericException"/>.
/// </summary>
/// <param name="message">The exception message.</param>
/// <param name="error">The error type.</param>
public GenericException(string? message, string? error)
: this(message, error, description: null)
{
}
/// <summary>
/// Creates a new <see cref="GenericException"/>.
/// </summary>
/// <param name="message">The exception message.</param>
/// <param name="error">The error type.</param>
/// <param name="description">The error description.</param>
public GenericException(string? message, string? error, string? description)
: this(message, error, description, uri: null)
{
}
/// <summary>
/// Creates a new <see cref="GenericException"/>.
/// </summary>
/// <param name="message">The exception message.</param>
/// <param name="error">The error type.</param>
/// <param name="description">The error description.</param>
public GenericException(string? message, string? error, string? description)
: this(message, error, description, uri: null)
{
}
/// <summary>
/// Creates a new <see cref="GenericException"/>.
/// </summary>
/// <param name="message">The exception message.</param>
/// <param name="error">The error type.</param>
/// <param name="description">The error description.</param>
/// <param name="uri">The error URI.</param>
public GenericException(string? message, string? error, string? description, string? uri)
: base(message)
{
Error = error;
ErrorDescription = description;
ErrorUri = uri;
}
/// <summary>
/// Creates a new <see cref="GenericException"/>.
/// </summary>
/// <param name="message">The exception message.</param>
/// <param name="error">The error type.</param>
/// <param name="description">The error description.</param>
/// <param name="uri">The error URI.</param>
public GenericException(string? message, string? error, string? description, string? uri)
: base(message)
{
Error = error;
ErrorDescription = description;
ErrorUri = uri;
}
/// <summary>
/// Gets the error type.
/// </summary>
public string? Error { get; }
/// <summary>
/// Gets the error type.
/// </summary>
public string? Error { get; }
/// <summary>
/// Gets the error description.
/// </summary>
public string? ErrorDescription { get; }
/// <summary>
/// Gets the error description.
/// </summary>
public string? ErrorDescription { get; }
/// <summary>
/// Gets the error URI.
/// </summary>
public string? ErrorUri { get; }
}
/// <summary>
/// Gets the error URI.
/// </summary>
public string? ErrorUri { get; }
}
/// <summary>
/// Represents an OpenIddict validation exception.
/// </summary>
public class ValidationException : Exception
{
/// <summary>
/// Represents an OpenIddict validation exception.
/// Creates a new <see cref="ValidationException"/>.
/// </summary>
public class ValidationException : Exception
/// <param name="message">The exception message.</param>
public ValidationException(string? message)
: this(message, ImmutableArray.Create<ValidationResult>())
{
/// <summary>
/// Creates a new <see cref="ValidationException"/>.
/// </summary>
/// <param name="message">The exception message.</param>
public ValidationException(string? message)
: this(message, ImmutableArray.Create<ValidationResult>())
{
}
/// <summary>
/// Creates a new <see cref="ValidationException"/>.
/// </summary>
/// <param name="message">The exception message.</param>
/// <param name="results">The validation results.</param>
public ValidationException(string? message, ImmutableArray<ValidationResult> results)
: this(message, results, exception: null)
{
}
}
/// <summary>
/// Creates a new <see cref="ValidationException"/>.
/// </summary>
/// <param name="message">The exception message.</param>
/// <param name="results">The validation results.</param>
/// <param name="exception">The inner exception.</param>
public ValidationException(string? message, ImmutableArray<ValidationResult> results, Exception? exception)
: base(message, exception)
{
Results = results;
}
/// <summary>
/// Creates a new <see cref="ValidationException"/>.
/// </summary>
/// <param name="message">The exception message.</param>
/// <param name="results">The validation results.</param>
public ValidationException(string? message, ImmutableArray<ValidationResult> results)
: this(message, results, exception: null)
{
}
/// <summary>
/// Gets the validation results associated with this exception.
/// </summary>
public ImmutableArray<ValidationResult> Results { get; }
/// <summary>
/// Creates a new <see cref="ValidationException"/>.
/// </summary>
/// <param name="message">The exception message.</param>
/// <param name="results">The validation results.</param>
/// <param name="exception">The inner exception.</param>
public ValidationException(string? message, ImmutableArray<ValidationResult> results, Exception? exception)
: base(message, exception)
{
Results = results;
}
/// <summary>
/// Gets the validation results associated with this exception.
/// </summary>
public ImmutableArray<ValidationResult> Results { get; }
}
}

71
src/OpenIddict.Abstractions/OpenIddictExtensions.cs

@ -6,51 +6,50 @@
using System;
namespace Microsoft.Extensions.DependencyInjection
namespace Microsoft.Extensions.DependencyInjection;
/// <summary>
/// Exposes extensions allowing to register the OpenIddict services.
/// </summary>
public static class OpenIddictExtensions
{
/// <summary>
/// Exposes extensions allowing to register the OpenIddict services.
/// Provides a common entry point for registering the OpenIddict services.
/// </summary>
public static class OpenIddictExtensions
/// <param name="services">The services collection.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddOpenIddict(this IServiceCollection services)
{
/// <summary>
/// Provides a common entry point for registering the OpenIddict services.
/// </summary>
/// <param name="services">The services collection.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddOpenIddict(this IServiceCollection services)
if (services is null)
{
if (services is null)
{
throw new ArgumentNullException(nameof(services));
}
return new OpenIddictBuilder(services);
throw new ArgumentNullException(nameof(services));
}
/// <summary>
/// Provides a common entry point for registering the OpenIddict services.
/// </summary>
/// <param name="services">The services collection.</param>
/// <param name="configuration">The configuration delegate used to register new services.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
/// <returns>The <see cref="IServiceCollection"/>.</returns>
public static IServiceCollection AddOpenIddict(this IServiceCollection services, Action<OpenIddictBuilder> configuration)
return new OpenIddictBuilder(services);
}
/// <summary>
/// Provides a common entry point for registering the OpenIddict services.
/// </summary>
/// <param name="services">The services collection.</param>
/// <param name="configuration">The configuration delegate used to register new services.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
/// <returns>The <see cref="IServiceCollection"/>.</returns>
public static IServiceCollection AddOpenIddict(this IServiceCollection services, Action<OpenIddictBuilder> configuration)
{
if (services is null)
{
if (services is null)
{
throw new ArgumentNullException(nameof(services));
}
throw new ArgumentNullException(nameof(services));
}
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
configuration(services.AddOpenIddict());
configuration(services.AddOpenIddict());
return services;
}
return services;
}
}
}

103
src/OpenIddict.Abstractions/Primitives/OpenIddictConverter.cs

@ -9,71 +9,70 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using SR = OpenIddict.Abstractions.OpenIddictResources;
namespace OpenIddict.Abstractions
namespace OpenIddict.Abstractions;
/// <summary>
/// Represents a JSON.NET converter able to convert OpenIddict primitives.
/// </summary>
public class OpenIddictConverter : JsonConverter<OpenIddictMessage>
{
/// <summary>
/// Represents a JSON.NET converter able to convert OpenIddict primitives.
/// Determines whether the specified type is supported by this converter.
/// </summary>
public class OpenIddictConverter : JsonConverter<OpenIddictMessage>
/// <param name="typeToConvert">The type to convert.</param>
/// <returns><c>true</c> if the type is supported, <c>false</c> otherwise.</returns>
public override bool CanConvert(Type typeToConvert)
{
/// <summary>
/// Determines whether the specified type is supported by this converter.
/// </summary>
/// <param name="typeToConvert">The type to convert.</param>
/// <returns><c>true</c> if the type is supported, <c>false</c> otherwise.</returns>
public override bool CanConvert(Type typeToConvert)
if (typeToConvert is null)
{
if (typeToConvert is null)
{
throw new ArgumentNullException(nameof(typeToConvert));
}
return typeToConvert == typeof(OpenIddictMessage) ||
typeToConvert == typeof(OpenIddictRequest) ||
typeToConvert == typeof(OpenIddictResponse);
throw new ArgumentNullException(nameof(typeToConvert));
}
/// <summary>
/// Deserializes an <see cref="OpenIddictMessage"/> instance.
/// </summary>
/// <param name="reader">The JSON reader.</param>
/// <param name="typeToConvert">The type of the deserialized instance.</param>
/// <param name="options">The JSON serializer options.</param>
/// <returns>The deserialized <see cref="OpenIddictMessage"/> instance.</returns>
public override OpenIddictMessage Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
return typeToConvert == typeof(OpenIddictMessage) ||
typeToConvert == typeof(OpenIddictRequest) ||
typeToConvert == typeof(OpenIddictResponse);
}
/// <summary>
/// Deserializes an <see cref="OpenIddictMessage"/> instance.
/// </summary>
/// <param name="reader">The JSON reader.</param>
/// <param name="typeToConvert">The type of the deserialized instance.</param>
/// <param name="options">The JSON serializer options.</param>
/// <returns>The deserialized <see cref="OpenIddictMessage"/> instance.</returns>
public override OpenIddictMessage Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (typeToConvert is null)
{
if (typeToConvert is null)
{
throw new ArgumentNullException(nameof(typeToConvert));
}
throw new ArgumentNullException(nameof(typeToConvert));
}
using var document = JsonDocument.ParseValue(ref reader);
using var document = JsonDocument.ParseValue(ref reader);
return typeToConvert == typeof(OpenIddictMessage) ? new OpenIddictMessage(document.RootElement.Clone()) :
typeToConvert == typeof(OpenIddictRequest) ? new OpenIddictRequest(document.RootElement.Clone()) :
typeToConvert == typeof(OpenIddictResponse) ? new OpenIddictResponse(document.RootElement.Clone()) :
throw new ArgumentException(SR.GetResourceString(SR.ID0176), nameof(typeToConvert));
}
return typeToConvert == typeof(OpenIddictMessage) ? new OpenIddictMessage(document.RootElement.Clone()) :
typeToConvert == typeof(OpenIddictRequest) ? new OpenIddictRequest(document.RootElement.Clone()) :
typeToConvert == typeof(OpenIddictResponse) ? new OpenIddictResponse(document.RootElement.Clone()) :
throw new ArgumentException(SR.GetResourceString(SR.ID0176), nameof(typeToConvert));
}
/// <summary>
/// Serializes an OpenIddict primitive.
/// </summary>
/// <param name="writer">The JSON writer.</param>
/// <param name="value">The instance.</param>
/// <param name="options">The JSON serializer options.</param>
public override void Write(Utf8JsonWriter writer, OpenIddictMessage value, JsonSerializerOptions options)
/// <summary>
/// Serializes an OpenIddict primitive.
/// </summary>
/// <param name="writer">The JSON writer.</param>
/// <param name="value">The instance.</param>
/// <param name="options">The JSON serializer options.</param>
public override void Write(Utf8JsonWriter writer, OpenIddictMessage value, JsonSerializerOptions options)
{
if (writer is null)
{
if (writer is null)
{
throw new ArgumentNullException(nameof(writer));
}
if (value is null)
{
throw new ArgumentNullException(nameof(value));
}
throw new ArgumentNullException(nameof(writer));
}
value.WriteTo(writer);
if (value is null)
{
throw new ArgumentNullException(nameof(value));
}
value.WriteTo(writer);
}
}

2809
src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs

File diff suppressed because it is too large

611
src/OpenIddict.Abstractions/Primitives/OpenIddictMessage.cs

@ -17,397 +17,396 @@ using System.Text.Json.Serialization;
using Microsoft.Extensions.Primitives;
using SR = OpenIddict.Abstractions.OpenIddictResources;
namespace OpenIddict.Abstractions
namespace OpenIddict.Abstractions;
/// <summary>
/// Represents an abstract OpenIddict message.
/// </summary>
/// <remarks>
/// Security notice: developers instantiating this type are responsible of ensuring that the
/// imported parameters are safe and won't cause the resulting message to grow abnormally,
/// which may result in an excessive memory consumption and a potential denial of service.
/// </remarks>
[DebuggerDisplay("Parameters: {Parameters.Count}")]
[JsonConverter(typeof(OpenIddictConverter))]
public class OpenIddictMessage
{
/// <summary>
/// Represents an abstract OpenIddict message.
/// Initializes a new OpenIddict message.
/// </summary>
/// <remarks>
/// Security notice: developers instantiating this type are responsible of ensuring that the
/// imported parameters are safe and won't cause the resulting message to grow abnormally,
/// which may result in an excessive memory consumption and a potential denial of service.
/// </remarks>
[DebuggerDisplay("Parameters: {Parameters.Count}")]
[JsonConverter(typeof(OpenIddictConverter))]
public class OpenIddictMessage
public OpenIddictMessage()
{
/// <summary>
/// Initializes a new OpenIddict message.
/// </summary>
public OpenIddictMessage()
}
/// <summary>
/// Initializes a new OpenIddict message.
/// </summary>
/// <param name="parameters">The message parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictMessage(JsonElement parameters)
{
if (parameters.ValueKind != JsonValueKind.Object)
{
throw new ArgumentException(SR.GetResourceString(SR.ID0189), nameof(parameters));
}
/// <summary>
/// Initializes a new OpenIddict message.
/// </summary>
/// <param name="parameters">The message parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictMessage(JsonElement parameters)
foreach (var parameter in parameters.EnumerateObject())
{
if (parameters.ValueKind != JsonValueKind.Object)
// Ignore parameters whose name is null or empty.
if (string.IsNullOrEmpty(parameter.Name))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0189), nameof(parameters));
continue;
}
foreach (var parameter in parameters.EnumerateObject())
// While generally discouraged, JSON objects can contain multiple properties with
// the same name. In this case, the last occurrence replaces the previous ones.
if (HasParameter(parameter.Name))
{
// Ignore parameters whose name is null or empty.
if (string.IsNullOrEmpty(parameter.Name))
{
continue;
}
RemoveParameter(parameter.Name);
}
// While generally discouraged, JSON objects can contain multiple properties with
// the same name. In this case, the last occurrence replaces the previous ones.
if (HasParameter(parameter.Name))
{
RemoveParameter(parameter.Name);
}
AddParameter(parameter.Name, parameter.Value);
}
}
AddParameter(parameter.Name, parameter.Value);
}
/// <summary>
/// Initializes a new OpenIddict message.
/// </summary>
/// <param name="parameters">The message parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictMessage(IEnumerable<KeyValuePair<string, OpenIddictParameter>> parameters)
{
if (parameters is null)
{
throw new ArgumentNullException(nameof(parameters));
}
/// <summary>
/// Initializes a new OpenIddict message.
/// </summary>
/// <param name="parameters">The message parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictMessage(IEnumerable<KeyValuePair<string, OpenIddictParameter>> parameters)
foreach (var parameter in parameters)
{
if (parameters is null)
// Ignore parameters whose name is null or empty.
if (string.IsNullOrEmpty(parameter.Key))
{
throw new ArgumentNullException(nameof(parameters));
continue;
}
foreach (var parameter in parameters)
{
// Ignore parameters whose name is null or empty.
if (string.IsNullOrEmpty(parameter.Key))
{
continue;
}
AddParameter(parameter.Key, parameter.Value);
}
}
AddParameter(parameter.Key, parameter.Value);
}
/// <summary>
/// Initializes a new OpenIddict message.
/// </summary>
/// <param name="parameters">The message parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictMessage(IEnumerable<KeyValuePair<string, string?>> parameters)
{
if (parameters is null)
{
throw new ArgumentNullException(nameof(parameters));
}
/// <summary>
/// Initializes a new OpenIddict message.
/// </summary>
/// <param name="parameters">The message parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictMessage(IEnumerable<KeyValuePair<string, string?>> parameters)
foreach (var parameter in parameters.GroupBy(parameter => parameter.Key))
{
if (parameters is null)
// Ignore parameters whose name is null or empty.
if (string.IsNullOrEmpty(parameter.Key))
{
throw new ArgumentNullException(nameof(parameters));
continue;
}
foreach (var parameter in parameters.GroupBy(parameter => parameter.Key))
var values = parameter.Select(parameter => parameter.Value).ToArray();
// Note: the core OAuth 2.0 specification requires that request parameters
// not be present more than once but derived specifications like the
// token exchange specification deliberately allow specifying multiple
// parameters with the same name to represent a multi-valued parameter.
AddParameter(parameter.Key, values.Length switch
{
// Ignore parameters whose name is null or empty.
if (string.IsNullOrEmpty(parameter.Key))
{
continue;
}
var values = parameter.Select(parameter => parameter.Value).ToArray();
// Note: the core OAuth 2.0 specification requires that request parameters
// not be present more than once but derived specifications like the
// token exchange specification deliberately allow specifying multiple
// parameters with the same name to represent a multi-valued parameter.
AddParameter(parameter.Key, values.Length switch
{
0 => default,
1 => values[0],
_ => values
});
}
0 => default,
1 => values[0],
_ => values
});
}
}
/// <summary>
/// Initializes a new OpenIddict message.
/// </summary>
/// <param name="parameters">The message parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictMessage(IEnumerable<KeyValuePair<string, string?[]?>> parameters)
/// <summary>
/// Initializes a new OpenIddict message.
/// </summary>
/// <param name="parameters">The message parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictMessage(IEnumerable<KeyValuePair<string, string?[]?>> parameters)
{
if (parameters is null)
{
if (parameters is null)
{
throw new ArgumentNullException(nameof(parameters));
}
foreach (var parameter in parameters)
{
// Ignore parameters whose name is null or empty.
if (string.IsNullOrEmpty(parameter.Key))
{
continue;
}
// Note: the core OAuth 2.0 specification requires that request parameters
// not be present more than once but derived specifications like the
// token exchange specification deliberately allow specifying multiple
// parameters with the same name to represent a multi-valued parameter.
AddParameter(parameter.Key, parameter.Value?.Length switch
{
null or 0 => default,
1 => parameter.Value[0],
_ => parameter.Value
});
}
throw new ArgumentNullException(nameof(parameters));
}
/// <summary>
/// Initializes a new OpenIddict message.
/// </summary>
/// <param name="parameters">The message parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictMessage(IEnumerable<KeyValuePair<string, StringValues>> parameters)
foreach (var parameter in parameters)
{
if (parameters is null)
// Ignore parameters whose name is null or empty.
if (string.IsNullOrEmpty(parameter.Key))
{
throw new ArgumentNullException(nameof(parameters));
continue;
}
foreach (var parameter in parameters)
// Note: the core OAuth 2.0 specification requires that request parameters
// not be present more than once but derived specifications like the
// token exchange specification deliberately allow specifying multiple
// parameters with the same name to represent a multi-valued parameter.
AddParameter(parameter.Key, parameter.Value?.Length switch
{
// Ignore parameters whose name is null or empty.
if (string.IsNullOrEmpty(parameter.Key))
{
continue;
}
// Note: the core OAuth 2.0 specification requires that request parameters
// not be present more than once but derived specifications like the
// token exchange specification deliberately allow specifying multiple
// parameters with the same name to represent a multi-valued parameter.
AddParameter(parameter.Key, parameter.Value.Count switch
{
0 => default,
1 => parameter.Value[0],
_ => parameter.Value.ToArray()
});
}
null or 0 => default,
1 => parameter.Value[0],
_ => parameter.Value
});
}
}
/// <summary>
/// Gets or sets a parameter.
/// </summary>
/// <param name="name">The parameter name.</param>
/// <returns>The parameter value.</returns>
public OpenIddictParameter? this[string name]
/// <summary>
/// Initializes a new OpenIddict message.
/// </summary>
/// <param name="parameters">The message parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictMessage(IEnumerable<KeyValuePair<string, StringValues>> parameters)
{
if (parameters is null)
{
get => GetParameter(name);
set => SetParameter(name, value);
throw new ArgumentNullException(nameof(parameters));
}
/// <summary>
/// Gets the number of parameters contained in the current message.
/// </summary>
public int Count => Parameters.Count;
/// <summary>
/// Gets the dictionary containing the parameters.
/// </summary>
protected Dictionary<string, OpenIddictParameter> Parameters { get; }
= new Dictionary<string, OpenIddictParameter>(StringComparer.Ordinal);
/// <summary>
/// Adds a parameter. Note: an exception is thrown if a parameter with the same name was already added.
/// </summary>
/// <param name="name">The parameter name.</param>
/// <param name="value">The parameter value.</param>
/// <returns>The current instance, which allows chaining calls.</returns>
public OpenIddictMessage AddParameter(string name, OpenIddictParameter value)
foreach (var parameter in parameters)
{
if (string.IsNullOrEmpty(name))
// Ignore parameters whose name is null or empty.
if (string.IsNullOrEmpty(parameter.Key))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0190), nameof(name));
continue;
}
if (Parameters.ContainsKey(name))
// Note: the core OAuth 2.0 specification requires that request parameters
// not be present more than once but derived specifications like the
// token exchange specification deliberately allow specifying multiple
// parameters with the same name to represent a multi-valued parameter.
AddParameter(parameter.Key, parameter.Value.Count switch
{
throw new ArgumentException(SR.GetResourceString(SR.ID0191), nameof(name));
}
0 => default,
1 => parameter.Value[0],
_ => parameter.Value.ToArray()
});
}
}
Parameters.Add(name, value);
/// <summary>
/// Gets or sets a parameter.
/// </summary>
/// <param name="name">The parameter name.</param>
/// <returns>The parameter value.</returns>
public OpenIddictParameter? this[string name]
{
get => GetParameter(name);
set => SetParameter(name, value);
}
return this;
/// <summary>
/// Gets the number of parameters contained in the current message.
/// </summary>
public int Count => Parameters.Count;
/// <summary>
/// Gets the dictionary containing the parameters.
/// </summary>
protected Dictionary<string, OpenIddictParameter> Parameters { get; }
= new Dictionary<string, OpenIddictParameter>(StringComparer.Ordinal);
/// <summary>
/// Adds a parameter. Note: an exception is thrown if a parameter with the same name was already added.
/// </summary>
/// <param name="name">The parameter name.</param>
/// <param name="value">The parameter value.</param>
/// <returns>The current instance, which allows chaining calls.</returns>
public OpenIddictMessage AddParameter(string name, OpenIddictParameter value)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0190), nameof(name));
}
/// <summary>
/// Gets the value corresponding to a given parameter.
/// </summary>
/// <param name="name">The parameter name.</param>
/// <returns>The parameter value, or <c>null</c> if it cannot be found.</returns>
public OpenIddictParameter? GetParameter(string name)
if (Parameters.ContainsKey(name))
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0190), nameof(name));
}
throw new ArgumentException(SR.GetResourceString(SR.ID0191), nameof(name));
}
if (Parameters.TryGetValue(name, out OpenIddictParameter value))
{
return value;
}
Parameters.Add(name, value);
return null;
}
return this;
}
/// <summary>
/// Gets all the parameters associated with this instance.
/// </summary>
/// <returns>The parameters associated with this instance.</returns>
public IReadOnlyDictionary<string, OpenIddictParameter> GetParameters()
=> new ReadOnlyDictionary<string, OpenIddictParameter>(Parameters);
/// <summary>
/// Determines whether the current message contains the specified parameter.
/// </summary>
/// <param name="name">The parameter name.</param>
/// <returns><c>true</c> if the parameter is present, <c>false</c> otherwise.</returns>
public bool HasParameter(string name)
/// <summary>
/// Gets the value corresponding to a given parameter.
/// </summary>
/// <param name="name">The parameter name.</param>
/// <returns>The parameter value, or <c>null</c> if it cannot be found.</returns>
public OpenIddictParameter? GetParameter(string name)
{
if (string.IsNullOrEmpty(name))
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0190), nameof(name));
}
return Parameters.ContainsKey(name);
throw new ArgumentException(SR.GetResourceString(SR.ID0190), nameof(name));
}
/// <summary>
/// Removes a parameter.
/// </summary>
/// <param name="name">The parameter name.</param>
/// <returns>The current instance, which allows chaining calls.</returns>
public OpenIddictMessage RemoveParameter(string name)
if (Parameters.TryGetValue(name, out OpenIddictParameter value))
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0190), nameof(name));
}
return value;
}
Parameters.Remove(name);
return null;
}
return this;
/// <summary>
/// Gets all the parameters associated with this instance.
/// </summary>
/// <returns>The parameters associated with this instance.</returns>
public IReadOnlyDictionary<string, OpenIddictParameter> GetParameters()
=> new ReadOnlyDictionary<string, OpenIddictParameter>(Parameters);
/// <summary>
/// Determines whether the current message contains the specified parameter.
/// </summary>
/// <param name="name">The parameter name.</param>
/// <returns><c>true</c> if the parameter is present, <c>false</c> otherwise.</returns>
public bool HasParameter(string name)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0190), nameof(name));
}
/// <summary>
/// Adds, replaces or removes a parameter.
/// Note: this method automatically removes empty parameters.
/// </summary>
/// <param name="name">The parameter name.</param>
/// <param name="value">The parameter value.</param>
/// <returns>The current instance, which allows chaining calls.</returns>
public OpenIddictMessage SetParameter(string name, OpenIddictParameter? value)
return Parameters.ContainsKey(name);
}
/// <summary>
/// Removes a parameter.
/// </summary>
/// <param name="name">The parameter name.</param>
/// <returns>The current instance, which allows chaining calls.</returns>
public OpenIddictMessage RemoveParameter(string name)
{
if (string.IsNullOrEmpty(name))
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0190), nameof(name));
}
throw new ArgumentException(SR.GetResourceString(SR.ID0190), nameof(name));
}
// If the parameter value is null or empty, remove the corresponding entry from the collection.
if (value is null || OpenIddictParameter.IsNullOrEmpty(value.GetValueOrDefault()))
{
Parameters.Remove(name);
}
Parameters.Remove(name);
else
{
Parameters[name] = value.GetValueOrDefault();
}
return this;
}
return this;
/// <summary>
/// Adds, replaces or removes a parameter.
/// Note: this method automatically removes empty parameters.
/// </summary>
/// <param name="name">The parameter name.</param>
/// <param name="value">The parameter value.</param>
/// <returns>The current instance, which allows chaining calls.</returns>
public OpenIddictMessage SetParameter(string name, OpenIddictParameter? value)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0190), nameof(name));
}
/// <summary>
/// Tries to get the value corresponding to a given parameter.
/// </summary>
/// <param name="name">The parameter name.</param>
/// <param name="value">The parameter value.</param>
/// <returns><c>true</c> if the parameter could be found, <c>false</c> otherwise.</returns>
public bool TryGetParameter(string name, out OpenIddictParameter value)
// If the parameter value is null or empty, remove the corresponding entry from the collection.
if (value is null || OpenIddictParameter.IsNullOrEmpty(value.GetValueOrDefault()))
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0190), nameof(name));
}
return Parameters.TryGetValue(name, out value);
Parameters.Remove(name);
}
/// <summary>
/// Returns a <see cref="string"/> representation of the current instance that can be used in logs.
/// Note: sensitive parameters like client secrets are automatically removed for security reasons.
/// </summary>
/// <returns>The indented JSON representation corresponding to this message.</returns>
public override string ToString()
else
{
using var stream = new MemoryStream();
using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
Indented = true
});
Parameters[name] = value.GetValueOrDefault();
}
writer.WriteStartObject();
return this;
}
foreach (var parameter in Parameters)
{
writer.WritePropertyName(parameter.Key);
// Remove sensitive parameters from the generated payload.
switch (parameter.Key)
{
case OpenIddictConstants.Parameters.AccessToken:
case OpenIddictConstants.Parameters.Assertion:
case OpenIddictConstants.Parameters.ClientAssertion:
case OpenIddictConstants.Parameters.ClientSecret:
case OpenIddictConstants.Parameters.Code:
case OpenIddictConstants.Parameters.IdToken:
case OpenIddictConstants.Parameters.IdTokenHint:
case OpenIddictConstants.Parameters.Password:
case OpenIddictConstants.Parameters.RefreshToken:
case OpenIddictConstants.Parameters.Token:
writer.WriteStringValue("[redacted]");
continue;
}
parameter.Value.WriteTo(writer);
}
/// <summary>
/// Tries to get the value corresponding to a given parameter.
/// </summary>
/// <param name="name">The parameter name.</param>
/// <param name="value">The parameter value.</param>
/// <returns><c>true</c> if the parameter could be found, <c>false</c> otherwise.</returns>
public bool TryGetParameter(string name, out OpenIddictParameter value)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0190), nameof(name));
}
writer.WriteEndObject();
writer.Flush();
return Parameters.TryGetValue(name, out value);
}
return Encoding.UTF8.GetString(stream.ToArray());
}
/// <summary>
/// Returns a <see cref="string"/> representation of the current instance that can be used in logs.
/// Note: sensitive parameters like client secrets are automatically removed for security reasons.
/// </summary>
/// <returns>The indented JSON representation corresponding to this message.</returns>
public override string ToString()
{
using var stream = new MemoryStream();
using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
Indented = true
});
/// <summary>
/// Writes the message to the specified JSON writer.
/// </summary>
/// <param name="writer">The UTF-8 JSON writer.</param>
public void WriteTo(Utf8JsonWriter writer)
writer.WriteStartObject();
foreach (var parameter in Parameters)
{
if (writer is null)
writer.WritePropertyName(parameter.Key);
// Remove sensitive parameters from the generated payload.
switch (parameter.Key)
{
throw new ArgumentNullException(nameof(writer));
case OpenIddictConstants.Parameters.AccessToken:
case OpenIddictConstants.Parameters.Assertion:
case OpenIddictConstants.Parameters.ClientAssertion:
case OpenIddictConstants.Parameters.ClientSecret:
case OpenIddictConstants.Parameters.Code:
case OpenIddictConstants.Parameters.IdToken:
case OpenIddictConstants.Parameters.IdTokenHint:
case OpenIddictConstants.Parameters.Password:
case OpenIddictConstants.Parameters.RefreshToken:
case OpenIddictConstants.Parameters.Token:
writer.WriteStringValue("[redacted]");
continue;
}
writer.WriteStartObject();
parameter.Value.WriteTo(writer);
}
foreach (var parameter in Parameters)
{
writer.WritePropertyName(parameter.Key);
parameter.Value.WriteTo(writer);
}
writer.WriteEndObject();
writer.Flush();
return Encoding.UTF8.GetString(stream.ToArray());
}
writer.WriteEndObject();
/// <summary>
/// Writes the message to the specified JSON writer.
/// </summary>
/// <param name="writer">The UTF-8 JSON writer.</param>
public void WriteTo(Utf8JsonWriter writer)
{
if (writer is null)
{
throw new ArgumentNullException(nameof(writer));
}
writer.WriteStartObject();
foreach (var parameter in Parameters)
{
writer.WritePropertyName(parameter.Key);
parameter.Value.WriteTo(writer);
}
writer.WriteEndObject();
}
}

1335
src/OpenIddict.Abstractions/Primitives/OpenIddictParameter.cs

File diff suppressed because it is too large

875
src/OpenIddict.Abstractions/Primitives/OpenIddictRequest.cs

@ -10,445 +10,444 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Primitives;
namespace OpenIddict.Abstractions
namespace OpenIddict.Abstractions;
/// <summary>
/// Represents a generic OpenIddict request.
/// </summary>
/// <remarks>
/// Security notice: developers instantiating this type are responsible of ensuring that the
/// imported parameters are safe and won't cause the resulting message to grow abnormally,
/// which may result in an excessive memory consumption and a potential denial of service.
/// </remarks>
[DebuggerDisplay("Parameters: {Parameters.Count}")]
[JsonConverter(typeof(OpenIddictConverter))]
public class OpenIddictRequest : OpenIddictMessage
{
/// <summary>
/// Represents a generic OpenIddict request.
/// </summary>
/// <remarks>
/// Security notice: developers instantiating this type are responsible of ensuring that the
/// imported parameters are safe and won't cause the resulting message to grow abnormally,
/// which may result in an excessive memory consumption and a potential denial of service.
/// </remarks>
[DebuggerDisplay("Parameters: {Parameters.Count}")]
[JsonConverter(typeof(OpenIddictConverter))]
public class OpenIddictRequest : OpenIddictMessage
{
/// <summary>
/// Initializes a new OpenIddict request.
/// </summary>
public OpenIddictRequest()
: base()
{
}
/// <summary>
/// Initializes a new OpenIddict request.
/// </summary>
/// <param name="parameters">The request parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictRequest(JsonElement parameters)
: base(parameters)
{
}
/// <summary>
/// Initializes a new OpenIddict request.
/// </summary>
/// <param name="parameters">The request parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictRequest(IEnumerable<KeyValuePair<string, OpenIddictParameter>> parameters)
: base(parameters)
{
}
/// <summary>
/// Initializes a new OpenIddict request.
/// </summary>
/// <param name="parameters">The request parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictRequest(IEnumerable<KeyValuePair<string, string?>> parameters)
: base(parameters)
{
}
/// <summary>
/// Initializes a new OpenIddict request.
/// </summary>
/// <param name="parameters">The request parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictRequest(IEnumerable<KeyValuePair<string, string?[]?>> parameters)
: base(parameters)
{
}
/// <summary>
/// Initializes a new OpenIddict request.
/// </summary>
/// <param name="parameters">The request parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictRequest(IEnumerable<KeyValuePair<string, StringValues>> parameters)
: base(parameters)
{
}
/// <summary>
/// Gets or sets the "access_token" parameter.
/// </summary>
public string? AccessToken
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.AccessToken);
set => SetParameter(OpenIddictConstants.Parameters.AccessToken, value);
}
/// <summary>
/// Gets or sets the "acr_values" parameter.
/// </summary>
public string? AcrValues
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.AcrValues);
set => SetParameter(OpenIddictConstants.Parameters.AcrValues, value);
}
/// <summary>
/// Gets or sets the "assertion" parameter.
/// </summary>
public string? Assertion
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.Assertion);
set => SetParameter(OpenIddictConstants.Parameters.Assertion, value);
}
/// <summary>
/// Gets or sets the "audience" parameters.
/// </summary>
public string[]? Audiences
{
get => (string[]?) GetParameter(OpenIddictConstants.Parameters.Audience);
set => SetParameter(OpenIddictConstants.Parameters.Audience, value);
}
/// <summary>
/// Gets or sets the "claims" parameter.
/// </summary>
public JsonElement Claims
{
get => (JsonElement) GetParameter(OpenIddictConstants.Parameters.Claims);
set => SetParameter(OpenIddictConstants.Parameters.Claims, value);
}
/// <summary>
/// Gets or sets the "claims_locales" parameter.
/// </summary>
public string? ClaimsLocales
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.ClaimsLocales);
set => SetParameter(OpenIddictConstants.Parameters.ClaimsLocales, value);
}
/// <summary>
/// Gets or sets the "client_assertion" parameter.
/// </summary>
public string? ClientAssertion
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.ClientAssertion);
set => SetParameter(OpenIddictConstants.Parameters.ClientAssertion, value);
}
/// <summary>
/// Gets or sets the "client_assertion_type" parameter.
/// </summary>
public string? ClientAssertionType
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.ClientAssertionType);
set => SetParameter(OpenIddictConstants.Parameters.ClientAssertionType, value);
}
/// <summary>
/// Gets or sets the "client_id" parameter.
/// </summary>
public string? ClientId
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.ClientId);
set => SetParameter(OpenIddictConstants.Parameters.ClientId, value);
}
/// <summary>
/// Gets or sets the "client_secret" parameter.
/// </summary>
public string? ClientSecret
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.ClientSecret);
set => SetParameter(OpenIddictConstants.Parameters.ClientSecret, value);
}
/// <summary>
/// Gets or sets the "code" parameter.
/// </summary>
public string? Code
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.Code);
set => SetParameter(OpenIddictConstants.Parameters.Code, value);
}
/// <summary>
/// Gets or sets the "code_challenge" parameter.
/// </summary>
public string? CodeChallenge
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.CodeChallenge);
set => SetParameter(OpenIddictConstants.Parameters.CodeChallenge, value);
}
/// <summary>
/// Gets or sets the "code_challenge_method" parameter.
/// </summary>
public string? CodeChallengeMethod
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.CodeChallengeMethod);
set => SetParameter(OpenIddictConstants.Parameters.CodeChallengeMethod, value);
}
/// <summary>
/// Gets or sets the "code_verifier" parameter.
/// </summary>
public string? CodeVerifier
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.CodeVerifier);
set => SetParameter(OpenIddictConstants.Parameters.CodeVerifier, value);
}
/// <summary>
/// Gets or sets the "device_code" parameter.
/// </summary>
public string? DeviceCode
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.DeviceCode);
set => SetParameter(OpenIddictConstants.Parameters.DeviceCode, value);
}
/// <summary>
/// Gets or sets the "display" parameter.
/// </summary>
public string? Display
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.Display);
set => SetParameter(OpenIddictConstants.Parameters.Display, value);
}
/// <summary>
/// Gets or sets the "grant_type" parameter.
/// </summary>
public string? GrantType
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.GrantType);
set => SetParameter(OpenIddictConstants.Parameters.GrantType, value);
}
/// <summary>
/// Gets or sets the "identity_provider" parameter.
/// </summary>
public string? IdentityProvider
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.IdentityProvider);
set => SetParameter(OpenIddictConstants.Parameters.IdentityProvider, value);
}
/// <summary>
/// Gets or sets the "id_token_hint" parameter.
/// </summary>
public string? IdTokenHint
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.IdTokenHint);
set => SetParameter(OpenIddictConstants.Parameters.IdTokenHint, value);
}
/// <summary>
/// Gets or sets the "login_hint" parameter.
/// </summary>
public string? LoginHint
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.LoginHint);
set => SetParameter(OpenIddictConstants.Parameters.LoginHint, value);
}
/// <summary>
/// Gets or sets the "max_age" parameter.
/// </summary>
public long? MaxAge
{
get => (long?) GetParameter(OpenIddictConstants.Parameters.MaxAge);
set => SetParameter(OpenIddictConstants.Parameters.MaxAge, value);
}
/// <summary>
/// Gets or sets the "nonce" parameter.
/// </summary>
public string? Nonce
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.Nonce);
set => SetParameter(OpenIddictConstants.Parameters.Nonce, value);
}
/// <summary>
/// Gets or sets the "password" parameter.
/// </summary>
public string? Password
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.Password);
set => SetParameter(OpenIddictConstants.Parameters.Password, value);
}
/// <summary>
/// Gets or sets the "post_logout_redirect_uri" parameter.
/// </summary>
public string? PostLogoutRedirectUri
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.PostLogoutRedirectUri);
set => SetParameter(OpenIddictConstants.Parameters.PostLogoutRedirectUri, value);
}
/// <summary>
/// Gets or sets the "prompt" parameter.
/// </summary>
public string? Prompt
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.Prompt);
set => SetParameter(OpenIddictConstants.Parameters.Prompt, value);
}
/// <summary>
/// Gets or sets the "redirect_uri" parameter.
/// </summary>
public string? RedirectUri
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.RedirectUri);
set => SetParameter(OpenIddictConstants.Parameters.RedirectUri, value);
}
/// <summary>
/// Gets or sets the "refresh_token" parameter.
/// </summary>
public string? RefreshToken
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.RefreshToken);
set => SetParameter(OpenIddictConstants.Parameters.RefreshToken, value);
}
/// <summary>
/// Gets or sets the "request" parameter.
/// </summary>
public string? Request
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.Request);
set => SetParameter(OpenIddictConstants.Parameters.Request, value);
}
/// <summary>
/// Gets or sets the "request_id" parameter.
/// </summary>
public string? RequestId
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.RequestId);
set => SetParameter(OpenIddictConstants.Parameters.RequestId, value);
}
/// <summary>
/// Gets or sets the "request_uri" parameter.
/// </summary>
public string? RequestUri
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.RequestUri);
set => SetParameter(OpenIddictConstants.Parameters.RequestUri, value);
}
/// <summary>
/// Gets or sets the "resource" parameters.
/// </summary>
public string[]? Resources
{
get => (string[]?) GetParameter(OpenIddictConstants.Parameters.Resource);
set => SetParameter(OpenIddictConstants.Parameters.Resource, value);
}
/// <summary>
/// Gets or sets the "response_mode" parameter.
/// </summary>
public string? ResponseMode
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.ResponseMode);
set => SetParameter(OpenIddictConstants.Parameters.ResponseMode, value);
}
/// <summary>
/// Gets or sets the "response_type" parameter.
/// </summary>
public string? ResponseType
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.ResponseType);
set => SetParameter(OpenIddictConstants.Parameters.ResponseType, value);
}
/// <summary>
/// Gets or sets the "scope" parameter.
/// </summary>
public string? Scope
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.Scope);
set => SetParameter(OpenIddictConstants.Parameters.Scope, value);
}
/// <summary>
/// Gets or sets the "state" parameter.
/// </summary>
public string? State
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.State);
set => SetParameter(OpenIddictConstants.Parameters.State, value);
}
/// <summary>
/// Gets or sets the "token" parameter.
/// </summary>
public string? Token
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.Token);
set => SetParameter(OpenIddictConstants.Parameters.Token, value);
}
/// <summary>
/// Gets or sets the "token_type_hint" parameter.
/// </summary>
public string? TokenTypeHint
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.TokenTypeHint);
set => SetParameter(OpenIddictConstants.Parameters.TokenTypeHint, value);
}
/// <summary>
/// Gets or sets the "registration" parameter.
/// </summary>
public JsonElement Registration
{
get => (JsonElement) GetParameter(OpenIddictConstants.Parameters.Registration);
set => SetParameter(OpenIddictConstants.Parameters.Registration, value);
}
/// <summary>
/// Gets or sets the "ui_locales" parameter.
/// </summary>
public string? UiLocales
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.UiLocales);
set => SetParameter(OpenIddictConstants.Parameters.UiLocales, value);
}
/// <summary>
/// Gets or sets the "user_code" parameter.
/// </summary>
public string? UserCode
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.UserCode);
set => SetParameter(OpenIddictConstants.Parameters.UserCode, value);
}
/// <summary>
/// Gets or sets the "username" parameter.
/// </summary>
public string? Username
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.Username);
set => SetParameter(OpenIddictConstants.Parameters.Username, value);
}
/// Initializes a new OpenIddict request.
/// </summary>
public OpenIddictRequest()
: base()
{
}
/// <summary>
/// Initializes a new OpenIddict request.
/// </summary>
/// <param name="parameters">The request parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictRequest(JsonElement parameters)
: base(parameters)
{
}
/// <summary>
/// Initializes a new OpenIddict request.
/// </summary>
/// <param name="parameters">The request parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictRequest(IEnumerable<KeyValuePair<string, OpenIddictParameter>> parameters)
: base(parameters)
{
}
/// <summary>
/// Initializes a new OpenIddict request.
/// </summary>
/// <param name="parameters">The request parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictRequest(IEnumerable<KeyValuePair<string, string?>> parameters)
: base(parameters)
{
}
/// <summary>
/// Initializes a new OpenIddict request.
/// </summary>
/// <param name="parameters">The request parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictRequest(IEnumerable<KeyValuePair<string, string?[]?>> parameters)
: base(parameters)
{
}
/// <summary>
/// Initializes a new OpenIddict request.
/// </summary>
/// <param name="parameters">The request parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictRequest(IEnumerable<KeyValuePair<string, StringValues>> parameters)
: base(parameters)
{
}
/// <summary>
/// Gets or sets the "access_token" parameter.
/// </summary>
public string? AccessToken
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.AccessToken);
set => SetParameter(OpenIddictConstants.Parameters.AccessToken, value);
}
/// <summary>
/// Gets or sets the "acr_values" parameter.
/// </summary>
public string? AcrValues
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.AcrValues);
set => SetParameter(OpenIddictConstants.Parameters.AcrValues, value);
}
/// <summary>
/// Gets or sets the "assertion" parameter.
/// </summary>
public string? Assertion
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.Assertion);
set => SetParameter(OpenIddictConstants.Parameters.Assertion, value);
}
/// <summary>
/// Gets or sets the "audience" parameters.
/// </summary>
public string[]? Audiences
{
get => (string[]?) GetParameter(OpenIddictConstants.Parameters.Audience);
set => SetParameter(OpenIddictConstants.Parameters.Audience, value);
}
/// <summary>
/// Gets or sets the "claims" parameter.
/// </summary>
public JsonElement Claims
{
get => (JsonElement) GetParameter(OpenIddictConstants.Parameters.Claims);
set => SetParameter(OpenIddictConstants.Parameters.Claims, value);
}
/// <summary>
/// Gets or sets the "claims_locales" parameter.
/// </summary>
public string? ClaimsLocales
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.ClaimsLocales);
set => SetParameter(OpenIddictConstants.Parameters.ClaimsLocales, value);
}
/// <summary>
/// Gets or sets the "client_assertion" parameter.
/// </summary>
public string? ClientAssertion
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.ClientAssertion);
set => SetParameter(OpenIddictConstants.Parameters.ClientAssertion, value);
}
/// <summary>
/// Gets or sets the "client_assertion_type" parameter.
/// </summary>
public string? ClientAssertionType
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.ClientAssertionType);
set => SetParameter(OpenIddictConstants.Parameters.ClientAssertionType, value);
}
/// <summary>
/// Gets or sets the "client_id" parameter.
/// </summary>
public string? ClientId
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.ClientId);
set => SetParameter(OpenIddictConstants.Parameters.ClientId, value);
}
/// <summary>
/// Gets or sets the "client_secret" parameter.
/// </summary>
public string? ClientSecret
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.ClientSecret);
set => SetParameter(OpenIddictConstants.Parameters.ClientSecret, value);
}
/// <summary>
/// Gets or sets the "code" parameter.
/// </summary>
public string? Code
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.Code);
set => SetParameter(OpenIddictConstants.Parameters.Code, value);
}
/// <summary>
/// Gets or sets the "code_challenge" parameter.
/// </summary>
public string? CodeChallenge
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.CodeChallenge);
set => SetParameter(OpenIddictConstants.Parameters.CodeChallenge, value);
}
/// <summary>
/// Gets or sets the "code_challenge_method" parameter.
/// </summary>
public string? CodeChallengeMethod
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.CodeChallengeMethod);
set => SetParameter(OpenIddictConstants.Parameters.CodeChallengeMethod, value);
}
/// <summary>
/// Gets or sets the "code_verifier" parameter.
/// </summary>
public string? CodeVerifier
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.CodeVerifier);
set => SetParameter(OpenIddictConstants.Parameters.CodeVerifier, value);
}
/// <summary>
/// Gets or sets the "device_code" parameter.
/// </summary>
public string? DeviceCode
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.DeviceCode);
set => SetParameter(OpenIddictConstants.Parameters.DeviceCode, value);
}
/// <summary>
/// Gets or sets the "display" parameter.
/// </summary>
public string? Display
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.Display);
set => SetParameter(OpenIddictConstants.Parameters.Display, value);
}
/// <summary>
/// Gets or sets the "grant_type" parameter.
/// </summary>
public string? GrantType
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.GrantType);
set => SetParameter(OpenIddictConstants.Parameters.GrantType, value);
}
/// <summary>
/// Gets or sets the "identity_provider" parameter.
/// </summary>
public string? IdentityProvider
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.IdentityProvider);
set => SetParameter(OpenIddictConstants.Parameters.IdentityProvider, value);
}
/// <summary>
/// Gets or sets the "id_token_hint" parameter.
/// </summary>
public string? IdTokenHint
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.IdTokenHint);
set => SetParameter(OpenIddictConstants.Parameters.IdTokenHint, value);
}
/// <summary>
/// Gets or sets the "login_hint" parameter.
/// </summary>
public string? LoginHint
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.LoginHint);
set => SetParameter(OpenIddictConstants.Parameters.LoginHint, value);
}
/// <summary>
/// Gets or sets the "max_age" parameter.
/// </summary>
public long? MaxAge
{
get => (long?) GetParameter(OpenIddictConstants.Parameters.MaxAge);
set => SetParameter(OpenIddictConstants.Parameters.MaxAge, value);
}
/// <summary>
/// Gets or sets the "nonce" parameter.
/// </summary>
public string? Nonce
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.Nonce);
set => SetParameter(OpenIddictConstants.Parameters.Nonce, value);
}
/// <summary>
/// Gets or sets the "password" parameter.
/// </summary>
public string? Password
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.Password);
set => SetParameter(OpenIddictConstants.Parameters.Password, value);
}
/// <summary>
/// Gets or sets the "post_logout_redirect_uri" parameter.
/// </summary>
public string? PostLogoutRedirectUri
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.PostLogoutRedirectUri);
set => SetParameter(OpenIddictConstants.Parameters.PostLogoutRedirectUri, value);
}
/// <summary>
/// Gets or sets the "prompt" parameter.
/// </summary>
public string? Prompt
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.Prompt);
set => SetParameter(OpenIddictConstants.Parameters.Prompt, value);
}
/// <summary>
/// Gets or sets the "redirect_uri" parameter.
/// </summary>
public string? RedirectUri
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.RedirectUri);
set => SetParameter(OpenIddictConstants.Parameters.RedirectUri, value);
}
/// <summary>
/// Gets or sets the "refresh_token" parameter.
/// </summary>
public string? RefreshToken
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.RefreshToken);
set => SetParameter(OpenIddictConstants.Parameters.RefreshToken, value);
}
/// <summary>
/// Gets or sets the "request" parameter.
/// </summary>
public string? Request
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.Request);
set => SetParameter(OpenIddictConstants.Parameters.Request, value);
}
/// <summary>
/// Gets or sets the "request_id" parameter.
/// </summary>
public string? RequestId
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.RequestId);
set => SetParameter(OpenIddictConstants.Parameters.RequestId, value);
}
/// <summary>
/// Gets or sets the "request_uri" parameter.
/// </summary>
public string? RequestUri
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.RequestUri);
set => SetParameter(OpenIddictConstants.Parameters.RequestUri, value);
}
/// <summary>
/// Gets or sets the "resource" parameters.
/// </summary>
public string[]? Resources
{
get => (string[]?) GetParameter(OpenIddictConstants.Parameters.Resource);
set => SetParameter(OpenIddictConstants.Parameters.Resource, value);
}
/// <summary>
/// Gets or sets the "response_mode" parameter.
/// </summary>
public string? ResponseMode
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.ResponseMode);
set => SetParameter(OpenIddictConstants.Parameters.ResponseMode, value);
}
/// <summary>
/// Gets or sets the "response_type" parameter.
/// </summary>
public string? ResponseType
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.ResponseType);
set => SetParameter(OpenIddictConstants.Parameters.ResponseType, value);
}
/// <summary>
/// Gets or sets the "scope" parameter.
/// </summary>
public string? Scope
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.Scope);
set => SetParameter(OpenIddictConstants.Parameters.Scope, value);
}
/// <summary>
/// Gets or sets the "state" parameter.
/// </summary>
public string? State
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.State);
set => SetParameter(OpenIddictConstants.Parameters.State, value);
}
/// <summary>
/// Gets or sets the "token" parameter.
/// </summary>
public string? Token
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.Token);
set => SetParameter(OpenIddictConstants.Parameters.Token, value);
}
/// <summary>
/// Gets or sets the "token_type_hint" parameter.
/// </summary>
public string? TokenTypeHint
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.TokenTypeHint);
set => SetParameter(OpenIddictConstants.Parameters.TokenTypeHint, value);
}
/// <summary>
/// Gets or sets the "registration" parameter.
/// </summary>
public JsonElement Registration
{
get => (JsonElement) GetParameter(OpenIddictConstants.Parameters.Registration);
set => SetParameter(OpenIddictConstants.Parameters.Registration, value);
}
/// <summary>
/// Gets or sets the "ui_locales" parameter.
/// </summary>
public string? UiLocales
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.UiLocales);
set => SetParameter(OpenIddictConstants.Parameters.UiLocales, value);
}
/// <summary>
/// Gets or sets the "user_code" parameter.
/// </summary>
public string? UserCode
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.UserCode);
set => SetParameter(OpenIddictConstants.Parameters.UserCode, value);
}
/// <summary>
/// Gets or sets the "username" parameter.
/// </summary>
public string? Username
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.Username);
set => SetParameter(OpenIddictConstants.Parameters.Username, value);
}
}

371
src/OpenIddict.Abstractions/Primitives/OpenIddictResponse.cs

@ -10,193 +10,192 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Primitives;
namespace OpenIddict.Abstractions
namespace OpenIddict.Abstractions;
/// <summary>
/// Represents a generic OpenIddict response.
/// </summary>
/// <remarks>
/// Security notice: developers instantiating this type are responsible of ensuring that the
/// imported parameters are safe and won't cause the resulting message to grow abnormally,
/// which may result in an excessive memory consumption and a potential denial of service.
/// </remarks>
[DebuggerDisplay("Parameters: {Parameters.Count}")]
[JsonConverter(typeof(OpenIddictConverter))]
public class OpenIddictResponse : OpenIddictMessage
{
/// <summary>
/// Represents a generic OpenIddict response.
/// </summary>
/// <remarks>
/// Security notice: developers instantiating this type are responsible of ensuring that the
/// imported parameters are safe and won't cause the resulting message to grow abnormally,
/// which may result in an excessive memory consumption and a potential denial of service.
/// </remarks>
[DebuggerDisplay("Parameters: {Parameters.Count}")]
[JsonConverter(typeof(OpenIddictConverter))]
public class OpenIddictResponse : OpenIddictMessage
{
/// <summary>
/// Initializes a new OpenIddict response.
/// </summary>
public OpenIddictResponse()
: base()
{
}
/// <summary>
/// Initializes a new OpenIddict response.
/// </summary>
/// <param name="parameters">The response parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictResponse(JsonElement parameters)
: base(parameters)
{
}
/// <summary>
/// Initializes a new OpenIddict response.
/// </summary>
/// <param name="parameters">The response parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictResponse(IEnumerable<KeyValuePair<string, OpenIddictParameter>> parameters)
: base(parameters)
{
}
/// <summary>
/// Initializes a new OpenIddict response.
/// </summary>
/// <param name="parameters">The response parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictResponse(IEnumerable<KeyValuePair<string, string?>> parameters)
: base(parameters)
{
}
/// <summary>
/// Initializes a new OpenIddict response.
/// </summary>
/// <param name="parameters">The response parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictResponse(IEnumerable<KeyValuePair<string, string?[]?>> parameters)
: base(parameters)
{
}
/// <summary>
/// Initializes a new OpenIddict response.
/// </summary>
/// <param name="parameters">The response parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictResponse(IEnumerable<KeyValuePair<string, StringValues>> parameters)
: base(parameters)
{
}
/// <summary>
/// Gets or sets the "access_token" parameter.
/// </summary>
public string? AccessToken
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.AccessToken);
set => SetParameter(OpenIddictConstants.Parameters.AccessToken, value);
}
/// <summary>
/// Gets or sets the "code" parameter.
/// </summary>
public string? Code
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.Code);
set => SetParameter(OpenIddictConstants.Parameters.Code, value);
}
/// <summary>
/// Gets or sets the "device_code" parameter.
/// </summary>
public string? DeviceCode
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.DeviceCode);
set => SetParameter(OpenIddictConstants.Parameters.DeviceCode, value);
}
/// <summary>
/// Gets or sets the "error" parameter.
/// </summary>
public string? Error
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.Error);
set => SetParameter(OpenIddictConstants.Parameters.Error, value);
}
/// <summary>
/// Gets or sets the "error_description" parameter.
/// </summary>
public string? ErrorDescription
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.ErrorDescription);
set => SetParameter(OpenIddictConstants.Parameters.ErrorDescription, value);
}
/// <summary>
/// Gets or sets the "error_uri" parameter.
/// </summary>
public string? ErrorUri
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.ErrorUri);
set => SetParameter(OpenIddictConstants.Parameters.ErrorUri, value);
}
/// <summary>
/// Gets or sets the "expires_in" parameter.
/// </summary>
public long? ExpiresIn
{
get => (long?) GetParameter(OpenIddictConstants.Parameters.ExpiresIn);
set => SetParameter(OpenIddictConstants.Parameters.ExpiresIn, value);
}
/// <summary>
/// Gets or sets the "id_token" parameter.
/// </summary>
public string? IdToken
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.IdToken);
set => SetParameter(OpenIddictConstants.Parameters.IdToken, value);
}
/// <summary>
/// Gets or sets the "refresh_token" parameter.
/// </summary>
public string? RefreshToken
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.RefreshToken);
set => SetParameter(OpenIddictConstants.Parameters.RefreshToken, value);
}
/// <summary>
/// Gets or sets the "scope" parameter.
/// </summary>
public string? Scope
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.Scope);
set => SetParameter(OpenIddictConstants.Parameters.Scope, value);
}
/// <summary>
/// Gets or sets the "state" parameter.
/// </summary>
public string? State
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.State);
set => SetParameter(OpenIddictConstants.Parameters.State, value);
}
/// <summary>
/// Gets or sets the "token_type" parameter.
/// </summary>
public string? TokenType
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.TokenType);
set => SetParameter(OpenIddictConstants.Parameters.TokenType, value);
}
/// <summary>
/// Gets or sets the "user_code" parameter.
/// </summary>
public string? UserCode
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.UserCode);
set => SetParameter(OpenIddictConstants.Parameters.UserCode, value);
}
/// Initializes a new OpenIddict response.
/// </summary>
public OpenIddictResponse()
: base()
{
}
/// <summary>
/// Initializes a new OpenIddict response.
/// </summary>
/// <param name="parameters">The response parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictResponse(JsonElement parameters)
: base(parameters)
{
}
/// <summary>
/// Initializes a new OpenIddict response.
/// </summary>
/// <param name="parameters">The response parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictResponse(IEnumerable<KeyValuePair<string, OpenIddictParameter>> parameters)
: base(parameters)
{
}
/// <summary>
/// Initializes a new OpenIddict response.
/// </summary>
/// <param name="parameters">The response parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictResponse(IEnumerable<KeyValuePair<string, string?>> parameters)
: base(parameters)
{
}
/// <summary>
/// Initializes a new OpenIddict response.
/// </summary>
/// <param name="parameters">The response parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictResponse(IEnumerable<KeyValuePair<string, string?[]?>> parameters)
: base(parameters)
{
}
/// <summary>
/// Initializes a new OpenIddict response.
/// </summary>
/// <param name="parameters">The response parameters.</param>
/// <remarks>Parameters with a null or empty key are always ignored.</remarks>
public OpenIddictResponse(IEnumerable<KeyValuePair<string, StringValues>> parameters)
: base(parameters)
{
}
/// <summary>
/// Gets or sets the "access_token" parameter.
/// </summary>
public string? AccessToken
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.AccessToken);
set => SetParameter(OpenIddictConstants.Parameters.AccessToken, value);
}
/// <summary>
/// Gets or sets the "code" parameter.
/// </summary>
public string? Code
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.Code);
set => SetParameter(OpenIddictConstants.Parameters.Code, value);
}
/// <summary>
/// Gets or sets the "device_code" parameter.
/// </summary>
public string? DeviceCode
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.DeviceCode);
set => SetParameter(OpenIddictConstants.Parameters.DeviceCode, value);
}
/// <summary>
/// Gets or sets the "error" parameter.
/// </summary>
public string? Error
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.Error);
set => SetParameter(OpenIddictConstants.Parameters.Error, value);
}
/// <summary>
/// Gets or sets the "error_description" parameter.
/// </summary>
public string? ErrorDescription
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.ErrorDescription);
set => SetParameter(OpenIddictConstants.Parameters.ErrorDescription, value);
}
/// <summary>
/// Gets or sets the "error_uri" parameter.
/// </summary>
public string? ErrorUri
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.ErrorUri);
set => SetParameter(OpenIddictConstants.Parameters.ErrorUri, value);
}
/// <summary>
/// Gets or sets the "expires_in" parameter.
/// </summary>
public long? ExpiresIn
{
get => (long?) GetParameter(OpenIddictConstants.Parameters.ExpiresIn);
set => SetParameter(OpenIddictConstants.Parameters.ExpiresIn, value);
}
/// <summary>
/// Gets or sets the "id_token" parameter.
/// </summary>
public string? IdToken
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.IdToken);
set => SetParameter(OpenIddictConstants.Parameters.IdToken, value);
}
/// <summary>
/// Gets or sets the "refresh_token" parameter.
/// </summary>
public string? RefreshToken
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.RefreshToken);
set => SetParameter(OpenIddictConstants.Parameters.RefreshToken, value);
}
/// <summary>
/// Gets or sets the "scope" parameter.
/// </summary>
public string? Scope
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.Scope);
set => SetParameter(OpenIddictConstants.Parameters.Scope, value);
}
/// <summary>
/// Gets or sets the "state" parameter.
/// </summary>
public string? State
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.State);
set => SetParameter(OpenIddictConstants.Parameters.State, value);
}
/// <summary>
/// Gets or sets the "token_type" parameter.
/// </summary>
public string? TokenType
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.TokenType);
set => SetParameter(OpenIddictConstants.Parameters.TokenType, value);
}
/// <summary>
/// Gets or sets the "user_code" parameter.
/// </summary>
public string? UserCode
{
get => (string?) GetParameter(OpenIddictConstants.Parameters.UserCode);
set => SetParameter(OpenIddictConstants.Parameters.UserCode, value);
}
}

23
src/OpenIddict.Abstractions/Resolvers/IOpenIddictApplicationStoreResolver.cs

@ -1,18 +1,17 @@
using System;
namespace OpenIddict.Abstractions
namespace OpenIddict.Abstractions;
/// <summary>
/// Exposes a method allowing to resolve an application store.
/// </summary>
public interface IOpenIddictApplicationStoreResolver
{
/// <summary>
/// Exposes a method allowing to resolve an application store.
/// Returns an application store compatible with the specified application type or throws an
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
/// </summary>
public interface IOpenIddictApplicationStoreResolver
{
/// <summary>
/// Returns an application store compatible with the specified application type or throws an
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
/// </summary>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <returns>An <see cref="IOpenIddictApplicationStore{TApplication}"/>.</returns>
IOpenIddictApplicationStore<TApplication> Get<TApplication>() where TApplication : class;
}
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <returns>An <see cref="IOpenIddictApplicationStore{TApplication}"/>.</returns>
IOpenIddictApplicationStore<TApplication> Get<TApplication>() where TApplication : class;
}

23
src/OpenIddict.Abstractions/Resolvers/IOpenIddictAuthorizationStoreResolver.cs

@ -1,18 +1,17 @@
using System;
namespace OpenIddict.Abstractions
namespace OpenIddict.Abstractions;
/// <summary>
/// Exposes a method allowing to resolve an authorization store.
/// </summary>
public interface IOpenIddictAuthorizationStoreResolver
{
/// <summary>
/// Exposes a method allowing to resolve an authorization store.
/// Returns an authorization store compatible with the specified authorization type or throws an
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
/// </summary>
public interface IOpenIddictAuthorizationStoreResolver
{
/// <summary>
/// Returns an authorization store compatible with the specified authorization type or throws an
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
/// </summary>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <returns>An <see cref="IOpenIddictAuthorizationStore{TAuthorization}"/>.</returns>
IOpenIddictAuthorizationStore<TAuthorization> Get<TAuthorization>() where TAuthorization : class;
}
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <returns>An <see cref="IOpenIddictAuthorizationStore{TAuthorization}"/>.</returns>
IOpenIddictAuthorizationStore<TAuthorization> Get<TAuthorization>() where TAuthorization : class;
}

23
src/OpenIddict.Abstractions/Resolvers/IOpenIddictScopeStoreResolver.cs

@ -1,18 +1,17 @@
using System;
namespace OpenIddict.Abstractions
namespace OpenIddict.Abstractions;
/// <summary>
/// Exposes a method allowing to resolve a scope store.
/// </summary>
public interface IOpenIddictScopeStoreResolver
{
/// <summary>
/// Exposes a method allowing to resolve a scope store.
/// Returns a scope store compatible with the specified scope type or throws an
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
/// </summary>
public interface IOpenIddictScopeStoreResolver
{
/// <summary>
/// Returns a scope store compatible with the specified scope type or throws an
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
/// </summary>
/// <typeparam name="TScope">The type of the Scope entity.</typeparam>
/// <returns>An <see cref="IOpenIddictScopeStore{TScope}"/>.</returns>
IOpenIddictScopeStore<TScope> Get<TScope>() where TScope : class;
}
/// <typeparam name="TScope">The type of the Scope entity.</typeparam>
/// <returns>An <see cref="IOpenIddictScopeStore{TScope}"/>.</returns>
IOpenIddictScopeStore<TScope> Get<TScope>() where TScope : class;
}

23
src/OpenIddict.Abstractions/Resolvers/IOpenIddictTokenStoreResolver.cs

@ -1,18 +1,17 @@
using System;
namespace OpenIddict.Abstractions
namespace OpenIddict.Abstractions;
/// <summary>
/// Exposes a method allowing to resolve a token store.
/// </summary>
public interface IOpenIddictTokenStoreResolver
{
/// <summary>
/// Exposes a method allowing to resolve a token store.
/// Returns a token store compatible with the specified token type or throws an
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
/// </summary>
public interface IOpenIddictTokenStoreResolver
{
/// <summary>
/// Returns a token store compatible with the specified token type or throws an
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
/// </summary>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <returns>An <see cref="IOpenIddictTokenStore{TToken}"/>.</returns>
IOpenIddictTokenStore<TToken> Get<TToken>() where TToken : class;
}
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <returns>An <see cref="IOpenIddictTokenStore{TToken}"/>.</returns>
IOpenIddictTokenStore<TToken> Get<TToken>() where TToken : class;
}

755
src/OpenIddict.Abstractions/Stores/IOpenIddictApplicationStore.cs

@ -13,383 +13,382 @@ using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
namespace OpenIddict.Abstractions
namespace OpenIddict.Abstractions;
/// <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>
/// 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>
/// Determines the number of applications that exist in the database.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of applications in the database.
/// </returns>
ValueTask<long> CountAsync(CancellationToken cancellationToken);
/// <summary>
/// Determines the number of applications that match the specified query.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of applications that match the specified query.
/// </returns>
ValueTask<long> CountAsync<TResult>(Func<IQueryable<TApplication>, IQueryable<TResult>> query, CancellationToken cancellationToken);
/// <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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask CreateAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Removes an existing application.
/// </summary>
/// <param name="application">The application to delete.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask DeleteAsync(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="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client application corresponding to the identifier.
/// </returns>
ValueTask<TApplication?> FindByIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves an application using its client identifier.
/// </summary>
/// <param name="identifier">The client 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="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client application corresponding to the identifier.
/// </returns>
ValueTask<TApplication?> FindByClientIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves all the applications associated with the specified post_logout_redirect_uri.
/// </summary>
/// <param name="address">The post_logout_redirect_uri associated with the applications.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The client applications corresponding to the specified post_logout_redirect_uri.</returns>
IAsyncEnumerable<TApplication> FindByPostLogoutRedirectUriAsync(string address, CancellationToken cancellationToken);
/// <summary>
/// Retrieves all the applications associated with the specified redirect_uri.
/// </summary>
/// <param name="address">The redirect_uri associated with the applications.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The client applications corresponding to the specified redirect_uri.</returns>
IAsyncEnumerable<TApplication> FindByRedirectUriAsync(string address, CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the first element returned when executing the query.
/// </returns>
ValueTask<TResult?> GetAsync<TState, TResult>(
Func<IQueryable<TApplication>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the client identifier 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client identifier associated with the application.
/// </returns>
ValueTask<string?> GetClientIdAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the client secret associated with an application.
/// Note: depending on the manager used to create the application,
/// the client secret may be hashed for security reasons.
/// </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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client secret associated with the application.
/// </returns>
ValueTask<string?> GetClientSecretAsync(TApplication application, 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client type of the application (by default, "public").
/// </returns>
ValueTask<string?> GetClientTypeAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the consent 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the consent type of the application (by default, "explicit").
/// </returns>
ValueTask<string?> GetConsentTypeAsync(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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the display name associated with the application.
/// </returns>
ValueTask<string?> GetDisplayNameAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the localized display names 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the localized display names associated with the application.
/// </returns>
ValueTask<ImmutableDictionary<CultureInfo, string>> GetDisplayNamesAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the unique identifier 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the application.
/// </returns>
ValueTask<string?> GetIdAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the permissions 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the permissions associated with the application.
/// </returns>
ValueTask<ImmutableArray<string>> GetPermissionsAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the logout callback addresses 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the post_logout_redirect_uri associated with the application.
/// </returns>
ValueTask<ImmutableArray<string>> GetPostLogoutRedirectUrisAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the additional properties 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the application.
/// </returns>
ValueTask<ImmutableDictionary<string, JsonElement>> GetPropertiesAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the callback addresses 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the redirect_uri associated with the application.
/// </returns>
ValueTask<ImmutableArray<string>> GetRedirectUrisAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the requirements 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the requirements associated with the application.
/// </returns>
ValueTask<ImmutableArray<string>> GetRequirementsAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Instantiates a new application.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the instantiated application, that can be persisted in the database.
/// </returns>
ValueTask<TApplication> InstantiateAsync(CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <param name="count">The number of results to return.</param>
/// <param name="offset">The number of results to skip.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TApplication> ListAsync(int? count, int? offset, CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TResult> ListAsync<TState, TResult>(
Func<IQueryable<TApplication>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken);
/// <summary>
/// Sets the client identifier associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="identifier">The client 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetClientIdAsync(TApplication application, string? identifier, CancellationToken cancellationToken);
/// <summary>
/// Sets the client secret associated with an application.
/// Note: depending on the manager used to create the application,
/// the client secret may be hashed for security reasons.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="secret">The client secret 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetClientSecretAsync(TApplication application, string? secret, CancellationToken cancellationToken);
/// <summary>
/// Sets the client type associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="type">The client type 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetClientTypeAsync(TApplication application, string? type, CancellationToken cancellationToken);
/// <summary>
/// Sets the consent type associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="type">The consent type 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetConsentTypeAsync(TApplication application, string? type, CancellationToken cancellationToken);
/// <summary>
/// Sets the display name associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="name">The display name 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetDisplayNameAsync(TApplication application, string? name, CancellationToken cancellationToken);
/// <summary>
/// Sets the localized display names associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="names">The localized display names 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetDisplayNamesAsync(TApplication application,
ImmutableDictionary<CultureInfo, string> names, CancellationToken cancellationToken);
/// <summary>
/// Sets the permissions associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="permissions">The permissions 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetPermissionsAsync(TApplication application, ImmutableArray<string> permissions, CancellationToken cancellationToken);
/// <summary>
/// Sets the logout callback addresses associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="addresses">The logout callback addresses 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetPostLogoutRedirectUrisAsync(TApplication application,
ImmutableArray<string> addresses, CancellationToken cancellationToken);
/// <summary>
/// Sets the additional properties associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="properties">The additional properties 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetPropertiesAsync(TApplication application,
ImmutableDictionary<string, JsonElement> properties, CancellationToken cancellationToken);
/// <summary>
/// Sets the callback addresses associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="addresses">The callback addresses 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetRedirectUrisAsync(TApplication application,
ImmutableArray<string> addresses, CancellationToken cancellationToken);
/// <summary>
/// Sets the requirements associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="requirements">The requirements 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetRequirementsAsync(TApplication application, ImmutableArray<string> requirements, CancellationToken cancellationToken);
/// <summary>
/// Updates an existing application.
/// </summary>
/// <param name="application">The application to update.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask UpdateAsync(TApplication application, CancellationToken cancellationToken);
}
}
/// Determines the number of applications that exist in the database.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of applications in the database.
/// </returns>
ValueTask<long> CountAsync(CancellationToken cancellationToken);
/// <summary>
/// Determines the number of applications that match the specified query.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of applications that match the specified query.
/// </returns>
ValueTask<long> CountAsync<TResult>(Func<IQueryable<TApplication>, IQueryable<TResult>> query, CancellationToken cancellationToken);
/// <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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask CreateAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Removes an existing application.
/// </summary>
/// <param name="application">The application to delete.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask DeleteAsync(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="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client application corresponding to the identifier.
/// </returns>
ValueTask<TApplication?> FindByIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves an application using its client identifier.
/// </summary>
/// <param name="identifier">The client 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="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client application corresponding to the identifier.
/// </returns>
ValueTask<TApplication?> FindByClientIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves all the applications associated with the specified post_logout_redirect_uri.
/// </summary>
/// <param name="address">The post_logout_redirect_uri associated with the applications.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The client applications corresponding to the specified post_logout_redirect_uri.</returns>
IAsyncEnumerable<TApplication> FindByPostLogoutRedirectUriAsync(string address, CancellationToken cancellationToken);
/// <summary>
/// Retrieves all the applications associated with the specified redirect_uri.
/// </summary>
/// <param name="address">The redirect_uri associated with the applications.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The client applications corresponding to the specified redirect_uri.</returns>
IAsyncEnumerable<TApplication> FindByRedirectUriAsync(string address, CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the first element returned when executing the query.
/// </returns>
ValueTask<TResult?> GetAsync<TState, TResult>(
Func<IQueryable<TApplication>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the client identifier 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client identifier associated with the application.
/// </returns>
ValueTask<string?> GetClientIdAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the client secret associated with an application.
/// Note: depending on the manager used to create the application,
/// the client secret may be hashed for security reasons.
/// </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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client secret associated with the application.
/// </returns>
ValueTask<string?> GetClientSecretAsync(TApplication application, 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client type of the application (by default, "public").
/// </returns>
ValueTask<string?> GetClientTypeAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the consent 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the consent type of the application (by default, "explicit").
/// </returns>
ValueTask<string?> GetConsentTypeAsync(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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the display name associated with the application.
/// </returns>
ValueTask<string?> GetDisplayNameAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the localized display names 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the localized display names associated with the application.
/// </returns>
ValueTask<ImmutableDictionary<CultureInfo, string>> GetDisplayNamesAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the unique identifier 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the application.
/// </returns>
ValueTask<string?> GetIdAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the permissions 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the permissions associated with the application.
/// </returns>
ValueTask<ImmutableArray<string>> GetPermissionsAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the logout callback addresses 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the post_logout_redirect_uri associated with the application.
/// </returns>
ValueTask<ImmutableArray<string>> GetPostLogoutRedirectUrisAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the additional properties 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the application.
/// </returns>
ValueTask<ImmutableDictionary<string, JsonElement>> GetPropertiesAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the callback addresses 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the redirect_uri associated with the application.
/// </returns>
ValueTask<ImmutableArray<string>> GetRedirectUrisAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the requirements 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="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the requirements associated with the application.
/// </returns>
ValueTask<ImmutableArray<string>> GetRequirementsAsync(TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Instantiates a new application.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the instantiated application, that can be persisted in the database.
/// </returns>
ValueTask<TApplication> InstantiateAsync(CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <param name="count">The number of results to return.</param>
/// <param name="offset">The number of results to skip.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TApplication> ListAsync(int? count, int? offset, CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TResult> ListAsync<TState, TResult>(
Func<IQueryable<TApplication>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken);
/// <summary>
/// Sets the client identifier associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="identifier">The client 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetClientIdAsync(TApplication application, string? identifier, CancellationToken cancellationToken);
/// <summary>
/// Sets the client secret associated with an application.
/// Note: depending on the manager used to create the application,
/// the client secret may be hashed for security reasons.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="secret">The client secret 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetClientSecretAsync(TApplication application, string? secret, CancellationToken cancellationToken);
/// <summary>
/// Sets the client type associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="type">The client type 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetClientTypeAsync(TApplication application, string? type, CancellationToken cancellationToken);
/// <summary>
/// Sets the consent type associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="type">The consent type 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetConsentTypeAsync(TApplication application, string? type, CancellationToken cancellationToken);
/// <summary>
/// Sets the display name associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="name">The display name 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetDisplayNameAsync(TApplication application, string? name, CancellationToken cancellationToken);
/// <summary>
/// Sets the localized display names associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="names">The localized display names 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetDisplayNamesAsync(TApplication application,
ImmutableDictionary<CultureInfo, string> names, CancellationToken cancellationToken);
/// <summary>
/// Sets the permissions associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="permissions">The permissions 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetPermissionsAsync(TApplication application, ImmutableArray<string> permissions, CancellationToken cancellationToken);
/// <summary>
/// Sets the logout callback addresses associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="addresses">The logout callback addresses 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetPostLogoutRedirectUrisAsync(TApplication application,
ImmutableArray<string> addresses, CancellationToken cancellationToken);
/// <summary>
/// Sets the additional properties associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="properties">The additional properties 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetPropertiesAsync(TApplication application,
ImmutableDictionary<string, JsonElement> properties, CancellationToken cancellationToken);
/// <summary>
/// Sets the callback addresses associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="addresses">The callback addresses 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetRedirectUrisAsync(TApplication application,
ImmutableArray<string> addresses, CancellationToken cancellationToken);
/// <summary>
/// Sets the requirements associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="requirements">The requirements 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetRequirementsAsync(TApplication application, ImmutableArray<string> requirements, CancellationToken cancellationToken);
/// <summary>
/// Updates an existing application.
/// </summary>
/// <param name="application">The application to update.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask UpdateAsync(TApplication application, CancellationToken cancellationToken);
}

687
src/OpenIddict.Abstractions/Stores/IOpenIddictAuthorizationStore.cs

@ -12,349 +12,348 @@ using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
namespace OpenIddict.Abstractions
namespace OpenIddict.Abstractions;
/// <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
{
/// <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
{
/// <summary>
/// Determines the number of authorizations that exist in the database.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of authorizations in the database.
/// </returns>
ValueTask<long> CountAsync(CancellationToken cancellationToken);
/// <summary>
/// Determines the number of authorizations that match the specified query.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of authorizations that match the specified query.
/// </returns>
ValueTask<long> CountAsync<TResult>(Func<IQueryable<TAuthorization>, IQueryable<TResult>> query, CancellationToken cancellationToken);
/// <summary>
/// Creates a new authorization.
/// </summary>
/// <param name="authorization">The authorization to create.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask CreateAsync(TAuthorization authorization, CancellationToken cancellationToken);
/// <summary>
/// Removes an existing authorization.
/// </summary>
/// <param name="authorization">The authorization to delete.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask DeleteAsync(TAuthorization authorization, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the authorizations corresponding to the specified
/// subject and associated with the application identifier.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the subject/client.</returns>
IAsyncEnumerable<TAuthorization> FindAsync(string subject, string client, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the authorizations matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="status">The authorization status.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the criteria.</returns>
IAsyncEnumerable<TAuthorization> FindAsync(
string subject, string client,
string status, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the authorizations matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="status">The authorization status.</param>
/// <param name="type">The authorization type.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the criteria.</returns>
IAsyncEnumerable<TAuthorization> FindAsync(
string subject, string client,
string status, string type, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the authorizations matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="status">The authorization status.</param>
/// <param name="type">The authorization type.</param>
/// <param name="scopes">The minimal scopes associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the criteria.</returns>
IAsyncEnumerable<TAuthorization> FindAsync(
string subject, string client,
string status, string type,
ImmutableArray<string> scopes, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the list of authorizations corresponding to the specified application identifier.
/// </summary>
/// <param name="identifier">The application identifier associated with the authorizations.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the specified application.</returns>
IAsyncEnumerable<TAuthorization> FindByApplicationIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves an authorization using its unique identifier.
/// </summary>
/// <param name="identifier">The unique identifier associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the authorization corresponding to the identifier.
/// </returns>
ValueTask<TAuthorization?> FindByIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves all the authorizations corresponding to the specified subject.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the specified subject.</returns>
IAsyncEnumerable<TAuthorization> FindBySubjectAsync(string subject, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the optional application identifier associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the application identifier associated with the authorization.
/// </returns>
ValueTask<string?> GetApplicationIdAsync(TAuthorization authorization, CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the first element returned when executing the query.
/// </returns>
ValueTask<TResult?> GetAsync<TState, TResult>(
Func<IQueryable<TAuthorization>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the creation date associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the creation date associated with the specified authorization.
/// </returns>
ValueTask<DateTimeOffset?> GetCreationDateAsync(TAuthorization authorization, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the unique identifier associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the authorization.
/// </returns>
ValueTask<string?> GetIdAsync(TAuthorization authorization, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the additional properties associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the authorization.
/// </returns>
ValueTask<ImmutableDictionary<string, JsonElement>> GetPropertiesAsync(TAuthorization authorization, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the scopes associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the scopes associated with the specified authorization.
/// </returns>
ValueTask<ImmutableArray<string>> GetScopesAsync(TAuthorization authorization, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the status associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the status associated with the specified authorization.
/// </returns>
ValueTask<string?> GetStatusAsync(TAuthorization authorization, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the subject associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the subject associated with the specified authorization.
/// </returns>
ValueTask<string?> GetSubjectAsync(TAuthorization authorization, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the type associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the type associated with the specified authorization.
/// </returns>
ValueTask<string?> GetTypeAsync(TAuthorization authorization, CancellationToken cancellationToken);
/// <summary>
/// Instantiates a new authorization.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the instantiated authorization, that can be persisted in the database.
/// </returns>
ValueTask<TAuthorization> InstantiateAsync(CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <param name="count">The number of results to return.</param>
/// <param name="offset">The number of results to skip.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TAuthorization> ListAsync(int? count, int? offset, CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TResult> ListAsync<TState, TResult>(
Func<IQueryable<TAuthorization>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken);
/// <summary>
/// Removes the authorizations that are marked as invalid and the ad-hoc ones that have no token attached.
/// Only authorizations created before the specified <paramref name="threshold"/> are removed.
/// </summary>
/// <remarks>
/// To ensure ad-hoc authorizations that no longer have any valid/non-expired token
/// attached are correctly removed, the tokens should always be pruned first.
/// </remarks>
/// <param name="threshold">The date before which authorizations are not pruned.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask PruneAsync(DateTimeOffset threshold, CancellationToken cancellationToken);
/// <summary>
/// Sets the application identifier associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="identifier">The unique identifier associated with the client application.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetApplicationIdAsync(TAuthorization authorization, string? identifier, CancellationToken cancellationToken);
/// <summary>
/// Sets the creation date associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="date">The expiration date.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetCreationDateAsync(TAuthorization authorization, DateTimeOffset? date, CancellationToken cancellationToken);
/// <summary>
/// Sets the additional properties associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="properties">The additional properties associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetPropertiesAsync(TAuthorization authorization,
ImmutableDictionary<string, JsonElement> properties, CancellationToken cancellationToken);
/// <summary>
/// Sets the scopes associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="scopes">The scopes associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetScopesAsync(TAuthorization authorization,
ImmutableArray<string> scopes, CancellationToken cancellationToken);
/// <summary>
/// Sets the status associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="status">The status associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetStatusAsync(TAuthorization authorization, string? status, CancellationToken cancellationToken);
/// <summary>
/// Sets the subject associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetSubjectAsync(TAuthorization authorization, string? subject, CancellationToken cancellationToken);
/// <summary>
/// Sets the type associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="type">The type associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetTypeAsync(TAuthorization authorization, string? type, CancellationToken cancellationToken);
/// <summary>
/// Updates an existing authorization.
/// </summary>
/// <param name="authorization">The authorization to update.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask UpdateAsync(TAuthorization authorization, CancellationToken cancellationToken);
}
}
/// Determines the number of authorizations that exist in the database.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of authorizations in the database.
/// </returns>
ValueTask<long> CountAsync(CancellationToken cancellationToken);
/// <summary>
/// Determines the number of authorizations that match the specified query.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of authorizations that match the specified query.
/// </returns>
ValueTask<long> CountAsync<TResult>(Func<IQueryable<TAuthorization>, IQueryable<TResult>> query, CancellationToken cancellationToken);
/// <summary>
/// Creates a new authorization.
/// </summary>
/// <param name="authorization">The authorization to create.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask CreateAsync(TAuthorization authorization, CancellationToken cancellationToken);
/// <summary>
/// Removes an existing authorization.
/// </summary>
/// <param name="authorization">The authorization to delete.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask DeleteAsync(TAuthorization authorization, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the authorizations corresponding to the specified
/// subject and associated with the application identifier.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the subject/client.</returns>
IAsyncEnumerable<TAuthorization> FindAsync(string subject, string client, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the authorizations matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="status">The authorization status.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the criteria.</returns>
IAsyncEnumerable<TAuthorization> FindAsync(
string subject, string client,
string status, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the authorizations matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="status">The authorization status.</param>
/// <param name="type">The authorization type.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the criteria.</returns>
IAsyncEnumerable<TAuthorization> FindAsync(
string subject, string client,
string status, string type, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the authorizations matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="status">The authorization status.</param>
/// <param name="type">The authorization type.</param>
/// <param name="scopes">The minimal scopes associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the criteria.</returns>
IAsyncEnumerable<TAuthorization> FindAsync(
string subject, string client,
string status, string type,
ImmutableArray<string> scopes, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the list of authorizations corresponding to the specified application identifier.
/// </summary>
/// <param name="identifier">The application identifier associated with the authorizations.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the specified application.</returns>
IAsyncEnumerable<TAuthorization> FindByApplicationIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves an authorization using its unique identifier.
/// </summary>
/// <param name="identifier">The unique identifier associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the authorization corresponding to the identifier.
/// </returns>
ValueTask<TAuthorization?> FindByIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves all the authorizations corresponding to the specified subject.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the specified subject.</returns>
IAsyncEnumerable<TAuthorization> FindBySubjectAsync(string subject, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the optional application identifier associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the application identifier associated with the authorization.
/// </returns>
ValueTask<string?> GetApplicationIdAsync(TAuthorization authorization, CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the first element returned when executing the query.
/// </returns>
ValueTask<TResult?> GetAsync<TState, TResult>(
Func<IQueryable<TAuthorization>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the creation date associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the creation date associated with the specified authorization.
/// </returns>
ValueTask<DateTimeOffset?> GetCreationDateAsync(TAuthorization authorization, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the unique identifier associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the authorization.
/// </returns>
ValueTask<string?> GetIdAsync(TAuthorization authorization, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the additional properties associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the authorization.
/// </returns>
ValueTask<ImmutableDictionary<string, JsonElement>> GetPropertiesAsync(TAuthorization authorization, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the scopes associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the scopes associated with the specified authorization.
/// </returns>
ValueTask<ImmutableArray<string>> GetScopesAsync(TAuthorization authorization, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the status associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the status associated with the specified authorization.
/// </returns>
ValueTask<string?> GetStatusAsync(TAuthorization authorization, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the subject associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the subject associated with the specified authorization.
/// </returns>
ValueTask<string?> GetSubjectAsync(TAuthorization authorization, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the type associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the type associated with the specified authorization.
/// </returns>
ValueTask<string?> GetTypeAsync(TAuthorization authorization, CancellationToken cancellationToken);
/// <summary>
/// Instantiates a new authorization.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the instantiated authorization, that can be persisted in the database.
/// </returns>
ValueTask<TAuthorization> InstantiateAsync(CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <param name="count">The number of results to return.</param>
/// <param name="offset">The number of results to skip.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TAuthorization> ListAsync(int? count, int? offset, CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TResult> ListAsync<TState, TResult>(
Func<IQueryable<TAuthorization>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken);
/// <summary>
/// Removes the authorizations that are marked as invalid and the ad-hoc ones that have no token attached.
/// Only authorizations created before the specified <paramref name="threshold"/> are removed.
/// </summary>
/// <remarks>
/// To ensure ad-hoc authorizations that no longer have any valid/non-expired token
/// attached are correctly removed, the tokens should always be pruned first.
/// </remarks>
/// <param name="threshold">The date before which authorizations are not pruned.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask PruneAsync(DateTimeOffset threshold, CancellationToken cancellationToken);
/// <summary>
/// Sets the application identifier associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="identifier">The unique identifier associated with the client application.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetApplicationIdAsync(TAuthorization authorization, string? identifier, CancellationToken cancellationToken);
/// <summary>
/// Sets the creation date associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="date">The expiration date.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetCreationDateAsync(TAuthorization authorization, DateTimeOffset? date, CancellationToken cancellationToken);
/// <summary>
/// Sets the additional properties associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="properties">The additional properties associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetPropertiesAsync(TAuthorization authorization,
ImmutableDictionary<string, JsonElement> properties, CancellationToken cancellationToken);
/// <summary>
/// Sets the scopes associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="scopes">The scopes associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetScopesAsync(TAuthorization authorization,
ImmutableArray<string> scopes, CancellationToken cancellationToken);
/// <summary>
/// Sets the status associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="status">The status associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetStatusAsync(TAuthorization authorization, string? status, CancellationToken cancellationToken);
/// <summary>
/// Sets the subject associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetSubjectAsync(TAuthorization authorization, string? subject, CancellationToken cancellationToken);
/// <summary>
/// Sets the type associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="type">The type associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetTypeAsync(TAuthorization authorization, string? type, CancellationToken cancellationToken);
/// <summary>
/// Updates an existing authorization.
/// </summary>
/// <param name="authorization">The authorization to update.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask UpdateAsync(TAuthorization authorization, CancellationToken cancellationToken);
}

529
src/OpenIddict.Abstractions/Stores/IOpenIddictScopeStore.cs

@ -13,298 +13,297 @@ using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
namespace OpenIddict.Abstractions
namespace OpenIddict.Abstractions;
/// <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
{
/// <summary>
/// Provides methods allowing to manage the scopes stored in a database.
/// Determines the number of scopes that exist in the database.
/// </summary>
/// <typeparam name="TScope">The type of the Scope entity.</typeparam>
public interface IOpenIddictScopeStore<TScope> where TScope : class
{
/// <summary>
/// Determines the number of scopes that exist in the database.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of scopes in the database.
/// </returns>
ValueTask<long> CountAsync(CancellationToken cancellationToken);
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of scopes in the database.
/// </returns>
ValueTask<long> CountAsync(CancellationToken cancellationToken);
/// <summary>
/// Determines the number of scopes that match the specified query.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of scopes that match the specified query.
/// </returns>
ValueTask<long> CountAsync<TResult>(Func<IQueryable<TScope>, IQueryable<TResult>> query, CancellationToken cancellationToken);
/// <summary>
/// Determines the number of scopes that match the specified query.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of scopes that match the specified query.
/// </returns>
ValueTask<long> CountAsync<TResult>(Func<IQueryable<TScope>, IQueryable<TResult>> query, CancellationToken cancellationToken);
/// <summary>
/// Creates a new scope.
/// </summary>
/// <param name="scope">The scope to create.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask CreateAsync(TScope scope, CancellationToken cancellationToken);
/// <summary>
/// Creates a new scope.
/// </summary>
/// <param name="scope">The scope to create.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask CreateAsync(TScope scope, CancellationToken cancellationToken);
/// <summary>
/// Removes an existing scope.
/// </summary>
/// <param name="scope">The scope to delete.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask DeleteAsync(TScope scope, CancellationToken cancellationToken);
/// <summary>
/// Removes an existing scope.
/// </summary>
/// <param name="scope">The scope to delete.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask DeleteAsync(TScope scope, CancellationToken cancellationToken);
/// <summary>
/// Retrieves a scope using its unique identifier.
/// </summary>
/// <param name="identifier">The unique identifier associated with the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the scope corresponding to the identifier.
/// </returns>
ValueTask<TScope?> FindByIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves a scope using its unique identifier.
/// </summary>
/// <param name="identifier">The unique identifier associated with the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the scope corresponding to the identifier.
/// </returns>
ValueTask<TScope?> FindByIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves a scope using its name.
/// </summary>
/// <param name="name">The name associated with the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the scope corresponding to the specified name.
/// </returns>
ValueTask<TScope?> FindByNameAsync(string name, CancellationToken cancellationToken);
/// <summary>
/// Retrieves a scope using its name.
/// </summary>
/// <param name="name">The name associated with the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the scope corresponding to the specified name.
/// </returns>
ValueTask<TScope?> FindByNameAsync(string name, CancellationToken cancellationToken);
/// <summary>
/// Retrieves a list of scopes using their name.
/// </summary>
/// <param name="names">The names associated with the scopes.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The scopes corresponding to the specified names.</returns>
IAsyncEnumerable<TScope> FindByNamesAsync(ImmutableArray<string> names, CancellationToken cancellationToken);
/// <summary>
/// Retrieves a list of scopes using their name.
/// </summary>
/// <param name="names">The names associated with the scopes.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The scopes corresponding to the specified names.</returns>
IAsyncEnumerable<TScope> FindByNamesAsync(ImmutableArray<string> names, CancellationToken cancellationToken);
/// <summary>
/// Retrieves all the scopes that contain the specified resource.
/// </summary>
/// <param name="resource">The resource associated with the scopes.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The scopes associated with the specified resource.</returns>
IAsyncEnumerable<TScope> FindByResourceAsync(string resource, CancellationToken cancellationToken);
/// <summary>
/// Retrieves all the scopes that contain the specified resource.
/// </summary>
/// <param name="resource">The resource associated with the scopes.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The scopes associated with the specified resource.</returns>
IAsyncEnumerable<TScope> FindByResourceAsync(string resource, CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the first element returned when executing the query.
/// </returns>
ValueTask<TResult?> GetAsync<TState, TResult>(
Func<IQueryable<TScope>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the first element returned when executing the query.
/// </returns>
ValueTask<TResult?> GetAsync<TState, TResult>(
Func<IQueryable<TScope>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the description associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the description associated with the specified scope.
/// </returns>
ValueTask<string?> GetDescriptionAsync(TScope scope, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the description associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the description associated with the specified scope.
/// </returns>
ValueTask<string?> GetDescriptionAsync(TScope scope, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the localized descriptions associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the localized descriptions associated with the specified scope.
/// </returns>
ValueTask<ImmutableDictionary<CultureInfo, string>> GetDescriptionsAsync(TScope scope, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the localized descriptions associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the localized descriptions associated with the specified scope.
/// </returns>
ValueTask<ImmutableDictionary<CultureInfo, string>> GetDescriptionsAsync(TScope scope, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the display name associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the display name associated with the scope.
/// </returns>
ValueTask<string?> GetDisplayNameAsync(TScope scope, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the display name associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the display name associated with the scope.
/// </returns>
ValueTask<string?> GetDisplayNameAsync(TScope scope, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the localized display names associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the localized display names associated with the scope.
/// </returns>
ValueTask<ImmutableDictionary<CultureInfo, string>> GetDisplayNamesAsync(TScope scope, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the localized display names associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the localized display names associated with the scope.
/// </returns>
ValueTask<ImmutableDictionary<CultureInfo, string>> GetDisplayNamesAsync(TScope scope, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the unique identifier associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the scope.
/// </returns>
ValueTask<string?> GetIdAsync(TScope scope, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the unique identifier associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the scope.
/// </returns>
ValueTask<string?> GetIdAsync(TScope scope, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the name associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the name associated with the specified scope.
/// </returns>
ValueTask<string?> GetNameAsync(TScope scope, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the name associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the name associated with the specified scope.
/// </returns>
ValueTask<string?> GetNameAsync(TScope scope, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the additional properties associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation, whose
/// result returns all the additional properties associated with the scope.
/// </returns>
ValueTask<ImmutableDictionary<string, JsonElement>> GetPropertiesAsync(TScope scope, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the additional properties associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation, whose
/// result returns all the additional properties associated with the scope.
/// </returns>
ValueTask<ImmutableDictionary<string, JsonElement>> GetPropertiesAsync(TScope scope, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the resources associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the resources associated with the scope.
/// </returns>
ValueTask<ImmutableArray<string>> GetResourcesAsync(TScope scope, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the resources associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the resources associated with the scope.
/// </returns>
ValueTask<ImmutableArray<string>> GetResourcesAsync(TScope scope, CancellationToken cancellationToken);
/// <summary>
/// Instantiates a new scope.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the instantiated scope, that can be persisted in the database.
/// </returns>
ValueTask<TScope> InstantiateAsync(CancellationToken cancellationToken);
/// <summary>
/// Instantiates a new scope.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the instantiated scope, that can be persisted in the database.
/// </returns>
ValueTask<TScope> InstantiateAsync(CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <param name="count">The number of results to return.</param>
/// <param name="offset">The number of results to skip.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TScope> ListAsync(int? count, int? offset, CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <param name="count">The number of results to return.</param>
/// <param name="offset">The number of results to skip.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TScope> ListAsync(int? count, int? offset, CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TResult> ListAsync<TState, TResult>(
Func<IQueryable<TScope>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TResult> ListAsync<TState, TResult>(
Func<IQueryable<TScope>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken);
/// <summary>
/// Sets the description associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="description">The description associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetDescriptionAsync(TScope scope, string? description, CancellationToken cancellationToken);
/// <summary>
/// Sets the description associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="description">The description associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetDescriptionAsync(TScope scope, string? description, CancellationToken cancellationToken);
/// <summary>
/// Sets the localized descriptions associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="descriptions">The localized descriptions associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetDescriptionsAsync(TScope scope,
ImmutableDictionary<CultureInfo, string> descriptions, CancellationToken cancellationToken);
/// <summary>
/// Sets the localized descriptions associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="descriptions">The localized descriptions associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetDescriptionsAsync(TScope scope,
ImmutableDictionary<CultureInfo, string> descriptions, CancellationToken cancellationToken);
/// <summary>
/// Sets the display name associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="name">The display name associated with the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetDisplayNameAsync(TScope scope, string? name, CancellationToken cancellationToken);
/// <summary>
/// Sets the display name associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="name">The display name associated with the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetDisplayNameAsync(TScope scope, string? name, CancellationToken cancellationToken);
/// <summary>
/// Sets the localized display names associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="names">The localized display names associated with the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetDisplayNamesAsync(TScope scope,
ImmutableDictionary<CultureInfo, string> names, CancellationToken cancellationToken);
/// <summary>
/// Sets the localized display names associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="names">The localized display names associated with the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetDisplayNamesAsync(TScope scope,
ImmutableDictionary<CultureInfo, string> names, CancellationToken cancellationToken);
/// <summary>
/// Sets the name associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="name">The name associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetNameAsync(TScope scope, string? name, CancellationToken cancellationToken);
/// <summary>
/// Sets the name associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="name">The name associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetNameAsync(TScope scope, string? name, CancellationToken cancellationToken);
/// <summary>
/// Sets the additional properties associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="properties">The additional properties associated with the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetPropertiesAsync(TScope scope,
ImmutableDictionary<string, JsonElement> properties, CancellationToken cancellationToken);
/// <summary>
/// Sets the additional properties associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="properties">The additional properties associated with the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetPropertiesAsync(TScope scope,
ImmutableDictionary<string, JsonElement> properties, CancellationToken cancellationToken);
/// <summary>
/// Sets the resources associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="resources">The resources associated with the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetResourcesAsync(TScope scope, ImmutableArray<string> resources, CancellationToken cancellationToken);
/// <summary>
/// Sets the resources associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="resources">The resources associated with the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetResourcesAsync(TScope scope, ImmutableArray<string> resources, CancellationToken cancellationToken);
/// <summary>
/// Updates an existing scope.
/// </summary>
/// <param name="scope">The scope to update.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask UpdateAsync(TScope scope, CancellationToken cancellationToken);
}
}
/// <summary>
/// Updates an existing scope.
/// </summary>
/// <param name="scope">The scope to update.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask UpdateAsync(TScope scope, CancellationToken cancellationToken);
}

855
src/OpenIddict.Abstractions/Stores/IOpenIddictTokenStore.cs

@ -12,433 +12,432 @@ using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
namespace OpenIddict.Abstractions
namespace OpenIddict.Abstractions;
/// <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>
/// 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>
/// Determines the number of tokens that exist in the database.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of applications in the database.
/// </returns>
ValueTask<long> CountAsync(CancellationToken cancellationToken);
/// <summary>
/// Determines the number of tokens that match the specified query.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of tokens that match the specified query.
/// </returns>
ValueTask<long> CountAsync<TResult>(Func<IQueryable<TToken>, IQueryable<TResult>> query, CancellationToken cancellationToken);
/// <summary>
/// Creates a new token.
/// </summary>
/// <param name="token">The token to create.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask CreateAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Removes a token.
/// </summary>
/// <param name="token">The token to delete.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask DeleteAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the tokens corresponding to the specified
/// subject and associated with the application identifier.
/// </summary>
/// <param name="subject">The subject associated with the token.</param>
/// <param name="client">The client associated with the token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the subject/client.</returns>
IAsyncEnumerable<TToken> FindAsync(string subject, string client, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the tokens matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the token.</param>
/// <param name="client">The client associated with the token.</param>
/// <param name="status">The token status.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the criteria.</returns>
IAsyncEnumerable<TToken> FindAsync(
string subject, string client,
string status, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the tokens matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the token.</param>
/// <param name="client">The client associated with the token.</param>
/// <param name="status">The token status.</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>The tokens corresponding to the criteria.</returns>
IAsyncEnumerable<TToken> FindAsync(
string subject, string client,
string status, string type, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the list of tokens corresponding to the specified application identifier.
/// </summary>
/// <param name="identifier">The application identifier associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the specified application.</returns>
IAsyncEnumerable<TToken> FindByApplicationIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the list of tokens corresponding to the specified authorization identifier.
/// </summary>
/// <param name="identifier">The authorization identifier associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the specified authorization.</returns>
IAsyncEnumerable<TToken> FindByAuthorizationIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves a 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="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the token corresponding to the unique identifier.
/// </returns>
ValueTask<TToken?> FindByIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the list of tokens corresponding to the specified reference identifier.
/// Note: the reference identifier may be hashed or encrypted for security reasons.
/// </summary>
/// <param name="identifier">The reference identifier associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the tokens corresponding to the specified reference identifier.
/// </returns>
ValueTask<TToken?> FindByReferenceIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the list of tokens corresponding to the specified subject.
/// </summary>
/// <param name="subject">The subject associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the specified subject.</returns>
IAsyncEnumerable<TToken> FindBySubjectAsync(string subject, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the optional application identifier associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the application identifier associated with the token.
/// </returns>
ValueTask<string?> GetApplicationIdAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the first element returned when executing the query.
/// </returns>
ValueTask<TResult?> GetAsync<TState, TResult>(
Func<IQueryable<TToken>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the optional authorization identifier associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the authorization identifier associated with the token.
/// </returns>
ValueTask<string?> GetAuthorizationIdAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the creation date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the creation date associated with the specified token.
/// </returns>
ValueTask<DateTimeOffset?> GetCreationDateAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the expiration date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the expiration date associated with the specified token.
/// </returns>
ValueTask<DateTimeOffset?> GetExpirationDateAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the unique identifier associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the token.
/// </returns>
ValueTask<string?> GetIdAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the payload associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the payload associated with the specified token.
/// </returns>
ValueTask<string?> GetPayloadAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the additional properties associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the token.
/// </returns>
ValueTask<ImmutableDictionary<string, JsonElement>> GetPropertiesAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the redemption date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the redemption date associated with the specified token.
/// </returns>
ValueTask<DateTimeOffset?> GetRedemptionDateAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the reference identifier associated with a token.
/// Note: depending on the manager used to create the token,
/// the reference identifier may be hashed for security reasons.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the reference identifier associated with the specified token.
/// </returns>
ValueTask<string?> GetReferenceIdAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the status associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the status associated with the specified token.
/// </returns>
ValueTask<string?> GetStatusAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the subject associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the subject associated with the specified token.
/// </returns>
ValueTask<string?> GetSubjectAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the token type associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the token type associated with the specified token.
/// </returns>
ValueTask<string?> GetTypeAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Instantiates a new token.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the instantiated token, that can be persisted in the database.
/// </returns>
ValueTask<TToken> InstantiateAsync(CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <param name="count">The number of results to return.</param>
/// <param name="offset">The number of results to skip.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TToken> ListAsync(int? count, int? offset, CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TResult> ListAsync<TState, TResult>(
Func<IQueryable<TToken>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken);
/// <summary>
/// Removes the tokens that are marked as invalid or whose attached authorization is no longer valid.
/// Only tokens created before the specified <paramref name="threshold"/> are removed.
/// </summary>
/// <param name="threshold">The date before which tokens are not pruned.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask PruneAsync(DateTimeOffset threshold, CancellationToken cancellationToken);
/// <summary>
/// Sets the application identifier associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetApplicationIdAsync(TToken token, string? identifier, CancellationToken cancellationToken);
/// <summary>
/// Sets the authorization identifier associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetAuthorizationIdAsync(TToken token, string? identifier, CancellationToken cancellationToken);
/// <summary>
/// Sets the creation date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="date">The creation date.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetCreationDateAsync(TToken token, DateTimeOffset? date, CancellationToken cancellationToken);
/// <summary>
/// Sets the expiration date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="date">The expiration date.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetExpirationDateAsync(TToken token, DateTimeOffset? date, CancellationToken cancellationToken);
/// <summary>
/// Sets the payload associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="payload">The payload 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetPayloadAsync(TToken token, string? payload, CancellationToken cancellationToken);
/// <summary>
/// Sets the additional properties associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="properties">The additional properties 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetPropertiesAsync(TToken token,
ImmutableDictionary<string, JsonElement> properties, CancellationToken cancellationToken);
/// <summary>
/// Sets the redemption date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="date">The redemption date.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetRedemptionDateAsync(TToken token, DateTimeOffset? date, CancellationToken cancellationToken);
/// <summary>
/// Sets the reference identifier associated with a token.
/// Note: depending on the manager used to create the token,
/// the reference identifier may be hashed for security reasons.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="identifier">The reference 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetReferenceIdAsync(TToken token, string? identifier, CancellationToken cancellationToken);
/// <summary>
/// Sets the status associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="status">The status associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetStatusAsync(TToken token, string? status, CancellationToken cancellationToken);
/// <summary>
/// Sets the subject associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="subject">The subject 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetSubjectAsync(TToken token, string? subject, CancellationToken cancellationToken);
/// <summary>
/// Sets the token type associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="type">The token type 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetTypeAsync(TToken token, string? type, CancellationToken cancellationToken);
/// <summary>
/// Updates an existing token.
/// </summary>
/// <param name="token">The token to update.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask UpdateAsync(TToken token, CancellationToken cancellationToken);
}
}
/// Determines the number of tokens that exist in the database.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of applications in the database.
/// </returns>
ValueTask<long> CountAsync(CancellationToken cancellationToken);
/// <summary>
/// Determines the number of tokens that match the specified query.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of tokens that match the specified query.
/// </returns>
ValueTask<long> CountAsync<TResult>(Func<IQueryable<TToken>, IQueryable<TResult>> query, CancellationToken cancellationToken);
/// <summary>
/// Creates a new token.
/// </summary>
/// <param name="token">The token to create.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask CreateAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Removes a token.
/// </summary>
/// <param name="token">The token to delete.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask DeleteAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the tokens corresponding to the specified
/// subject and associated with the application identifier.
/// </summary>
/// <param name="subject">The subject associated with the token.</param>
/// <param name="client">The client associated with the token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the subject/client.</returns>
IAsyncEnumerable<TToken> FindAsync(string subject, string client, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the tokens matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the token.</param>
/// <param name="client">The client associated with the token.</param>
/// <param name="status">The token status.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the criteria.</returns>
IAsyncEnumerable<TToken> FindAsync(
string subject, string client,
string status, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the tokens matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the token.</param>
/// <param name="client">The client associated with the token.</param>
/// <param name="status">The token status.</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>The tokens corresponding to the criteria.</returns>
IAsyncEnumerable<TToken> FindAsync(
string subject, string client,
string status, string type, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the list of tokens corresponding to the specified application identifier.
/// </summary>
/// <param name="identifier">The application identifier associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the specified application.</returns>
IAsyncEnumerable<TToken> FindByApplicationIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the list of tokens corresponding to the specified authorization identifier.
/// </summary>
/// <param name="identifier">The authorization identifier associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the specified authorization.</returns>
IAsyncEnumerable<TToken> FindByAuthorizationIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves a 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="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the token corresponding to the unique identifier.
/// </returns>
ValueTask<TToken?> FindByIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the list of tokens corresponding to the specified reference identifier.
/// Note: the reference identifier may be hashed or encrypted for security reasons.
/// </summary>
/// <param name="identifier">The reference identifier associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the tokens corresponding to the specified reference identifier.
/// </returns>
ValueTask<TToken?> FindByReferenceIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the list of tokens corresponding to the specified subject.
/// </summary>
/// <param name="subject">The subject associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The tokens corresponding to the specified subject.</returns>
IAsyncEnumerable<TToken> FindBySubjectAsync(string subject, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the optional application identifier associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the application identifier associated with the token.
/// </returns>
ValueTask<string?> GetApplicationIdAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the first element returned when executing the query.
/// </returns>
ValueTask<TResult?> GetAsync<TState, TResult>(
Func<IQueryable<TToken>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the optional authorization identifier associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the authorization identifier associated with the token.
/// </returns>
ValueTask<string?> GetAuthorizationIdAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the creation date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the creation date associated with the specified token.
/// </returns>
ValueTask<DateTimeOffset?> GetCreationDateAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the expiration date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the expiration date associated with the specified token.
/// </returns>
ValueTask<DateTimeOffset?> GetExpirationDateAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the unique identifier associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the token.
/// </returns>
ValueTask<string?> GetIdAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the payload associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the payload associated with the specified token.
/// </returns>
ValueTask<string?> GetPayloadAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the additional properties associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the token.
/// </returns>
ValueTask<ImmutableDictionary<string, JsonElement>> GetPropertiesAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the redemption date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the redemption date associated with the specified token.
/// </returns>
ValueTask<DateTimeOffset?> GetRedemptionDateAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the reference identifier associated with a token.
/// Note: depending on the manager used to create the token,
/// the reference identifier may be hashed for security reasons.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the reference identifier associated with the specified token.
/// </returns>
ValueTask<string?> GetReferenceIdAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the status associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the status associated with the specified token.
/// </returns>
ValueTask<string?> GetStatusAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the subject associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the subject associated with the specified token.
/// </returns>
ValueTask<string?> GetSubjectAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the token type associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the token type associated with the specified token.
/// </returns>
ValueTask<string?> GetTypeAsync(TToken token, CancellationToken cancellationToken);
/// <summary>
/// Instantiates a new token.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the instantiated token, that can be persisted in the database.
/// </returns>
ValueTask<TToken> InstantiateAsync(CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <param name="count">The number of results to return.</param>
/// <param name="offset">The number of results to skip.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TToken> ListAsync(int? count, int? offset, CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
IAsyncEnumerable<TResult> ListAsync<TState, TResult>(
Func<IQueryable<TToken>, TState, IQueryable<TResult>> query,
TState state, CancellationToken cancellationToken);
/// <summary>
/// Removes the tokens that are marked as invalid or whose attached authorization is no longer valid.
/// Only tokens created before the specified <paramref name="threshold"/> are removed.
/// </summary>
/// <param name="threshold">The date before which tokens are not pruned.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask PruneAsync(DateTimeOffset threshold, CancellationToken cancellationToken);
/// <summary>
/// Sets the application identifier associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetApplicationIdAsync(TToken token, string? identifier, CancellationToken cancellationToken);
/// <summary>
/// Sets the authorization identifier associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetAuthorizationIdAsync(TToken token, string? identifier, CancellationToken cancellationToken);
/// <summary>
/// Sets the creation date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="date">The creation date.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetCreationDateAsync(TToken token, DateTimeOffset? date, CancellationToken cancellationToken);
/// <summary>
/// Sets the expiration date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="date">The expiration date.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetExpirationDateAsync(TToken token, DateTimeOffset? date, CancellationToken cancellationToken);
/// <summary>
/// Sets the payload associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="payload">The payload 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetPayloadAsync(TToken token, string? payload, CancellationToken cancellationToken);
/// <summary>
/// Sets the additional properties associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="properties">The additional properties 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetPropertiesAsync(TToken token,
ImmutableDictionary<string, JsonElement> properties, CancellationToken cancellationToken);
/// <summary>
/// Sets the redemption date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="date">The redemption date.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetRedemptionDateAsync(TToken token, DateTimeOffset? date, CancellationToken cancellationToken);
/// <summary>
/// Sets the reference identifier associated with a token.
/// Note: depending on the manager used to create the token,
/// the reference identifier may be hashed for security reasons.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="identifier">The reference 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetReferenceIdAsync(TToken token, string? identifier, CancellationToken cancellationToken);
/// <summary>
/// Sets the status associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="status">The status associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetStatusAsync(TToken token, string? status, CancellationToken cancellationToken);
/// <summary>
/// Sets the subject associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="subject">The subject 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetSubjectAsync(TToken token, string? subject, CancellationToken cancellationToken);
/// <summary>
/// Sets the token type associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="type">The token type 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="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask SetTypeAsync(TToken token, string? type, CancellationToken cancellationToken);
/// <summary>
/// Updates an existing token.
/// </summary>
/// <param name="token">The token to update.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
ValueTask UpdateAsync(TToken token, CancellationToken cancellationToken);
}

507
src/OpenIddict.Core/Caches/OpenIddictApplicationCache.cs

@ -17,354 +17,353 @@ using Microsoft.Extensions.Primitives;
using OpenIddict.Abstractions;
using SR = OpenIddict.Abstractions.OpenIddictResources;
namespace OpenIddict.Core
namespace OpenIddict.Core;
/// <summary>
/// Provides methods allowing to cache applications after retrieving them from the store.
/// </summary>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
public class OpenIddictApplicationCache<TApplication> : IOpenIddictApplicationCache<TApplication>, IDisposable where TApplication : class
{
/// <summary>
/// Provides methods allowing to cache applications after retrieving them from the store.
/// </summary>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
public class OpenIddictApplicationCache<TApplication> : IOpenIddictApplicationCache<TApplication>, IDisposable where TApplication : class
{
private readonly MemoryCache _cache;
private readonly ConcurrentDictionary<string, CancellationTokenSource> _signals;
private readonly IOpenIddictApplicationStore<TApplication> _store;
private readonly MemoryCache _cache;
private readonly ConcurrentDictionary<string, CancellationTokenSource> _signals;
private readonly IOpenIddictApplicationStore<TApplication> _store;
public OpenIddictApplicationCache(
IOptionsMonitor<OpenIddictCoreOptions> options,
IOpenIddictApplicationStoreResolver resolver)
public OpenIddictApplicationCache(
IOptionsMonitor<OpenIddictCoreOptions> options,
IOpenIddictApplicationStoreResolver resolver)
{
_cache = new MemoryCache(new MemoryCacheOptions
{
_cache = new MemoryCache(new MemoryCacheOptions
{
SizeLimit = options.CurrentValue.EntityCacheLimit
});
SizeLimit = options.CurrentValue.EntityCacheLimit
});
_signals = new ConcurrentDictionary<string, CancellationTokenSource>(StringComparer.Ordinal);
_store = resolver.Get<TApplication>();
_signals = new ConcurrentDictionary<string, CancellationTokenSource>(StringComparer.Ordinal);
_store = resolver.Get<TApplication>();
}
/// <inheritdoc/>
public async ValueTask AddAsync(TApplication application, CancellationToken cancellationToken)
{
if (application is null)
{
throw new ArgumentNullException(nameof(application));
}
/// <inheritdoc/>
public async ValueTask AddAsync(TApplication application, CancellationToken cancellationToken)
_cache.Remove(new
{
if (application is null)
{
throw new ArgumentNullException(nameof(application));
}
Method = nameof(FindByClientIdAsync),
Identifier = await _store.GetClientIdAsync(application, cancellationToken)
});
_cache.Remove(new
{
Method = nameof(FindByIdAsync),
Identifier = await _store.GetIdAsync(application, cancellationToken)
});
foreach (var address in await _store.GetPostLogoutRedirectUrisAsync(application, cancellationToken))
{
_cache.Remove(new
{
Method = nameof(FindByClientIdAsync),
Identifier = await _store.GetClientIdAsync(application, cancellationToken)
Method = nameof(FindByPostLogoutRedirectUriAsync),
Address = address
});
}
foreach (var address in await _store.GetRedirectUrisAsync(application, cancellationToken))
{
_cache.Remove(new
{
Method = nameof(FindByIdAsync),
Identifier = await _store.GetIdAsync(application, cancellationToken)
Method = nameof(FindByRedirectUriAsync),
Address = address
});
}
foreach (var address in await _store.GetPostLogoutRedirectUrisAsync(application, cancellationToken))
{
_cache.Remove(new
{
Method = nameof(FindByPostLogoutRedirectUriAsync),
Address = address
});
}
await CreateEntryAsync(new
{
Method = nameof(FindByIdAsync),
Identifier = await _store.GetIdAsync(application, cancellationToken)
}, application, cancellationToken);
foreach (var address in await _store.GetRedirectUrisAsync(application, cancellationToken))
{
_cache.Remove(new
{
Method = nameof(FindByRedirectUriAsync),
Address = address
});
}
await CreateEntryAsync(new
{
Method = nameof(FindByClientIdAsync),
Identifier = await _store.GetClientIdAsync(application, cancellationToken)
}, application, cancellationToken);
}
await CreateEntryAsync(new
{
Method = nameof(FindByIdAsync),
Identifier = await _store.GetIdAsync(application, cancellationToken)
}, application, cancellationToken);
/// <inheritdoc/>
public void Dispose()
{
foreach (var signal in _signals)
{
signal.Value.Dispose();
}
await CreateEntryAsync(new
{
Method = nameof(FindByClientIdAsync),
Identifier = await _store.GetClientIdAsync(application, cancellationToken)
}, application, cancellationToken);
_cache.Dispose();
}
/// <inheritdoc/>
public ValueTask<TApplication?> FindByClientIdAsync(string identifier, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(identifier))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0195), nameof(identifier));
}
/// <inheritdoc/>
public void Dispose()
var parameters = new
{
foreach (var signal in _signals)
{
signal.Value.Dispose();
}
Method = nameof(FindByClientIdAsync),
Identifier = identifier
};
_cache.Dispose();
if (_cache.TryGetValue(parameters, out TApplication? application))
{
return new ValueTask<TApplication?>(application);
}
/// <inheritdoc/>
public ValueTask<TApplication?> FindByClientIdAsync(string identifier, CancellationToken cancellationToken)
return new ValueTask<TApplication?>(ExecuteAsync());
async Task<TApplication?> ExecuteAsync()
{
if (string.IsNullOrEmpty(identifier))
if ((application = await _store.FindByClientIdAsync(identifier, cancellationToken)) is not null)
{
throw new ArgumentException(SR.GetResourceString(SR.ID0195), nameof(identifier));
await AddAsync(application, cancellationToken);
}
var parameters = new
{
Method = nameof(FindByClientIdAsync),
Identifier = identifier
};
if (_cache.TryGetValue(parameters, out TApplication? application))
{
return new ValueTask<TApplication?>(application);
}
await CreateEntryAsync(parameters, application, cancellationToken);
return new ValueTask<TApplication?>(ExecuteAsync());
return application;
}
}
async Task<TApplication?> ExecuteAsync()
{
if ((application = await _store.FindByClientIdAsync(identifier, cancellationToken)) is not null)
{
await AddAsync(application, cancellationToken);
}
/// <inheritdoc/>
public ValueTask<TApplication?> FindByIdAsync(string identifier, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(identifier))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0195), nameof(identifier));
}
await CreateEntryAsync(parameters, application, cancellationToken);
var parameters = new
{
Method = nameof(FindByIdAsync),
Identifier = identifier
};
return application;
}
if (_cache.TryGetValue(parameters, out TApplication? application))
{
return new ValueTask<TApplication?>(application);
}
/// <inheritdoc/>
public ValueTask<TApplication?> FindByIdAsync(string identifier, CancellationToken cancellationToken)
return new ValueTask<TApplication?>(ExecuteAsync());
async Task<TApplication?> ExecuteAsync()
{
if (string.IsNullOrEmpty(identifier))
if ((application = await _store.FindByIdAsync(identifier, cancellationToken)) is not null)
{
throw new ArgumentException(SR.GetResourceString(SR.ID0195), nameof(identifier));
await AddAsync(application, cancellationToken);
}
await CreateEntryAsync(parameters, application, cancellationToken);
return application;
}
}
/// <inheritdoc/>
public IAsyncEnumerable<TApplication> FindByPostLogoutRedirectUriAsync(string address, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(address))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0143), nameof(address));
}
return ExecuteAsync(cancellationToken);
async IAsyncEnumerable<TApplication> ExecuteAsync([EnumeratorCancellation] CancellationToken cancellationToken)
{
var parameters = new
{
Method = nameof(FindByIdAsync),
Identifier = identifier
Method = nameof(FindByPostLogoutRedirectUriAsync),
Address = address
};
if (_cache.TryGetValue(parameters, out TApplication? application))
if (!_cache.TryGetValue(parameters, out ImmutableArray<TApplication> applications))
{
return new ValueTask<TApplication?>(application);
}
return new ValueTask<TApplication?>(ExecuteAsync());
var builder = ImmutableArray.CreateBuilder<TApplication>();
async Task<TApplication?> ExecuteAsync()
{
if ((application = await _store.FindByIdAsync(identifier, cancellationToken)) is not null)
await foreach (var application in _store.FindByPostLogoutRedirectUriAsync(address, cancellationToken))
{
builder.Add(application);
await AddAsync(application, cancellationToken);
}
await CreateEntryAsync(parameters, application, cancellationToken);
applications = builder.ToImmutable();
return application;
await CreateEntryAsync(parameters, applications, cancellationToken);
}
}
/// <inheritdoc/>
public IAsyncEnumerable<TApplication> FindByPostLogoutRedirectUriAsync(string address, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(address))
foreach (var application in applications)
{
throw new ArgumentException(SR.GetResourceString(SR.ID0143), nameof(address));
yield return application;
}
}
}
return ExecuteAsync(cancellationToken);
async IAsyncEnumerable<TApplication> ExecuteAsync([EnumeratorCancellation] CancellationToken cancellationToken)
{
var parameters = new
{
Method = nameof(FindByPostLogoutRedirectUriAsync),
Address = address
};
if (!_cache.TryGetValue(parameters, out ImmutableArray<TApplication> applications))
{
var builder = ImmutableArray.CreateBuilder<TApplication>();
await foreach (var application in _store.FindByPostLogoutRedirectUriAsync(address, cancellationToken))
{
builder.Add(application);
await AddAsync(application, cancellationToken);
}
applications = builder.ToImmutable();
await CreateEntryAsync(parameters, applications, cancellationToken);
}
foreach (var application in applications)
{
yield return application;
}
}
/// <inheritdoc/>
public IAsyncEnumerable<TApplication> FindByRedirectUriAsync(string address, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(address))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0143), nameof(address));
}
/// <inheritdoc/>
public IAsyncEnumerable<TApplication> FindByRedirectUriAsync(string address, CancellationToken cancellationToken)
return ExecuteAsync(cancellationToken);
async IAsyncEnumerable<TApplication> ExecuteAsync([EnumeratorCancellation] CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(address))
var parameters = new
{
throw new ArgumentException(SR.GetResourceString(SR.ID0143), nameof(address));
}
return ExecuteAsync(cancellationToken);
Method = nameof(FindByRedirectUriAsync),
Address = address
};
async IAsyncEnumerable<TApplication> ExecuteAsync([EnumeratorCancellation] CancellationToken cancellationToken)
if (!_cache.TryGetValue(parameters, out ImmutableArray<TApplication> applications))
{
var parameters = new
{
Method = nameof(FindByRedirectUriAsync),
Address = address
};
var builder = ImmutableArray.CreateBuilder<TApplication>();
if (!_cache.TryGetValue(parameters, out ImmutableArray<TApplication> applications))
await foreach (var application in _store.FindByRedirectUriAsync(address, cancellationToken))
{
var builder = ImmutableArray.CreateBuilder<TApplication>();
await foreach (var application in _store.FindByRedirectUriAsync(address, cancellationToken))
{
builder.Add(application);
builder.Add(application);
await AddAsync(application, cancellationToken);
}
await AddAsync(application, cancellationToken);
}
applications = builder.ToImmutable();
applications = builder.ToImmutable();
await CreateEntryAsync(parameters, applications, cancellationToken);
}
await CreateEntryAsync(parameters, applications, cancellationToken);
}
foreach (var application in applications)
{
yield return application;
}
foreach (var application in applications)
{
yield return application;
}
}
}
/// <inheritdoc/>
public async ValueTask RemoveAsync(TApplication application, CancellationToken cancellationToken)
/// <inheritdoc/>
public async ValueTask RemoveAsync(TApplication application, CancellationToken cancellationToken)
{
if (application is null)
{
if (application is null)
{
throw new ArgumentNullException(nameof(application));
}
throw new ArgumentNullException(nameof(application));
}
var identifier = await _store.GetIdAsync(application, cancellationToken);
if (string.IsNullOrEmpty(identifier))
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0196));
}
var identifier = await _store.GetIdAsync(application, cancellationToken);
if (string.IsNullOrEmpty(identifier))
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0196));
}
if (_signals.TryRemove(identifier, out CancellationTokenSource? signal))
{
signal.Cancel();
signal.Dispose();
}
if (_signals.TryRemove(identifier, out CancellationTokenSource? signal))
{
signal.Cancel();
signal.Dispose();
}
}
/// <summary>
/// Creates a cache entry for the specified key.
/// </summary>
/// <param name="key">The cache key.</param>
/// <param name="application">The application to store in the cache entry, if applicable.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
protected virtual async ValueTask CreateEntryAsync(object key, TApplication? application, CancellationToken cancellationToken)
/// <summary>
/// Creates a cache entry for the specified key.
/// </summary>
/// <param name="key">The cache key.</param>
/// <param name="application">The application to store in the cache entry, if applicable.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
protected virtual async ValueTask CreateEntryAsync(object key, TApplication? application, CancellationToken cancellationToken)
{
if (key is null)
{
if (key is null)
{
throw new ArgumentNullException(nameof(key));
}
throw new ArgumentNullException(nameof(key));
}
using var entry = _cache.CreateEntry(key);
using var entry = _cache.CreateEntry(key);
if (application is not null)
if (application is not null)
{
var signal = await CreateExpirationSignalAsync(application, cancellationToken);
if (signal is null)
{
var signal = await CreateExpirationSignalAsync(application, cancellationToken);
if (signal is null)
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0197));
}
entry.AddExpirationToken(signal);
throw new InvalidOperationException(SR.GetResourceString(SR.ID0197));
}
entry.SetSize(1L);
entry.SetValue(application);
entry.AddExpirationToken(signal);
}
/// <summary>
/// Creates a cache entry for the specified key.
/// </summary>
/// <param name="key">The cache key.</param>
/// <param name="applications">The applications to store in the cache entry.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
protected virtual async ValueTask CreateEntryAsync(
object key, ImmutableArray<TApplication> applications, CancellationToken cancellationToken)
entry.SetSize(1L);
entry.SetValue(application);
}
/// <summary>
/// Creates a cache entry for the specified key.
/// </summary>
/// <param name="key">The cache key.</param>
/// <param name="applications">The applications to store in the cache entry.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
protected virtual async ValueTask CreateEntryAsync(
object key, ImmutableArray<TApplication> applications, CancellationToken cancellationToken)
{
if (key is null)
{
if (key is null)
{
throw new ArgumentNullException(nameof(key));
}
throw new ArgumentNullException(nameof(key));
}
using var entry = _cache.CreateEntry(key);
using var entry = _cache.CreateEntry(key);
foreach (var application in applications)
foreach (var application in applications)
{
var signal = await CreateExpirationSignalAsync(application, cancellationToken);
if (signal is null)
{
var signal = await CreateExpirationSignalAsync(application, cancellationToken);
if (signal is null)
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0197));
}
entry.AddExpirationToken(signal);
throw new InvalidOperationException(SR.GetResourceString(SR.ID0197));
}
entry.SetSize(applications.Length);
entry.SetValue(applications);
entry.AddExpirationToken(signal);
}
/// <summary>
/// Creates an expiration signal allowing to invalidate all the
/// cache entries associated with the specified application.
/// </summary>
/// <param name="application">The application associated with the expiration signal.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns an expiration signal for the specified application.
/// </returns>
protected virtual async ValueTask<IChangeToken> CreateExpirationSignalAsync(
TApplication application, CancellationToken cancellationToken)
entry.SetSize(applications.Length);
entry.SetValue(applications);
}
/// <summary>
/// Creates an expiration signal allowing to invalidate all the
/// cache entries associated with the specified application.
/// </summary>
/// <param name="application">The application associated with the expiration signal.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns an expiration signal for the specified application.
/// </returns>
protected virtual async ValueTask<IChangeToken> CreateExpirationSignalAsync(
TApplication application, CancellationToken cancellationToken)
{
if (application is null)
{
if (application is null)
{
throw new ArgumentNullException(nameof(application));
}
throw new ArgumentNullException(nameof(application));
}
var identifier = await _store.GetIdAsync(application, cancellationToken);
if (string.IsNullOrEmpty(identifier))
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0196));
}
var identifier = await _store.GetIdAsync(application, cancellationToken);
if (string.IsNullOrEmpty(identifier))
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0196));
}
var signal = _signals.GetOrAdd(identifier, _ => new CancellationTokenSource());
var signal = _signals.GetOrAdd(identifier, _ => new CancellationTokenSource());
return new CancellationChangeToken(signal.Token);
}
return new CancellationChangeToken(signal.Token);
}
}

773
src/OpenIddict.Core/Caches/OpenIddictAuthorizationCache.cs

@ -17,530 +17,529 @@ using Microsoft.Extensions.Primitives;
using OpenIddict.Abstractions;
using SR = OpenIddict.Abstractions.OpenIddictResources;
namespace OpenIddict.Core
namespace OpenIddict.Core;
/// <summary>
/// Provides methods allowing to cache authorizations after retrieving them from the store.
/// </summary>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
public class OpenIddictAuthorizationCache<TAuthorization> : IOpenIddictAuthorizationCache<TAuthorization>, IDisposable where TAuthorization : class
{
/// <summary>
/// Provides methods allowing to cache authorizations after retrieving them from the store.
/// </summary>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
public class OpenIddictAuthorizationCache<TAuthorization> : IOpenIddictAuthorizationCache<TAuthorization>, IDisposable where TAuthorization : class
{
private readonly MemoryCache _cache;
private readonly ConcurrentDictionary<string, CancellationTokenSource> _signals;
private readonly IOpenIddictAuthorizationStore<TAuthorization> _store;
private readonly MemoryCache _cache;
private readonly ConcurrentDictionary<string, CancellationTokenSource> _signals;
private readonly IOpenIddictAuthorizationStore<TAuthorization> _store;
public OpenIddictAuthorizationCache(
IOptionsMonitor<OpenIddictCoreOptions> options,
IOpenIddictAuthorizationStoreResolver resolver)
public OpenIddictAuthorizationCache(
IOptionsMonitor<OpenIddictCoreOptions> options,
IOpenIddictAuthorizationStoreResolver resolver)
{
_cache = new MemoryCache(new MemoryCacheOptions
{
_cache = new MemoryCache(new MemoryCacheOptions
{
SizeLimit = options.CurrentValue.EntityCacheLimit
});
SizeLimit = options.CurrentValue.EntityCacheLimit
});
_signals = new ConcurrentDictionary<string, CancellationTokenSource>(StringComparer.Ordinal);
_store = resolver.Get<TAuthorization>();
}
_signals = new ConcurrentDictionary<string, CancellationTokenSource>(StringComparer.Ordinal);
_store = resolver.Get<TAuthorization>();
}
/// <inheritdoc/>
public async ValueTask AddAsync(TAuthorization authorization, CancellationToken cancellationToken)
/// <inheritdoc/>
public async ValueTask AddAsync(TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization is null)
{
if (authorization is null)
{
throw new ArgumentNullException(nameof(authorization));
}
throw new ArgumentNullException(nameof(authorization));
}
_cache.Remove(new
{
Method = nameof(FindAsync),
Subject = await _store.GetSubjectAsync(authorization, cancellationToken),
Client = await _store.GetApplicationIdAsync(authorization, cancellationToken)
});
_cache.Remove(new
{
Method = nameof(FindAsync),
Subject = await _store.GetSubjectAsync(authorization, cancellationToken),
Client = await _store.GetApplicationIdAsync(authorization, cancellationToken)
});
_cache.Remove(new
{
Method = nameof(FindAsync),
Subject = await _store.GetSubjectAsync(authorization, cancellationToken),
Client = await _store.GetApplicationIdAsync(authorization, cancellationToken),
Status = await _store.GetStatusAsync(authorization, cancellationToken)
});
_cache.Remove(new
{
Method = nameof(FindAsync),
Subject = await _store.GetSubjectAsync(authorization, cancellationToken),
Client = await _store.GetApplicationIdAsync(authorization, cancellationToken),
Status = await _store.GetStatusAsync(authorization, cancellationToken)
});
_cache.Remove(new
{
Method = nameof(FindAsync),
Subject = await _store.GetSubjectAsync(authorization, cancellationToken),
Client = await _store.GetApplicationIdAsync(authorization, cancellationToken),
Status = await _store.GetStatusAsync(authorization, cancellationToken),
Type = await _store.GetTypeAsync(authorization, cancellationToken)
});
_cache.Remove(new
{
Method = nameof(FindAsync),
Subject = await _store.GetSubjectAsync(authorization, cancellationToken),
Client = await _store.GetApplicationIdAsync(authorization, cancellationToken),
Status = await _store.GetStatusAsync(authorization, cancellationToken),
Type = await _store.GetTypeAsync(authorization, cancellationToken)
});
_cache.Remove(new
{
Method = nameof(FindByApplicationIdAsync),
Identifier = await _store.GetApplicationIdAsync(authorization, cancellationToken)
});
_cache.Remove(new
{
Method = nameof(FindByApplicationIdAsync),
Identifier = await _store.GetApplicationIdAsync(authorization, cancellationToken)
});
_cache.Remove(new
{
Method = nameof(FindByIdAsync),
Identifier = await _store.GetIdAsync(authorization, cancellationToken)
});
_cache.Remove(new
{
Method = nameof(FindByIdAsync),
Identifier = await _store.GetIdAsync(authorization, cancellationToken)
});
_cache.Remove(new
{
Method = nameof(FindBySubjectAsync),
Subject = await _store.GetSubjectAsync(authorization, cancellationToken)
});
_cache.Remove(new
{
Method = nameof(FindBySubjectAsync),
Subject = await _store.GetSubjectAsync(authorization, cancellationToken)
});
await CreateEntryAsync(new
{
Method = nameof(FindByIdAsync),
Identifier = await _store.GetIdAsync(authorization, cancellationToken)
}, authorization, cancellationToken);
}
await CreateEntryAsync(new
{
Method = nameof(FindByIdAsync),
Identifier = await _store.GetIdAsync(authorization, cancellationToken)
}, authorization, cancellationToken);
/// <inheritdoc/>
public void Dispose()
{
foreach (var signal in _signals)
{
signal.Value.Dispose();
}
/// <inheritdoc/>
public void Dispose()
_cache.Dispose();
}
/// <inheritdoc/>
public IAsyncEnumerable<TAuthorization> FindAsync(string subject, string client, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(subject))
{
foreach (var signal in _signals)
{
signal.Value.Dispose();
}
throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
}
_cache.Dispose();
if (string.IsNullOrEmpty(client))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
}
/// <inheritdoc/>
public IAsyncEnumerable<TAuthorization> FindAsync(string subject, string client, CancellationToken cancellationToken)
return ExecuteAsync(cancellationToken);
async IAsyncEnumerable<TAuthorization> ExecuteAsync([EnumeratorCancellation] CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(subject))
var parameters = new
{
throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
}
Method = nameof(FindAsync),
Subject = subject,
Client = client
};
if (string.IsNullOrEmpty(client))
if (!_cache.TryGetValue(parameters, out ImmutableArray<TAuthorization> authorizations))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
}
return ExecuteAsync(cancellationToken);
var builder = ImmutableArray.CreateBuilder<TAuthorization>();
async IAsyncEnumerable<TAuthorization> ExecuteAsync([EnumeratorCancellation] CancellationToken cancellationToken)
{
var parameters = new
await foreach (var authorization in _store.FindAsync(subject, client, cancellationToken))
{
Method = nameof(FindAsync),
Subject = subject,
Client = client
};
builder.Add(authorization);
if (!_cache.TryGetValue(parameters, out ImmutableArray<TAuthorization> authorizations))
{
var builder = ImmutableArray.CreateBuilder<TAuthorization>();
await AddAsync(authorization, cancellationToken);
}
await foreach (var authorization in _store.FindAsync(subject, client, cancellationToken))
{
builder.Add(authorization);
authorizations = builder.ToImmutable();
await AddAsync(authorization, cancellationToken);
}
await CreateEntryAsync(parameters, authorizations, cancellationToken);
}
authorizations = builder.ToImmutable();
foreach (var authorization in authorizations)
{
yield return authorization;
}
}
}
await CreateEntryAsync(parameters, authorizations, cancellationToken);
}
/// <inheritdoc/>
public IAsyncEnumerable<TAuthorization> FindAsync(
string subject, string client,
string status, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(subject))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
}
foreach (var authorization in authorizations)
{
yield return authorization;
}
}
if (string.IsNullOrEmpty(client))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
}
/// <inheritdoc/>
public IAsyncEnumerable<TAuthorization> FindAsync(
string subject, string client,
string status, CancellationToken cancellationToken)
if (string.IsNullOrEmpty(status))
{
if (string.IsNullOrEmpty(subject))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
}
throw new ArgumentException(SR.GetResourceString(SR.ID0199), nameof(status));
}
if (string.IsNullOrEmpty(client))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
}
return ExecuteAsync(cancellationToken);
if (string.IsNullOrEmpty(status))
async IAsyncEnumerable<TAuthorization> ExecuteAsync([EnumeratorCancellation] CancellationToken cancellationToken)
{
var parameters = new
{
throw new ArgumentException(SR.GetResourceString(SR.ID0199), nameof(status));
}
return ExecuteAsync(cancellationToken);
Method = nameof(FindAsync),
Subject = subject,
Client = client,
Status = status
};
async IAsyncEnumerable<TAuthorization> ExecuteAsync([EnumeratorCancellation] CancellationToken cancellationToken)
if (!_cache.TryGetValue(parameters, out ImmutableArray<TAuthorization> authorizations))
{
var parameters = new
{
Method = nameof(FindAsync),
Subject = subject,
Client = client,
Status = status
};
var builder = ImmutableArray.CreateBuilder<TAuthorization>();
if (!_cache.TryGetValue(parameters, out ImmutableArray<TAuthorization> authorizations))
await foreach (var authorization in _store.FindAsync(subject, client, status, cancellationToken))
{
var builder = ImmutableArray.CreateBuilder<TAuthorization>();
await foreach (var authorization in _store.FindAsync(subject, client, status, cancellationToken))
{
builder.Add(authorization);
builder.Add(authorization);
await AddAsync(authorization, cancellationToken);
}
authorizations = builder.ToImmutable();
await CreateEntryAsync(parameters, authorizations, cancellationToken);
await AddAsync(authorization, cancellationToken);
}
foreach (var authorization in authorizations)
{
yield return authorization;
}
}
}
authorizations = builder.ToImmutable();
/// <inheritdoc/>
public IAsyncEnumerable<TAuthorization> FindAsync(
string subject, string client,
string status, string type, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(subject))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
}
if (string.IsNullOrEmpty(client))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
}
if (string.IsNullOrEmpty(status))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0199), nameof(status));
await CreateEntryAsync(parameters, authorizations, cancellationToken);
}
if (string.IsNullOrEmpty(type))
foreach (var authorization in authorizations)
{
throw new ArgumentException(SR.GetResourceString(SR.ID0200), nameof(type));
yield return authorization;
}
}
}
return ExecuteAsync(cancellationToken);
async IAsyncEnumerable<TAuthorization> ExecuteAsync([EnumeratorCancellation] CancellationToken cancellationToken)
{
var parameters = new
{
Method = nameof(FindAsync),
Subject = subject,
Client = client,
Status = status,
Type = type
};
if (!_cache.TryGetValue(parameters, out ImmutableArray<TAuthorization> authorizations))
{
var builder = ImmutableArray.CreateBuilder<TAuthorization>();
await foreach (var authorization in _store.FindAsync(subject, client, status, type, cancellationToken))
{
builder.Add(authorization);
await AddAsync(authorization, cancellationToken);
}
authorizations = builder.ToImmutable();
/// <inheritdoc/>
public IAsyncEnumerable<TAuthorization> FindAsync(
string subject, string client,
string status, string type, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(subject))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
}
await CreateEntryAsync(parameters, authorizations, cancellationToken);
}
if (string.IsNullOrEmpty(client))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
}
foreach (var authorization in authorizations)
{
yield return authorization;
}
}
if (string.IsNullOrEmpty(status))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0199), nameof(status));
}
/// <inheritdoc/>
public IAsyncEnumerable<TAuthorization> FindAsync(
string subject, string client,
string status, string type,
ImmutableArray<string> scopes, CancellationToken cancellationToken)
if (string.IsNullOrEmpty(type))
{
if (string.IsNullOrEmpty(subject))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
}
throw new ArgumentException(SR.GetResourceString(SR.ID0200), nameof(type));
}
if (string.IsNullOrEmpty(client))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
}
return ExecuteAsync(cancellationToken);
if (string.IsNullOrEmpty(status))
async IAsyncEnumerable<TAuthorization> ExecuteAsync([EnumeratorCancellation] CancellationToken cancellationToken)
{
var parameters = new
{
throw new ArgumentException(SR.GetResourceString(SR.ID0199), nameof(status));
}
Method = nameof(FindAsync),
Subject = subject,
Client = client,
Status = status,
Type = type
};
if (string.IsNullOrEmpty(type))
if (!_cache.TryGetValue(parameters, out ImmutableArray<TAuthorization> authorizations))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0200), nameof(type));
}
var builder = ImmutableArray.CreateBuilder<TAuthorization>();
// Note: this method is only partially cached.
return ExecuteAsync(cancellationToken);
async IAsyncEnumerable<TAuthorization> ExecuteAsync([EnumeratorCancellation] CancellationToken cancellationToken)
{
await foreach (var authorization in _store.FindAsync(subject, client, status, type, scopes, cancellationToken))
await foreach (var authorization in _store.FindAsync(subject, client, status, type, cancellationToken))
{
await AddAsync(authorization, cancellationToken);
builder.Add(authorization);
yield return authorization;
await AddAsync(authorization, cancellationToken);
}
authorizations = builder.ToImmutable();
await CreateEntryAsync(parameters, authorizations, cancellationToken);
}
}
/// <inheritdoc/>
public IAsyncEnumerable<TAuthorization> FindByApplicationIdAsync(string identifier, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(identifier))
foreach (var authorization in authorizations)
{
throw new ArgumentException(SR.GetResourceString(SR.ID0195), nameof(identifier));
yield return authorization;
}
}
}
return ExecuteAsync(cancellationToken);
/// <inheritdoc/>
public IAsyncEnumerable<TAuthorization> FindAsync(
string subject, string client,
string status, string type,
ImmutableArray<string> scopes, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(subject))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
}
async IAsyncEnumerable<TAuthorization> ExecuteAsync([EnumeratorCancellation] CancellationToken cancellationToken)
{
var parameters = new
{
Method = nameof(FindByApplicationIdAsync),
Identifier = identifier
};
if (string.IsNullOrEmpty(client))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0124), nameof(client));
}
if (!_cache.TryGetValue(parameters, out ImmutableArray<TAuthorization> authorizations))
{
var builder = ImmutableArray.CreateBuilder<TAuthorization>();
if (string.IsNullOrEmpty(status))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0199), nameof(status));
}
await foreach (var authorization in _store.FindByApplicationIdAsync(identifier, cancellationToken))
{
builder.Add(authorization);
if (string.IsNullOrEmpty(type))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0200), nameof(type));
}
await AddAsync(authorization, cancellationToken);
}
// Note: this method is only partially cached.
authorizations = builder.ToImmutable();
return ExecuteAsync(cancellationToken);
await CreateEntryAsync(parameters, authorizations, cancellationToken);
}
async IAsyncEnumerable<TAuthorization> ExecuteAsync([EnumeratorCancellation] CancellationToken cancellationToken)
{
await foreach (var authorization in _store.FindAsync(subject, client, status, type, scopes, cancellationToken))
{
await AddAsync(authorization, cancellationToken);
foreach (var authorization in authorizations)
{
yield return authorization;
}
yield return authorization;
}
}
}
/// <inheritdoc/>
public ValueTask<TAuthorization?> FindByIdAsync(string identifier, CancellationToken cancellationToken)
/// <inheritdoc/>
public IAsyncEnumerable<TAuthorization> FindByApplicationIdAsync(string identifier, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(identifier))
{
if (string.IsNullOrEmpty(identifier))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0195), nameof(identifier));
}
throw new ArgumentException(SR.GetResourceString(SR.ID0195), nameof(identifier));
}
return ExecuteAsync(cancellationToken);
async IAsyncEnumerable<TAuthorization> ExecuteAsync([EnumeratorCancellation] CancellationToken cancellationToken)
{
var parameters = new
{
Method = nameof(FindByIdAsync),
Method = nameof(FindByApplicationIdAsync),
Identifier = identifier
};
if (_cache.TryGetValue(parameters, out TAuthorization? authorization))
if (!_cache.TryGetValue(parameters, out ImmutableArray<TAuthorization> authorizations))
{
return new ValueTask<TAuthorization?>(authorization);
}
var builder = ImmutableArray.CreateBuilder<TAuthorization>();
return new ValueTask<TAuthorization?>(ExecuteAsync());
async Task<TAuthorization?> ExecuteAsync()
{
if ((authorization = await _store.FindByIdAsync(identifier, cancellationToken)) is not null)
await foreach (var authorization in _store.FindByApplicationIdAsync(identifier, cancellationToken))
{
builder.Add(authorization);
await AddAsync(authorization, cancellationToken);
}
await CreateEntryAsync(parameters, authorization, cancellationToken);
authorizations = builder.ToImmutable();
return authorization;
await CreateEntryAsync(parameters, authorizations, cancellationToken);
}
}
/// <inheritdoc/>
public IAsyncEnumerable<TAuthorization> FindBySubjectAsync(string subject, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(subject))
foreach (var authorization in authorizations)
{
throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
yield return authorization;
}
}
}
return ExecuteAsync(cancellationToken);
/// <inheritdoc/>
public ValueTask<TAuthorization?> FindByIdAsync(string identifier, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(identifier))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0195), nameof(identifier));
}
async IAsyncEnumerable<TAuthorization> ExecuteAsync([EnumeratorCancellation] CancellationToken cancellationToken)
{
var parameters = new
{
Method = nameof(FindBySubjectAsync),
Subject = subject
};
var parameters = new
{
Method = nameof(FindByIdAsync),
Identifier = identifier
};
if (!_cache.TryGetValue(parameters, out ImmutableArray<TAuthorization> authorizations))
{
var builder = ImmutableArray.CreateBuilder<TAuthorization>();
if (_cache.TryGetValue(parameters, out TAuthorization? authorization))
{
return new ValueTask<TAuthorization?>(authorization);
}
await foreach (var authorization in _store.FindBySubjectAsync(subject, cancellationToken))
{
builder.Add(authorization);
return new ValueTask<TAuthorization?>(ExecuteAsync());
await AddAsync(authorization, cancellationToken);
}
async Task<TAuthorization?> ExecuteAsync()
{
if ((authorization = await _store.FindByIdAsync(identifier, cancellationToken)) is not null)
{
await AddAsync(authorization, cancellationToken);
}
authorizations = builder.ToImmutable();
await CreateEntryAsync(parameters, authorization, cancellationToken);
await CreateEntryAsync(parameters, authorizations, cancellationToken);
}
return authorization;
}
}
foreach (var authorization in authorizations)
{
yield return authorization;
}
}
/// <inheritdoc/>
public IAsyncEnumerable<TAuthorization> FindBySubjectAsync(string subject, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(subject))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0198), nameof(subject));
}
/// <inheritdoc/>
public async ValueTask RemoveAsync(TAuthorization authorization, CancellationToken cancellationToken)
return ExecuteAsync(cancellationToken);
async IAsyncEnumerable<TAuthorization> ExecuteAsync([EnumeratorCancellation] CancellationToken cancellationToken)
{
if (authorization is null)
var parameters = new
{
throw new ArgumentNullException(nameof(authorization));
}
Method = nameof(FindBySubjectAsync),
Subject = subject
};
var identifier = await _store.GetIdAsync(authorization, cancellationToken);
if (string.IsNullOrEmpty(identifier))
if (!_cache.TryGetValue(parameters, out ImmutableArray<TAuthorization> authorizations))
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0196));
var builder = ImmutableArray.CreateBuilder<TAuthorization>();
await foreach (var authorization in _store.FindBySubjectAsync(subject, cancellationToken))
{
builder.Add(authorization);
await AddAsync(authorization, cancellationToken);
}
authorizations = builder.ToImmutable();
await CreateEntryAsync(parameters, authorizations, cancellationToken);
}
if (_signals.TryRemove(identifier, out CancellationTokenSource? signal))
foreach (var authorization in authorizations)
{
signal.Cancel();
signal.Dispose();
yield return authorization;
}
}
}
/// <summary>
/// Creates a cache entry for the specified key.
/// </summary>
/// <param name="key">The cache key.</param>
/// <param name="authorization">The authorization to store in the cache entry, if applicable.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
protected virtual async ValueTask CreateEntryAsync(object key, TAuthorization? authorization, CancellationToken cancellationToken)
/// <inheritdoc/>
public async ValueTask RemoveAsync(TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization is null)
{
if (key is null)
{
throw new ArgumentNullException(nameof(key));
}
using var entry = _cache.CreateEntry(key);
throw new ArgumentNullException(nameof(authorization));
}
if (authorization is not null)
{
var signal = await CreateExpirationSignalAsync(authorization, cancellationToken);
if (signal is null)
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0197));
}
var identifier = await _store.GetIdAsync(authorization, cancellationToken);
if (string.IsNullOrEmpty(identifier))
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0196));
}
entry.AddExpirationToken(signal);
}
if (_signals.TryRemove(identifier, out CancellationTokenSource? signal))
{
signal.Cancel();
signal.Dispose();
}
}
entry.SetSize(1L);
entry.SetValue(authorization);
/// <summary>
/// Creates a cache entry for the specified key.
/// </summary>
/// <param name="key">The cache key.</param>
/// <param name="authorization">The authorization to store in the cache entry, if applicable.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
protected virtual async ValueTask CreateEntryAsync(object key, TAuthorization? authorization, CancellationToken cancellationToken)
{
if (key is null)
{
throw new ArgumentNullException(nameof(key));
}
/// <summary>
/// Creates a cache entry for the specified key.
/// </summary>
/// <param name="key">The cache key.</param>
/// <param name="authorizations">The authorizations to store in the cache entry.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
protected virtual async ValueTask CreateEntryAsync(
object key, ImmutableArray<TAuthorization> authorizations, CancellationToken cancellationToken)
using var entry = _cache.CreateEntry(key);
if (authorization is not null)
{
if (key is null)
var signal = await CreateExpirationSignalAsync(authorization, cancellationToken);
if (signal is null)
{
throw new ArgumentNullException(nameof(key));
throw new InvalidOperationException(SR.GetResourceString(SR.ID0197));
}
using var entry = _cache.CreateEntry(key);
foreach (var authorization in authorizations)
{
var signal = await CreateExpirationSignalAsync(authorization, cancellationToken);
if (signal is null)
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0197));
}
entry.AddExpirationToken(signal);
}
entry.AddExpirationToken(signal);
}
entry.SetSize(1L);
entry.SetValue(authorization);
}
entry.SetSize(authorizations.Length);
entry.SetValue(authorizations);
/// <summary>
/// Creates a cache entry for the specified key.
/// </summary>
/// <param name="key">The cache key.</param>
/// <param name="authorizations">The authorizations to store in the cache entry.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
protected virtual async ValueTask CreateEntryAsync(
object key, ImmutableArray<TAuthorization> authorizations, CancellationToken cancellationToken)
{
if (key is null)
{
throw new ArgumentNullException(nameof(key));
}
/// <summary>
/// Creates an expiration signal allowing to invalidate all the
/// cache entries associated with the specified authorization.
/// </summary>
/// <param name="authorization">The authorization associated with the expiration signal.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns an expiration signal for the specified authorization.
/// </returns>
protected virtual async ValueTask<IChangeToken> CreateExpirationSignalAsync(
TAuthorization authorization, CancellationToken cancellationToken)
using var entry = _cache.CreateEntry(key);
foreach (var authorization in authorizations)
{
if (authorization is null)
var signal = await CreateExpirationSignalAsync(authorization, cancellationToken);
if (signal is null)
{
throw new ArgumentNullException(nameof(authorization));
throw new InvalidOperationException(SR.GetResourceString(SR.ID0197));
}
var identifier = await _store.GetIdAsync(authorization, cancellationToken);
if (string.IsNullOrEmpty(identifier))
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0201));
}
entry.AddExpirationToken(signal);
}
var signal = _signals.GetOrAdd(identifier, _ => new CancellationTokenSource());
entry.SetSize(authorizations.Length);
entry.SetValue(authorizations);
}
return new CancellationChangeToken(signal.Token);
/// <summary>
/// Creates an expiration signal allowing to invalidate all the
/// cache entries associated with the specified authorization.
/// </summary>
/// <param name="authorization">The authorization associated with the expiration signal.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns an expiration signal for the specified authorization.
/// </returns>
protected virtual async ValueTask<IChangeToken> CreateExpirationSignalAsync(
TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization is null)
{
throw new ArgumentNullException(nameof(authorization));
}
var identifier = await _store.GetIdAsync(authorization, cancellationToken);
if (string.IsNullOrEmpty(identifier))
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0201));
}
var signal = _signals.GetOrAdd(identifier, _ => new CancellationTokenSource());
return new CancellationChangeToken(signal.Token);
}
}

469
src/OpenIddict.Core/Caches/OpenIddictScopeCache.cs

@ -18,326 +18,325 @@ using Microsoft.Extensions.Primitives;
using OpenIddict.Abstractions;
using SR = OpenIddict.Abstractions.OpenIddictResources;
namespace OpenIddict.Core
namespace OpenIddict.Core;
/// <summary>
/// Provides methods allowing to cache scopes after retrieving them from the store.
/// </summary>
/// <typeparam name="TScope">The type of the Scope entity.</typeparam>
public class OpenIddictScopeCache<TScope> : IOpenIddictScopeCache<TScope>, IDisposable where TScope : class
{
/// <summary>
/// Provides methods allowing to cache scopes after retrieving them from the store.
/// </summary>
/// <typeparam name="TScope">The type of the Scope entity.</typeparam>
public class OpenIddictScopeCache<TScope> : IOpenIddictScopeCache<TScope>, IDisposable where TScope : class
{
private readonly MemoryCache _cache;
private readonly ConcurrentDictionary<string, CancellationTokenSource> _signals;
private readonly IOpenIddictScopeStore<TScope> _store;
private readonly MemoryCache _cache;
private readonly ConcurrentDictionary<string, CancellationTokenSource> _signals;
private readonly IOpenIddictScopeStore<TScope> _store;
public OpenIddictScopeCache(
IOptionsMonitor<OpenIddictCoreOptions> options,
IOpenIddictScopeStoreResolver resolver)
public OpenIddictScopeCache(
IOptionsMonitor<OpenIddictCoreOptions> options,
IOpenIddictScopeStoreResolver resolver)
{
_cache = new MemoryCache(new MemoryCacheOptions
{
_cache = new MemoryCache(new MemoryCacheOptions
{
SizeLimit = options.CurrentValue.EntityCacheLimit
});
SizeLimit = options.CurrentValue.EntityCacheLimit
});
_signals = new ConcurrentDictionary<string, CancellationTokenSource>(StringComparer.Ordinal);
_store = resolver.Get<TScope>();
_signals = new ConcurrentDictionary<string, CancellationTokenSource>(StringComparer.Ordinal);
_store = resolver.Get<TScope>();
}
/// <inheritdoc/>
public async ValueTask AddAsync(TScope scope, CancellationToken cancellationToken)
{
if (scope is null)
{
throw new ArgumentNullException(nameof(scope));
}
/// <inheritdoc/>
public async ValueTask AddAsync(TScope scope, CancellationToken cancellationToken)
_cache.Remove(new
{
if (scope is null)
{
throw new ArgumentNullException(nameof(scope));
}
Method = nameof(FindByIdAsync),
Identifier = await _store.GetIdAsync(scope, cancellationToken)
});
_cache.Remove(new
{
Method = nameof(FindByIdAsync),
Identifier = await _store.GetIdAsync(scope, cancellationToken)
});
_cache.Remove(new
{
Method = nameof(FindByNameAsync),
Name = await _store.GetNameAsync(scope, cancellationToken)
});
foreach (var resource in await _store.GetResourcesAsync(scope, cancellationToken))
{
_cache.Remove(new
{
Method = nameof(FindByNameAsync),
Name = await _store.GetNameAsync(scope, cancellationToken)
Method = nameof(FindByResourceAsync),
Resource = resource
});
}
foreach (var resource in await _store.GetResourcesAsync(scope, cancellationToken))
{
_cache.Remove(new
{
Method = nameof(FindByResourceAsync),
Resource = resource
});
}
await CreateEntryAsync(new
{
Method = nameof(FindByIdAsync),
Identifier = await _store.GetIdAsync(scope, cancellationToken)
}, scope, cancellationToken);
await CreateEntryAsync(new
{
Method = nameof(FindByIdAsync),
Identifier = await _store.GetIdAsync(scope, cancellationToken)
}, scope, cancellationToken);
await CreateEntryAsync(new
{
Method = nameof(FindByNameAsync),
Name = await _store.GetNameAsync(scope, cancellationToken)
}, scope, cancellationToken);
}
await CreateEntryAsync(new
{
Method = nameof(FindByNameAsync),
Name = await _store.GetNameAsync(scope, cancellationToken)
}, scope, cancellationToken);
/// <inheritdoc/>
public void Dispose()
{
foreach (var signal in _signals)
{
signal.Value.Dispose();
}
/// <inheritdoc/>
public void Dispose()
{
foreach (var signal in _signals)
{
signal.Value.Dispose();
}
_cache.Dispose();
}
_cache.Dispose();
/// <inheritdoc/>
public ValueTask<TScope?> FindByIdAsync(string identifier, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(identifier))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0195), nameof(identifier));
}
/// <inheritdoc/>
public ValueTask<TScope?> FindByIdAsync(string identifier, CancellationToken cancellationToken)
var parameters = new
{
if (string.IsNullOrEmpty(identifier))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0195), nameof(identifier));
}
Method = nameof(FindByIdAsync),
Identifier = identifier
};
var parameters = new
{
Method = nameof(FindByIdAsync),
Identifier = identifier
};
if (_cache.TryGetValue(parameters, out TScope? scope))
{
return new ValueTask<TScope?>(scope);
}
if (_cache.TryGetValue(parameters, out TScope? scope))
return new ValueTask<TScope?>(ExecuteAsync());
async Task<TScope?> ExecuteAsync()
{
if ((scope = await _store.FindByIdAsync(identifier, cancellationToken)) is not null)
{
return new ValueTask<TScope?>(scope);
await AddAsync(scope, cancellationToken);
}
return new ValueTask<TScope?>(ExecuteAsync());
await CreateEntryAsync(parameters, scope, cancellationToken);
async Task<TScope?> ExecuteAsync()
{
if ((scope = await _store.FindByIdAsync(identifier, cancellationToken)) is not null)
{
await AddAsync(scope, cancellationToken);
}
await CreateEntryAsync(parameters, scope, cancellationToken);
return scope;
}
}
return scope;
}
/// <inheritdoc/>
public ValueTask<TScope?> FindByNameAsync(string name, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0202), nameof(name));
}
/// <inheritdoc/>
public ValueTask<TScope?> FindByNameAsync(string name, CancellationToken cancellationToken)
var parameters = new
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0202), nameof(name));
}
Method = nameof(FindByNameAsync),
Name = name
};
var parameters = new
{
Method = nameof(FindByNameAsync),
Name = name
};
if (_cache.TryGetValue(parameters, out TScope? scope))
{
return new ValueTask<TScope?>(scope);
}
if (_cache.TryGetValue(parameters, out TScope? scope))
async Task<TScope?> ExecuteAsync()
{
if ((scope = await _store.FindByNameAsync(name, cancellationToken)) is not null)
{
return new ValueTask<TScope?>(scope);
await AddAsync(scope, cancellationToken);
}
async Task<TScope?> ExecuteAsync()
{
if ((scope = await _store.FindByNameAsync(name, cancellationToken)) is not null)
{
await AddAsync(scope, cancellationToken);
}
await CreateEntryAsync(parameters, scope, cancellationToken);
await CreateEntryAsync(parameters, scope, cancellationToken);
return scope;
}
return new ValueTask<TScope?>(ExecuteAsync());
return scope;
}
/// <inheritdoc/>
public IAsyncEnumerable<TScope> FindByNamesAsync(ImmutableArray<string> names, CancellationToken cancellationToken)
return new ValueTask<TScope?>(ExecuteAsync());
}
/// <inheritdoc/>
public IAsyncEnumerable<TScope> FindByNamesAsync(ImmutableArray<string> names, CancellationToken cancellationToken)
{
if (names.Any(name => string.IsNullOrEmpty(name)))
{
if (names.Any(name => string.IsNullOrEmpty(name)))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0203), nameof(names));
}
throw new ArgumentException(SR.GetResourceString(SR.ID0203), nameof(names));
}
// Note: this method is only partially cached.
// Note: this method is only partially cached.
return ExecuteAsync(cancellationToken);
return ExecuteAsync(cancellationToken);
async IAsyncEnumerable<TScope> ExecuteAsync([EnumeratorCancellation] CancellationToken cancellationToken)
async IAsyncEnumerable<TScope> ExecuteAsync([EnumeratorCancellation] CancellationToken cancellationToken)
{
await foreach (var scope in _store.FindByNamesAsync(names, cancellationToken))
{
await foreach (var scope in _store.FindByNamesAsync(names, cancellationToken))
{
await AddAsync(scope, cancellationToken);
await AddAsync(scope, cancellationToken);
yield return scope;
}
yield return scope;
}
}
}
/// <inheritdoc/>
public IAsyncEnumerable<TScope> FindByResourceAsync(string resource, CancellationToken cancellationToken)
/// <inheritdoc/>
public IAsyncEnumerable<TScope> FindByResourceAsync(string resource, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(resource))
{
if (string.IsNullOrEmpty(resource))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0062), nameof(resource));
}
throw new ArgumentException(SR.GetResourceString(SR.ID0062), nameof(resource));
}
return ExecuteAsync(cancellationToken);
return ExecuteAsync(cancellationToken);
async IAsyncEnumerable<TScope> ExecuteAsync([EnumeratorCancellation] CancellationToken cancellationToken)
async IAsyncEnumerable<TScope> ExecuteAsync([EnumeratorCancellation] CancellationToken cancellationToken)
{
var parameters = new
{
var parameters = new
{
Method = nameof(FindByResourceAsync),
Resource = resource
};
Method = nameof(FindByResourceAsync),
Resource = resource
};
if (!_cache.TryGetValue(parameters, out ImmutableArray<TScope> scopes))
{
var builder = ImmutableArray.CreateBuilder<TScope>();
if (!_cache.TryGetValue(parameters, out ImmutableArray<TScope> scopes))
{
var builder = ImmutableArray.CreateBuilder<TScope>();
await foreach (var scope in _store.FindByResourceAsync(resource, cancellationToken))
{
builder.Add(scope);
await foreach (var scope in _store.FindByResourceAsync(resource, cancellationToken))
{
builder.Add(scope);
await AddAsync(scope, cancellationToken);
}
await AddAsync(scope, cancellationToken);
}
scopes = builder.ToImmutable();
scopes = builder.ToImmutable();
await CreateEntryAsync(parameters, scopes, cancellationToken);
}
await CreateEntryAsync(parameters, scopes, cancellationToken);
}
foreach (var scope in scopes)
{
yield return scope;
}
foreach (var scope in scopes)
{
yield return scope;
}
}
}
/// <inheritdoc/>
public async ValueTask RemoveAsync(TScope scope, CancellationToken cancellationToken)
/// <inheritdoc/>
public async ValueTask RemoveAsync(TScope scope, CancellationToken cancellationToken)
{
if (scope is null)
{
if (scope is null)
{
throw new ArgumentNullException(nameof(scope));
}
throw new ArgumentNullException(nameof(scope));
}
var identifier = await _store.GetIdAsync(scope, cancellationToken);
if (string.IsNullOrEmpty(identifier))
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0196));
}
var identifier = await _store.GetIdAsync(scope, cancellationToken);
if (string.IsNullOrEmpty(identifier))
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0196));
}
if (_signals.TryRemove(identifier, out CancellationTokenSource? signal))
{
signal.Cancel();
signal.Dispose();
}
if (_signals.TryRemove(identifier, out CancellationTokenSource? signal))
{
signal.Cancel();
signal.Dispose();
}
}
/// <summary>
/// Creates a cache entry for the specified key.
/// </summary>
/// <param name="key">The cache key.</param>
/// <param name="scope">The scope to store in the cache entry, if applicable.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
protected virtual async ValueTask CreateEntryAsync(object key, TScope? scope, CancellationToken cancellationToken)
/// <summary>
/// Creates a cache entry for the specified key.
/// </summary>
/// <param name="key">The cache key.</param>
/// <param name="scope">The scope to store in the cache entry, if applicable.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
protected virtual async ValueTask CreateEntryAsync(object key, TScope? scope, CancellationToken cancellationToken)
{
if (key is null)
{
if (key is null)
{
throw new ArgumentNullException(nameof(key));
}
throw new ArgumentNullException(nameof(key));
}
using var entry = _cache.CreateEntry(key);
using var entry = _cache.CreateEntry(key);
if (scope is not null)
if (scope is not null)
{
var signal = await CreateExpirationSignalAsync(scope, cancellationToken);
if (signal is null)
{
var signal = await CreateExpirationSignalAsync(scope, cancellationToken);
if (signal is null)
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0197));
}
entry.AddExpirationToken(signal);
throw new InvalidOperationException(SR.GetResourceString(SR.ID0197));
}
entry.SetSize(1L);
entry.SetValue(scope);
entry.AddExpirationToken(signal);
}
/// <summary>
/// Creates a cache entry for the specified key.
/// </summary>
/// <param name="key">The cache key.</param>
/// <param name="scopes">The scopes to store in the cache entry.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
protected virtual async ValueTask CreateEntryAsync(
object key, ImmutableArray<TScope> scopes, CancellationToken cancellationToken)
entry.SetSize(1L);
entry.SetValue(scope);
}
/// <summary>
/// Creates a cache entry for the specified key.
/// </summary>
/// <param name="key">The cache key.</param>
/// <param name="scopes">The scopes to store in the cache entry.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
protected virtual async ValueTask CreateEntryAsync(
object key, ImmutableArray<TScope> scopes, CancellationToken cancellationToken)
{
if (key is null)
{
if (key is null)
{
throw new ArgumentNullException(nameof(key));
}
throw new ArgumentNullException(nameof(key));
}
using var entry = _cache.CreateEntry(key);
using var entry = _cache.CreateEntry(key);
foreach (var scope in scopes)
foreach (var scope in scopes)
{
var signal = await CreateExpirationSignalAsync(scope, cancellationToken);
if (signal is null)
{
var signal = await CreateExpirationSignalAsync(scope, cancellationToken);
if (signal is null)
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0197));
}
entry.AddExpirationToken(signal);
throw new InvalidOperationException(SR.GetResourceString(SR.ID0197));
}
entry.SetSize(scopes.Length);
entry.SetValue(scopes);
entry.AddExpirationToken(signal);
}
/// <summary>
/// Creates an expiration signal allowing to invalidate all the
/// cache entries associated with the specified scope.
/// </summary>
/// <param name="scope">The scope associated with the expiration signal.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns an expiration signal for the specified scope.
/// </returns>
protected virtual async ValueTask<IChangeToken> CreateExpirationSignalAsync(TScope scope, CancellationToken cancellationToken)
entry.SetSize(scopes.Length);
entry.SetValue(scopes);
}
/// <summary>
/// Creates an expiration signal allowing to invalidate all the
/// cache entries associated with the specified scope.
/// </summary>
/// <param name="scope">The scope associated with the expiration signal.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns an expiration signal for the specified scope.
/// </returns>
protected virtual async ValueTask<IChangeToken> CreateExpirationSignalAsync(TScope scope, CancellationToken cancellationToken)
{
if (scope is null)
{
if (scope is null)
{
throw new ArgumentNullException(nameof(scope));
}
throw new ArgumentNullException(nameof(scope));
}
var identifier = await _store.GetIdAsync(scope, cancellationToken);
if (string.IsNullOrEmpty(identifier))
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0204));
}
var identifier = await _store.GetIdAsync(scope, cancellationToken);
if (string.IsNullOrEmpty(identifier))
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0204));
}
var signal = _signals.GetOrAdd(identifier, _ => new CancellationTokenSource());
var signal = _signals.GetOrAdd(identifier, _ => new CancellationTokenSource());
return new CancellationChangeToken(signal.Token);
}
return new CancellationChangeToken(signal.Token);
}
}

845
src/OpenIddict.Core/Caches/OpenIddictTokenCache.cs

File diff suppressed because it is too large

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

File diff suppressed because it is too large

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

File diff suppressed because it is too large

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

File diff suppressed because it is too large

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

File diff suppressed because it is too large

1371
src/OpenIddict.Core/OpenIddictCoreBuilder.cs

File diff suppressed because it is too large

177
src/OpenIddict.Core/OpenIddictCoreExtensions.cs

@ -10,120 +10,119 @@ using OpenIddict.Abstractions;
using OpenIddict.Core;
using SR = OpenIddict.Abstractions.OpenIddictResources;
namespace Microsoft.Extensions.DependencyInjection
{
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
/// <summary>
/// Exposes extensions allowing to register the OpenIddict core services.
/// </summary>
public static class OpenIddictCoreExtensions
{
/// <summary>
/// Exposes extensions allowing to register the OpenIddict core services.
/// Registers the OpenIddict core services in the DI container.
/// </summary>
public static class OpenIddictCoreExtensions
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictCoreBuilder AddCore(this OpenIddictBuilder builder)
{
/// <summary>
/// Registers the OpenIddict core services in the DI container.
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictCoreBuilder AddCore(this OpenIddictBuilder builder)
if (builder is null)
{
if (builder is null)
{
throw new ArgumentNullException(nameof(builder));
}
throw new ArgumentNullException(nameof(builder));
}
builder.Services.AddLogging();
builder.Services.AddMemoryCache();
builder.Services.AddOptions();
builder.Services.AddLogging();
builder.Services.AddMemoryCache();
builder.Services.AddOptions();
builder.Services.TryAddScoped(typeof(OpenIddictApplicationManager<>));
builder.Services.TryAddScoped(typeof(OpenIddictAuthorizationManager<>));
builder.Services.TryAddScoped(typeof(OpenIddictScopeManager<>));
builder.Services.TryAddScoped(typeof(OpenIddictTokenManager<>));
builder.Services.TryAddScoped(typeof(OpenIddictApplicationManager<>));
builder.Services.TryAddScoped(typeof(OpenIddictAuthorizationManager<>));
builder.Services.TryAddScoped(typeof(OpenIddictScopeManager<>));
builder.Services.TryAddScoped(typeof(OpenIddictTokenManager<>));
builder.Services.TryAddScoped(typeof(IOpenIddictApplicationCache<>), typeof(OpenIddictApplicationCache<>));
builder.Services.TryAddScoped(typeof(IOpenIddictAuthorizationCache<>), typeof(OpenIddictAuthorizationCache<>));
builder.Services.TryAddScoped(typeof(IOpenIddictScopeCache<>), typeof(OpenIddictScopeCache<>));
builder.Services.TryAddScoped(typeof(IOpenIddictTokenCache<>), typeof(OpenIddictTokenCache<>));
builder.Services.TryAddScoped(typeof(IOpenIddictApplicationCache<>), typeof(OpenIddictApplicationCache<>));
builder.Services.TryAddScoped(typeof(IOpenIddictAuthorizationCache<>), typeof(OpenIddictAuthorizationCache<>));
builder.Services.TryAddScoped(typeof(IOpenIddictScopeCache<>), typeof(OpenIddictScopeCache<>));
builder.Services.TryAddScoped(typeof(IOpenIddictTokenCache<>), typeof(OpenIddictTokenCache<>));
builder.Services.TryAddScoped<IOpenIddictApplicationStoreResolver, OpenIddictApplicationStoreResolver>();
builder.Services.TryAddScoped<IOpenIddictAuthorizationStoreResolver, OpenIddictAuthorizationStoreResolver>();
builder.Services.TryAddScoped<IOpenIddictScopeStoreResolver, OpenIddictScopeStoreResolver>();
builder.Services.TryAddScoped<IOpenIddictTokenStoreResolver, OpenIddictTokenStoreResolver>();
builder.Services.TryAddScoped<IOpenIddictApplicationStoreResolver, OpenIddictApplicationStoreResolver>();
builder.Services.TryAddScoped<IOpenIddictAuthorizationStoreResolver, OpenIddictAuthorizationStoreResolver>();
builder.Services.TryAddScoped<IOpenIddictScopeStoreResolver, OpenIddictScopeStoreResolver>();
builder.Services.TryAddScoped<IOpenIddictTokenStoreResolver, OpenIddictTokenStoreResolver>();
builder.Services.TryAddScoped(provider =>
builder.Services.TryAddScoped(provider =>
{
var options = provider.GetRequiredService<IOptionsMonitor<OpenIddictCoreOptions>>().CurrentValue;
if (options.DefaultApplicationType is null)
{
var options = provider.GetRequiredService<IOptionsMonitor<OpenIddictCoreOptions>>().CurrentValue;
if (options.DefaultApplicationType is null)
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0273));
}
throw new InvalidOperationException(SR.GetResourceString(SR.ID0273));
}
return (IOpenIddictApplicationManager) provider.GetRequiredService(
typeof(OpenIddictApplicationManager<>).MakeGenericType(options.DefaultApplicationType));
});
return (IOpenIddictApplicationManager) provider.GetRequiredService(
typeof(OpenIddictApplicationManager<>).MakeGenericType(options.DefaultApplicationType));
});
builder.Services.TryAddScoped(provider =>
builder.Services.TryAddScoped(provider =>
{
var options = provider.GetRequiredService<IOptionsMonitor<OpenIddictCoreOptions>>().CurrentValue;
if (options.DefaultAuthorizationType is null)
{
var options = provider.GetRequiredService<IOptionsMonitor<OpenIddictCoreOptions>>().CurrentValue;
if (options.DefaultAuthorizationType is null)
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0274));
}
throw new InvalidOperationException(SR.GetResourceString(SR.ID0274));
}
return (IOpenIddictAuthorizationManager) provider.GetRequiredService(
typeof(OpenIddictAuthorizationManager<>).MakeGenericType(options.DefaultAuthorizationType));
});
return (IOpenIddictAuthorizationManager) provider.GetRequiredService(
typeof(OpenIddictAuthorizationManager<>).MakeGenericType(options.DefaultAuthorizationType));
});
builder.Services.TryAddScoped(provider =>
builder.Services.TryAddScoped(provider =>
{
var options = provider.GetRequiredService<IOptionsMonitor<OpenIddictCoreOptions>>().CurrentValue;
if (options.DefaultScopeType is null)
{
var options = provider.GetRequiredService<IOptionsMonitor<OpenIddictCoreOptions>>().CurrentValue;
if (options.DefaultScopeType is null)
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0275));
}
throw new InvalidOperationException(SR.GetResourceString(SR.ID0275));
}
return (IOpenIddictScopeManager) provider.GetRequiredService(
typeof(OpenIddictScopeManager<>).MakeGenericType(options.DefaultScopeType));
});
return (IOpenIddictScopeManager) provider.GetRequiredService(
typeof(OpenIddictScopeManager<>).MakeGenericType(options.DefaultScopeType));
});
builder.Services.TryAddScoped(provider =>
builder.Services.TryAddScoped(provider =>
{
var options = provider.GetRequiredService<IOptionsMonitor<OpenIddictCoreOptions>>().CurrentValue;
if (options.DefaultTokenType is null)
{
var options = provider.GetRequiredService<IOptionsMonitor<OpenIddictCoreOptions>>().CurrentValue;
if (options.DefaultTokenType is null)
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0276));
}
throw new InvalidOperationException(SR.GetResourceString(SR.ID0276));
}
return (IOpenIddictTokenManager) provider.GetRequiredService(
typeof(OpenIddictTokenManager<>).MakeGenericType(options.DefaultTokenType));
});
return (IOpenIddictTokenManager) provider.GetRequiredService(
typeof(OpenIddictTokenManager<>).MakeGenericType(options.DefaultTokenType));
});
return new OpenIddictCoreBuilder(builder.Services);
}
return new OpenIddictCoreBuilder(builder.Services);
}
/// <summary>
/// Registers the OpenIddict core services in the DI container.
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <param name="configuration">The configuration delegate used to configure the core services.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddCore(this OpenIddictBuilder builder, Action<OpenIddictCoreBuilder> configuration)
/// <summary>
/// Registers the OpenIddict core services in the DI container.
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <param name="configuration">The configuration delegate used to configure the core services.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddCore(this OpenIddictBuilder builder, Action<OpenIddictCoreBuilder> configuration)
{
if (builder is null)
{
if (builder is null)
{
throw new ArgumentNullException(nameof(builder));
}
throw new ArgumentNullException(nameof(builder));
}
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
configuration(builder.AddCore());
configuration(builder.AddCore());
return builder;
}
return builder;
}
}
}

95
src/OpenIddict.Core/OpenIddictCoreOptions.cs

@ -6,60 +6,59 @@
using System;
namespace OpenIddict.Core
namespace OpenIddict.Core;
/// <summary>
/// Provides various settings needed to configure the OpenIddict core services.
/// </summary>
public class OpenIddictCoreOptions
{
/// <summary>
/// Provides various settings needed to configure the OpenIddict core services.
/// Gets or sets the type corresponding to the default Application entity,
/// used by the non-generic application manager and the server/validation services.
/// </summary>
public class OpenIddictCoreOptions
{
/// <summary>
/// Gets or sets the type corresponding to the default Application entity,
/// used by the non-generic application manager and the server/validation services.
/// </summary>
public Type? DefaultApplicationType { get; set; }
public Type? DefaultApplicationType { get; set; }
/// <summary>
/// Gets or sets the type corresponding to the default Authorization entity,
/// used by the non-generic authorization manager and the server/validation services.
/// </summary>
public Type? DefaultAuthorizationType { get; set; }
/// <summary>
/// Gets or sets the type corresponding to the default Authorization entity,
/// used by the non-generic authorization manager and the server/validation services.
/// </summary>
public Type? DefaultAuthorizationType { get; set; }
/// <summary>
/// Gets or sets the type corresponding to the default Scope entity,
/// used by the non-generic scope manager and the server/validation services.
/// </summary>
public Type? DefaultScopeType { get; set; }
/// <summary>
/// Gets or sets the type corresponding to the default Scope entity,
/// used by the non-generic scope manager and the server/validation services.
/// </summary>
public Type? DefaultScopeType { get; set; }
/// <summary>
/// Gets or sets the type corresponding to the default Token entity,
/// used by the non-generic token manager and the server/validation services.
/// </summary>
public Type? DefaultTokenType { get; set; }
/// <summary>
/// Gets or sets the type corresponding to the default Token entity,
/// used by the non-generic token manager and the server/validation services.
/// </summary>
public Type? DefaultTokenType { get; set; }
/// <summary>
/// Gets or sets a boolean indicating whether additional filtering should be disabled,
/// so that the OpenIddict managers don't execute a second check to ensure the results
/// returned by the stores exactly match the specified query filters, casing included.
/// This property SHOULD NOT be set to <c>true</c> except when the underlying stores
/// are guaranteed to execute case-sensitive filtering at the database level.
/// Disabling this feature MAY result in security vulnerabilities in the other cases.
/// </summary>
public bool DisableAdditionalFiltering { get; set; }
/// <summary>
/// Gets or sets a boolean indicating whether additional filtering should be disabled,
/// so that the OpenIddict managers don't execute a second check to ensure the results
/// returned by the stores exactly match the specified query filters, casing included.
/// This property SHOULD NOT be set to <c>true</c> except when the underlying stores
/// are guaranteed to execute case-sensitive filtering at the database level.
/// Disabling this feature MAY result in security vulnerabilities in the other cases.
/// </summary>
public bool DisableAdditionalFiltering { get; set; }
/// <summary>
/// Gets or sets a boolean indicating whether entity caching should be disabled.
/// Disabling entity caching may have a noticeable impact on the performance
/// of your application and result in multiple queries being sent by the stores.
/// </summary>
public bool DisableEntityCaching { get; set; }
/// <summary>
/// Gets or sets a boolean indicating whether entity caching should be disabled.
/// Disabling entity caching may have a noticeable impact on the performance
/// of your application and result in multiple queries being sent by the stores.
/// </summary>
public bool DisableEntityCaching { get; set; }
/// <summary>
/// Gets or sets the maximum number of cached entries allowed. When the threshold
/// is reached, the cache is automatically compacted to ensure it doesn't grow
/// abnormally and doesn't cause a memory starvation or out-of-memory exceptions.
/// This property is not used when <see cref="DisableEntityCaching"/> is <c>true</c>.
/// </summary>
public int EntityCacheLimit { get; set; } = 250;
}
}
/// <summary>
/// Gets or sets the maximum number of cached entries allowed. When the threshold
/// is reached, the cache is automatically compacted to ensure it doesn't grow
/// abnormally and doesn't cause a memory starvation or out-of-memory exceptions.
/// This property is not used when <see cref="DisableEntityCaching"/> is <c>true</c>.
/// </summary>
public int EntityCacheLimit { get; set; } = 250;
}

37
src/OpenIddict.Core/Resolvers/OpenIddictApplicationStoreResolver.cs

@ -3,26 +3,25 @@ using Microsoft.Extensions.DependencyInjection;
using OpenIddict.Abstractions;
using SR = OpenIddict.Abstractions.OpenIddictResources;
namespace OpenIddict.Core
namespace OpenIddict.Core;
/// <summary>
/// Exposes a method allowing to resolve an application store.
/// </summary>
public class OpenIddictApplicationStoreResolver : IOpenIddictApplicationStoreResolver
{
/// <summary>
/// Exposes a method allowing to resolve an application store.
/// </summary>
public class OpenIddictApplicationStoreResolver : IOpenIddictApplicationStoreResolver
{
private readonly IServiceProvider _provider;
private readonly IServiceProvider _provider;
public OpenIddictApplicationStoreResolver(IServiceProvider provider)
=> _provider = provider;
public OpenIddictApplicationStoreResolver(IServiceProvider provider)
=> _provider = provider;
/// <summary>
/// Returns an application store compatible with the specified application type or throws an
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
/// </summary>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <returns>An <see cref="IOpenIddictApplicationStore{TApplication}"/>.</returns>
public IOpenIddictApplicationStore<TApplication> Get<TApplication>() where TApplication : class
=> _provider.GetService<IOpenIddictApplicationStore<TApplication>>() ??
throw new InvalidOperationException(SR.GetResourceString(SR.ID0228));
}
/// <summary>
/// Returns an application store compatible with the specified application type or throws an
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
/// </summary>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <returns>An <see cref="IOpenIddictApplicationStore{TApplication}"/>.</returns>
public IOpenIddictApplicationStore<TApplication> Get<TApplication>() where TApplication : class
=> _provider.GetService<IOpenIddictApplicationStore<TApplication>>() ??
throw new InvalidOperationException(SR.GetResourceString(SR.ID0228));
}

37
src/OpenIddict.Core/Resolvers/OpenIddictAuthorizationStoreResolver.cs

@ -3,26 +3,25 @@ using Microsoft.Extensions.DependencyInjection;
using OpenIddict.Abstractions;
using SR = OpenIddict.Abstractions.OpenIddictResources;
namespace OpenIddict.Core
namespace OpenIddict.Core;
/// <summary>
/// Exposes a method allowing to resolve an authorization store.
/// </summary>
public class OpenIddictAuthorizationStoreResolver : IOpenIddictAuthorizationStoreResolver
{
/// <summary>
/// Exposes a method allowing to resolve an authorization store.
/// </summary>
public class OpenIddictAuthorizationStoreResolver : IOpenIddictAuthorizationStoreResolver
{
private readonly IServiceProvider _provider;
private readonly IServiceProvider _provider;
public OpenIddictAuthorizationStoreResolver(IServiceProvider provider)
=> _provider = provider;
public OpenIddictAuthorizationStoreResolver(IServiceProvider provider)
=> _provider = provider;
/// <summary>
/// Returns an authorization store compatible with the specified authorization type or throws an
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
/// </summary>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <returns>An <see cref="IOpenIddictAuthorizationStore{TAuthorization}"/>.</returns>
public IOpenIddictAuthorizationStore<TAuthorization> Get<TAuthorization>() where TAuthorization : class
=> _provider.GetService<IOpenIddictAuthorizationStore<TAuthorization>>() ??
throw new InvalidOperationException(SR.GetResourceString(SR.ID0229));
}
/// <summary>
/// Returns an authorization store compatible with the specified authorization type or throws an
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
/// </summary>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <returns>An <see cref="IOpenIddictAuthorizationStore{TAuthorization}"/>.</returns>
public IOpenIddictAuthorizationStore<TAuthorization> Get<TAuthorization>() where TAuthorization : class
=> _provider.GetService<IOpenIddictAuthorizationStore<TAuthorization>>() ??
throw new InvalidOperationException(SR.GetResourceString(SR.ID0229));
}

37
src/OpenIddict.Core/Resolvers/OpenIddictScopeStoreResolver.cs

@ -3,26 +3,25 @@ using Microsoft.Extensions.DependencyInjection;
using OpenIddict.Abstractions;
using SR = OpenIddict.Abstractions.OpenIddictResources;
namespace OpenIddict.Core
namespace OpenIddict.Core;
/// <summary>
/// Exposes a method allowing to resolve a scope store.
/// </summary>
public class OpenIddictScopeStoreResolver : IOpenIddictScopeStoreResolver
{
/// <summary>
/// Exposes a method allowing to resolve a scope store.
/// </summary>
public class OpenIddictScopeStoreResolver : IOpenIddictScopeStoreResolver
{
private readonly IServiceProvider _provider;
private readonly IServiceProvider _provider;
public OpenIddictScopeStoreResolver(IServiceProvider provider)
=> _provider = provider;
public OpenIddictScopeStoreResolver(IServiceProvider provider)
=> _provider = provider;
/// <summary>
/// Returns a scope store compatible with the specified scope type or throws an
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
/// </summary>
/// <typeparam name="TScope">The type of the Scope entity.</typeparam>
/// <returns>An <see cref="IOpenIddictScopeStore{TScope}"/>.</returns>
public IOpenIddictScopeStore<TScope> Get<TScope>() where TScope : class
=> _provider.GetService<IOpenIddictScopeStore<TScope>>() ??
throw new InvalidOperationException(SR.GetResourceString(SR.ID0230));
}
/// <summary>
/// Returns a scope store compatible with the specified scope type or throws an
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
/// </summary>
/// <typeparam name="TScope">The type of the Scope entity.</typeparam>
/// <returns>An <see cref="IOpenIddictScopeStore{TScope}"/>.</returns>
public IOpenIddictScopeStore<TScope> Get<TScope>() where TScope : class
=> _provider.GetService<IOpenIddictScopeStore<TScope>>() ??
throw new InvalidOperationException(SR.GetResourceString(SR.ID0230));
}

37
src/OpenIddict.Core/Resolvers/OpenIddictTokenStoreResolver.cs

@ -3,26 +3,25 @@ using Microsoft.Extensions.DependencyInjection;
using OpenIddict.Abstractions;
using SR = OpenIddict.Abstractions.OpenIddictResources;
namespace OpenIddict.Core
namespace OpenIddict.Core;
/// <summary>
/// Exposes a method allowing to resolve a token store.
/// </summary>
public class OpenIddictTokenStoreResolver : IOpenIddictTokenStoreResolver
{
/// <summary>
/// Exposes a method allowing to resolve a token store.
/// </summary>
public class OpenIddictTokenStoreResolver : IOpenIddictTokenStoreResolver
{
private readonly IServiceProvider _provider;
private readonly IServiceProvider _provider;
public OpenIddictTokenStoreResolver(IServiceProvider provider)
=> _provider = provider;
public OpenIddictTokenStoreResolver(IServiceProvider provider)
=> _provider = provider;
/// <summary>
/// Returns a token store compatible with the specified token type or throws an
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
/// </summary>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <returns>An <see cref="IOpenIddictTokenStore{TToken}"/>.</returns>
public IOpenIddictTokenStore<TToken> Get<TToken>() where TToken : class
=> _provider.GetService<IOpenIddictTokenStore<TToken>>() ??
throw new InvalidOperationException(SR.GetResourceString(SR.ID0231));
}
/// <summary>
/// Returns a token store compatible with the specified token type or throws an
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
/// </summary>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <returns>An <see cref="IOpenIddictTokenStore{TToken}"/>.</returns>
public IOpenIddictTokenStore<TToken> Get<TToken>() where TToken : class
=> _provider.GetService<IOpenIddictTokenStore<TToken>>() ??
throw new InvalidOperationException(SR.GetResourceString(SR.ID0231));
}

203
src/OpenIddict.EntityFramework.Models/OpenIddictEntityFrameworkApplication.cs

@ -8,111 +8,110 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace OpenIddict.EntityFramework.Models
namespace OpenIddict.EntityFramework.Models;
/// <summary>
/// Represents an OpenIddict application.
/// </summary>
public class OpenIddictEntityFrameworkApplication : OpenIddictEntityFrameworkApplication<string, OpenIddictEntityFrameworkAuthorization, OpenIddictEntityFrameworkToken>
{
/// <summary>
/// Represents an OpenIddict application.
/// </summary>
public class OpenIddictEntityFrameworkApplication : OpenIddictEntityFrameworkApplication<string, OpenIddictEntityFrameworkAuthorization, OpenIddictEntityFrameworkToken>
public OpenIddictEntityFrameworkApplication()
{
public OpenIddictEntityFrameworkApplication()
{
// Generate a new string identifier.
Id = Guid.NewGuid().ToString();
}
// Generate a new string identifier.
Id = Guid.NewGuid().ToString();
}
}
/// <summary>
/// Represents an OpenIddict application.
/// </summary>
[DebuggerDisplay("Id = {Id.ToString(),nq} ; ClientId = {ClientId,nq} ; Type = {Type,nq}")]
public class OpenIddictEntityFrameworkApplication<TKey, TAuthorization, TToken>
where TKey : notnull, IEquatable<TKey>
where TAuthorization : class
where TToken : class
{
/// <summary>
/// Gets the list of the authorizations associated with this application.
/// </summary>
public virtual ICollection<TAuthorization> Authorizations { get; } = new HashSet<TAuthorization>();
/// <summary>
/// Represents an OpenIddict application.
/// Gets or sets the client identifier associated with the current application.
/// </summary>
[DebuggerDisplay("Id = {Id.ToString(),nq} ; ClientId = {ClientId,nq} ; Type = {Type,nq}")]
public class OpenIddictEntityFrameworkApplication<TKey, TAuthorization, TToken>
where TKey : notnull, IEquatable<TKey>
where TAuthorization : class
where TToken : class
{
/// <summary>
/// Gets the list of the authorizations associated with this application.
/// </summary>
public virtual ICollection<TAuthorization> Authorizations { get; } = new HashSet<TAuthorization>();
/// <summary>
/// Gets or sets the client identifier associated with the current application.
/// </summary>
public virtual string? ClientId { get; set; }
/// <summary>
/// Gets or sets the client secret associated with the current application.
/// Note: depending on the application manager used to create this instance,
/// this property may be hashed or encrypted for security reasons.
/// </summary>
public virtual string? ClientSecret { get; set; }
/// <summary>
/// Gets or sets the concurrency token.
/// </summary>
public virtual string? ConcurrencyToken { get; set; } = Guid.NewGuid().ToString();
/// <summary>
/// Gets or sets the consent type associated with the current application.
/// </summary>
public virtual string? ConsentType { get; set; }
/// <summary>
/// Gets or sets the display name associated with the current application.
/// </summary>
public virtual string? DisplayName { get; set; }
/// <summary>
/// Gets or sets the localized display names
/// associated with the current application,
/// serialized as a JSON object.
/// </summary>
public virtual string? DisplayNames { 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 permissions associated with the
/// current application, serialized as a JSON array.
/// </summary>
public virtual string? Permissions { get; set; }
/// <summary>
/// Gets or sets the logout callback URLs associated with
/// the current application, serialized as a JSON array.
/// </summary>
public virtual string? PostLogoutRedirectUris { get; set; }
/// <summary>
/// Gets or sets the additional properties serialized as a JSON object,
/// or <c>null</c> if no bag was associated with the current application.
/// </summary>
public virtual string? Properties { get; set; }
/// <summary>
/// Gets or sets the callback URLs associated with the
/// current application, serialized as a JSON array.
/// </summary>
public virtual string? RedirectUris { get; set; }
/// <summary>
/// Gets or sets the requirements associated with the
/// current application, serialized as a JSON array.
/// </summary>
public virtual string? Requirements { get; set; }
/// <summary>
/// Gets the list of the tokens associated with this application.
/// </summary>
public virtual ICollection<TToken> Tokens { get; } = new HashSet<TToken>();
/// <summary>
/// Gets or sets the application type associated with the current application.
/// </summary>
public virtual string? Type { get; set; }
}
}
public virtual string? ClientId { get; set; }
/// <summary>
/// Gets or sets the client secret associated with the current application.
/// Note: depending on the application manager used to create this instance,
/// this property may be hashed or encrypted for security reasons.
/// </summary>
public virtual string? ClientSecret { get; set; }
/// <summary>
/// Gets or sets the concurrency token.
/// </summary>
public virtual string? ConcurrencyToken { get; set; } = Guid.NewGuid().ToString();
/// <summary>
/// Gets or sets the consent type associated with the current application.
/// </summary>
public virtual string? ConsentType { get; set; }
/// <summary>
/// Gets or sets the display name associated with the current application.
/// </summary>
public virtual string? DisplayName { get; set; }
/// <summary>
/// Gets or sets the localized display names
/// associated with the current application,
/// serialized as a JSON object.
/// </summary>
public virtual string? DisplayNames { 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 permissions associated with the
/// current application, serialized as a JSON array.
/// </summary>
public virtual string? Permissions { get; set; }
/// <summary>
/// Gets or sets the logout callback URLs associated with
/// the current application, serialized as a JSON array.
/// </summary>
public virtual string? PostLogoutRedirectUris { get; set; }
/// <summary>
/// Gets or sets the additional properties serialized as a JSON object,
/// or <c>null</c> if no bag was associated with the current application.
/// </summary>
public virtual string? Properties { get; set; }
/// <summary>
/// Gets or sets the callback URLs associated with the
/// current application, serialized as a JSON array.
/// </summary>
public virtual string? RedirectUris { get; set; }
/// <summary>
/// Gets or sets the requirements associated with the
/// current application, serialized as a JSON array.
/// </summary>
public virtual string? Requirements { get; set; }
/// <summary>
/// Gets the list of the tokens associated with this application.
/// </summary>
public virtual ICollection<TToken> Tokens { get; } = new HashSet<TToken>();
/// <summary>
/// Gets or sets the application type associated with the current application.
/// </summary>
public virtual string? Type { get; set; }
}

119
src/OpenIddict.EntityFramework.Models/OpenIddictEntityFrameworkAuthorization.cs

@ -8,79 +8,78 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace OpenIddict.EntityFramework.Models
namespace OpenIddict.EntityFramework.Models;
/// <summary>
/// Represents an OpenIddict authorization.
/// </summary>
public class OpenIddictEntityFrameworkAuthorization : OpenIddictEntityFrameworkAuthorization<string, OpenIddictEntityFrameworkApplication, OpenIddictEntityFrameworkToken>
{
/// <summary>
/// Represents an OpenIddict authorization.
/// </summary>
public class OpenIddictEntityFrameworkAuthorization : OpenIddictEntityFrameworkAuthorization<string, OpenIddictEntityFrameworkApplication, OpenIddictEntityFrameworkToken>
public OpenIddictEntityFrameworkAuthorization()
{
public OpenIddictEntityFrameworkAuthorization()
{
// Generate a new string identifier.
Id = Guid.NewGuid().ToString();
}
// Generate a new string identifier.
Id = Guid.NewGuid().ToString();
}
}
/// <summary>
/// Represents an OpenIddict authorization.
/// </summary>
[DebuggerDisplay("Id = {Id.ToString(),nq} ; Subject = {Subject,nq} ; Type = {Type,nq} ; Status = {Status,nq}")]
public class OpenIddictEntityFrameworkAuthorization<TKey, TApplication, TToken>
where TKey : notnull, IEquatable<TKey>
where TApplication : class
where TToken : class
{
/// <summary>
/// Represents an OpenIddict authorization.
/// Gets or sets the application associated with the current authorization.
/// </summary>
[DebuggerDisplay("Id = {Id.ToString(),nq} ; Subject = {Subject,nq} ; Type = {Type,nq} ; Status = {Status,nq}")]
public class OpenIddictEntityFrameworkAuthorization<TKey, TApplication, TToken>
where TKey : notnull, IEquatable<TKey>
where TApplication : class
where TToken : class
{
/// <summary>
/// Gets or sets the application associated with the current authorization.
/// </summary>
public virtual TApplication? Application { get; set; }
public virtual TApplication? Application { get; set; }
/// <summary>
/// Gets or sets the concurrency token.
/// </summary>
public virtual string? ConcurrencyToken { get; set; } = Guid.NewGuid().ToString();
/// <summary>
/// Gets or sets the concurrency token.
/// </summary>
public virtual string? ConcurrencyToken { get; set; } = Guid.NewGuid().ToString();
/// <summary>
/// Gets or sets the UTC creation date of the current authorization.
/// </summary>
public virtual DateTime? CreationDate { get; set; }
/// <summary>
/// Gets or sets the UTC creation date of the current authorization.
/// </summary>
public virtual DateTime? CreationDate { get; set; }
/// <summary>
/// Gets or sets the unique identifier associated with the current authorization.
/// </summary>
public virtual TKey? Id { get; set; }
/// <summary>
/// Gets or sets the unique identifier associated with the current authorization.
/// </summary>
public virtual TKey? Id { get; set; }
/// <summary>
/// Gets or sets the additional properties serialized as a JSON object,
/// or <c>null</c> if no bag was associated with the current authorization.
/// </summary>
public virtual string? Properties { get; set; }
/// <summary>
/// Gets or sets the additional properties serialized as a JSON object,
/// or <c>null</c> if no bag was associated with the current authorization.
/// </summary>
public virtual string? Properties { get; set; }
/// <summary>
/// Gets or sets the scopes associated with the current
/// authorization, serialized as a JSON array.
/// </summary>
public virtual string? Scopes { get; set; }
/// <summary>
/// Gets or sets the scopes associated with the current
/// authorization, serialized as a JSON array.
/// </summary>
public virtual string? Scopes { get; set; }
/// <summary>
/// Gets or sets the status of the current authorization.
/// </summary>
public virtual string? Status { get; set; }
/// <summary>
/// Gets or sets the status of the current authorization.
/// </summary>
public virtual string? Status { get; set; }
/// <summary>
/// Gets or sets the subject associated with the current authorization.
/// </summary>
public virtual string? Subject { get; set; }
/// <summary>
/// Gets or sets the subject associated with the current authorization.
/// </summary>
public virtual string? Subject { get; set; }
/// <summary>
/// Gets the list of tokens associated with the current authorization.
/// </summary>
public virtual ICollection<TToken> Tokens { get; } = new HashSet<TToken>();
/// <summary>
/// Gets the list of tokens associated with the current authorization.
/// </summary>
public virtual ICollection<TToken> Tokens { get; } = new HashSet<TToken>();
/// <summary>
/// Gets or sets the type of the current authorization.
/// </summary>
public virtual string? Type { get; set; }
}
/// <summary>
/// Gets or sets the type of the current authorization.
/// </summary>
public virtual string? Type { get; set; }
}

111
src/OpenIddict.EntityFramework.Models/OpenIddictEntityFrameworkScope.cs

@ -7,74 +7,73 @@
using System;
using System.Diagnostics;
namespace OpenIddict.EntityFramework.Models
namespace OpenIddict.EntityFramework.Models;
/// <summary>
/// Represents an OpenIddict scope.
/// </summary>
public class OpenIddictEntityFrameworkScope : OpenIddictEntityFrameworkScope<string>
{
/// <summary>
/// Represents an OpenIddict scope.
/// </summary>
public class OpenIddictEntityFrameworkScope : OpenIddictEntityFrameworkScope<string>
public OpenIddictEntityFrameworkScope()
{
public OpenIddictEntityFrameworkScope()
{
// Generate a new string identifier.
Id = Guid.NewGuid().ToString();
}
// Generate a new string identifier.
Id = Guid.NewGuid().ToString();
}
}
/// <summary>
/// Represents an OpenIddict scope.
/// </summary>
[DebuggerDisplay("Id = {Id.ToString(),nq} ; Name = {Name,nq}")]
public class OpenIddictEntityFrameworkScope<TKey> where TKey : notnull, IEquatable<TKey>
{
/// <summary>
/// Represents an OpenIddict scope.
/// Gets or sets the concurrency token.
/// </summary>
[DebuggerDisplay("Id = {Id.ToString(),nq} ; Name = {Name,nq}")]
public class OpenIddictEntityFrameworkScope<TKey> where TKey : notnull, IEquatable<TKey>
{
/// <summary>
/// Gets or sets the concurrency token.
/// </summary>
public virtual string? ConcurrencyToken { get; set; } = Guid.NewGuid().ToString();
public virtual string? ConcurrencyToken { get; set; } = Guid.NewGuid().ToString();
/// <summary>
/// Gets or sets the public description associated with the current scope.
/// </summary>
public virtual string? Description { get; set; }
/// <summary>
/// Gets or sets the public description associated with the current scope.
/// </summary>
public virtual string? Description { get; set; }
/// <summary>
/// Gets or sets the localized public descriptions associated
/// with the current scope, serialized as a JSON object.
/// </summary>
public virtual string? Descriptions { get; set; }
/// <summary>
/// Gets or sets the localized public descriptions associated
/// with the current scope, serialized as a JSON object.
/// </summary>
public virtual string? Descriptions { get; set; }
/// <summary>
/// Gets or sets the display name associated with the current scope.
/// </summary>
public virtual string? DisplayName { get; set; }
/// <summary>
/// Gets or sets the display name associated with the current scope.
/// </summary>
public virtual string? DisplayName { get; set; }
/// <summary>
/// Gets or sets the localized display names
/// associated with the current application,
/// serialized as a JSON object.
/// </summary>
public virtual string? DisplayNames { get; set; }
/// <summary>
/// Gets or sets the localized display names
/// associated with the current application,
/// serialized as a JSON object.
/// </summary>
public virtual string? DisplayNames { get; set; }
/// <summary>
/// Gets or sets the unique identifier associated with the current scope.
/// </summary>
public virtual TKey? Id { get; set; }
/// <summary>
/// Gets or sets the unique identifier associated with the current scope.
/// </summary>
public virtual TKey? Id { get; set; }
/// <summary>
/// Gets or sets the unique name associated with the current scope.
/// </summary>
public virtual string? Name { get; set; }
/// <summary>
/// Gets or sets the unique name associated with the current scope.
/// </summary>
public virtual string? Name { get; set; }
/// <summary>
/// Gets or sets the additional properties serialized as a JSON object,
/// or <c>null</c> if no bag was associated with the current scope.
/// </summary>
public virtual string? Properties { get; set; }
/// <summary>
/// Gets or sets the additional properties serialized as a JSON object,
/// or <c>null</c> if no bag was associated with the current scope.
/// </summary>
public virtual string? Properties { get; set; }
/// <summary>
/// Gets or sets the resources associated with the
/// current scope, serialized as a JSON array.
/// </summary>
public virtual string? Resources { get; set; }
}
/// <summary>
/// Gets or sets the resources associated with the
/// current scope, serialized as a JSON array.
/// </summary>
public virtual string? Resources { get; set; }
}

175
src/OpenIddict.EntityFramework.Models/OpenIddictEntityFrameworkToken.cs

@ -7,98 +7,97 @@
using System;
using System.Diagnostics;
namespace OpenIddict.EntityFramework.Models
namespace OpenIddict.EntityFramework.Models;
/// <summary>
/// Represents an OpenIddict token.
/// </summary>
public class OpenIddictEntityFrameworkToken : OpenIddictEntityFrameworkToken<string, OpenIddictEntityFrameworkApplication, OpenIddictEntityFrameworkAuthorization>
{
/// <summary>
/// Represents an OpenIddict token.
/// </summary>
public class OpenIddictEntityFrameworkToken : OpenIddictEntityFrameworkToken<string, OpenIddictEntityFrameworkApplication, OpenIddictEntityFrameworkAuthorization>
public OpenIddictEntityFrameworkToken()
{
public OpenIddictEntityFrameworkToken()
{
// Generate a new string identifier.
Id = Guid.NewGuid().ToString();
}
// Generate a new string identifier.
Id = Guid.NewGuid().ToString();
}
}
/// <summary>
/// Represents an OpenIddict token.
/// </summary>
[DebuggerDisplay("Id = {Id.ToString(),nq} ; Subject = {Subject,nq} ; Type = {Type,nq} ; Status = {Status,nq}")]
public class OpenIddictEntityFrameworkToken<TKey, TApplication, TAuthorization>
where TKey : notnull, IEquatable<TKey>
where TApplication : class
where TAuthorization : class
{
/// <summary>
/// Represents an OpenIddict token.
/// Gets or sets the application associated with the current token.
/// </summary>
[DebuggerDisplay("Id = {Id.ToString(),nq} ; Subject = {Subject,nq} ; Type = {Type,nq} ; Status = {Status,nq}")]
public class OpenIddictEntityFrameworkToken<TKey, TApplication, TAuthorization>
where TKey : notnull, IEquatable<TKey>
where TApplication : class
where TAuthorization : class
{
/// <summary>
/// Gets or sets the application associated with the current token.
/// </summary>
public virtual TApplication? Application { get; set; }
/// <summary>
/// Gets or sets the authorization associated with the current token.
/// </summary>
public virtual TAuthorization? Authorization { get; set; }
/// <summary>
/// Gets or sets the concurrency token.
/// </summary>
public virtual string? ConcurrencyToken { get; set; } = Guid.NewGuid().ToString();
/// <summary>
/// Gets or sets the UTC creation date of the current token.
/// </summary>
public virtual DateTime? CreationDate { get; set; }
/// <summary>
/// Gets or sets the UTC expiration date of the current token.
/// </summary>
public virtual DateTime? ExpirationDate { 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 payload of the current token, if applicable.
/// Note: this property is only used for reference tokens
/// and may be encrypted for security reasons.
/// </summary>
public virtual string? Payload { get; set; }
/// <summary>
/// Gets or sets the additional properties serialized as a JSON object,
/// or <c>null</c> if no bag was associated with the current token.
/// </summary>
public virtual string? Properties { get; set; }
/// <summary>
/// Gets or sets the UTC redemption date of the current token.
/// </summary>
public virtual DateTime? RedemptionDate { get; set; }
/// <summary>
/// Gets or sets the reference identifier associated
/// with the current token, if applicable.
/// Note: this property is only used for reference tokens
/// and may be hashed or encrypted for security reasons.
/// </summary>
public virtual string? ReferenceId { get; set; }
/// <summary>
/// Gets or sets the status of the current token.
/// </summary>
public virtual string? Status { get; set; }
/// <summary>
/// Gets or sets the subject associated with the current token.
/// </summary>
public virtual string? Subject { get; set; }
/// <summary>
/// Gets or sets the type of the current token.
/// </summary>
public virtual string? Type { get; set; }
}
public virtual TApplication? Application { get; set; }
/// <summary>
/// Gets or sets the authorization associated with the current token.
/// </summary>
public virtual TAuthorization? Authorization { get; set; }
/// <summary>
/// Gets or sets the concurrency token.
/// </summary>
public virtual string? ConcurrencyToken { get; set; } = Guid.NewGuid().ToString();
/// <summary>
/// Gets or sets the UTC creation date of the current token.
/// </summary>
public virtual DateTime? CreationDate { get; set; }
/// <summary>
/// Gets or sets the UTC expiration date of the current token.
/// </summary>
public virtual DateTime? ExpirationDate { 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 payload of the current token, if applicable.
/// Note: this property is only used for reference tokens
/// and may be encrypted for security reasons.
/// </summary>
public virtual string? Payload { get; set; }
/// <summary>
/// Gets or sets the additional properties serialized as a JSON object,
/// or <c>null</c> if no bag was associated with the current token.
/// </summary>
public virtual string? Properties { get; set; }
/// <summary>
/// Gets or sets the UTC redemption date of the current token.
/// </summary>
public virtual DateTime? RedemptionDate { get; set; }
/// <summary>
/// Gets or sets the reference identifier associated
/// with the current token, if applicable.
/// Note: this property is only used for reference tokens
/// and may be hashed or encrypted for security reasons.
/// </summary>
public virtual string? ReferenceId { get; set; }
/// <summary>
/// Gets or sets the status of the current token.
/// </summary>
public virtual string? Status { get; set; }
/// <summary>
/// Gets or sets the subject associated with the current token.
/// </summary>
public virtual string? Subject { get; set; }
/// <summary>
/// Gets or sets the type of the current token.
/// </summary>
public virtual string? Type { get; set; }
}

97
src/OpenIddict.EntityFramework/Configurations/OpenIddictEntityFrameworkApplicationConfiguration.cs

@ -11,64 +11,63 @@ using System.Data.Entity.Infrastructure.Annotations;
using System.Data.Entity.ModelConfiguration;
using OpenIddict.EntityFramework.Models;
namespace OpenIddict.EntityFramework
namespace OpenIddict.EntityFramework;
/// <summary>
/// Defines a relational mapping for the Application entity.
/// </summary>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <typeparam name="TKey">The type of the Key entity.</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
public class OpenIddictEntityFrameworkApplicationConfiguration<TApplication, TAuthorization, TToken, TKey> : EntityTypeConfiguration<TApplication>
where TApplication : OpenIddictEntityFrameworkApplication<TKey, TAuthorization, TToken>
where TAuthorization : OpenIddictEntityFrameworkAuthorization<TKey, TApplication, TToken>
where TToken : OpenIddictEntityFrameworkToken<TKey, TApplication, TAuthorization>
where TKey : notnull, IEquatable<TKey>
{
/// <summary>
/// Defines a relational mapping for the Application entity.
/// </summary>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <typeparam name="TKey">The type of the Key entity.</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
public class OpenIddictEntityFrameworkApplicationConfiguration<TApplication, TAuthorization, TToken, TKey> : EntityTypeConfiguration<TApplication>
where TApplication : OpenIddictEntityFrameworkApplication<TKey, TAuthorization, TToken>
where TAuthorization : OpenIddictEntityFrameworkAuthorization<TKey, TApplication, TToken>
where TToken : OpenIddictEntityFrameworkToken<TKey, TApplication, TAuthorization>
where TKey : notnull, IEquatable<TKey>
public OpenIddictEntityFrameworkApplicationConfiguration()
{
public OpenIddictEntityFrameworkApplicationConfiguration()
{
// Warning: optional foreign keys MUST NOT be added as CLR properties because
// Entity Framework would throw an exception due to the TKey generic parameter
// being non-nullable when using value types like short, int, long or Guid.
// Warning: optional foreign keys MUST NOT be added as CLR properties because
// Entity Framework would throw an exception due to the TKey generic parameter
// being non-nullable when using value types like short, int, long or Guid.
HasKey(application => application.Id);
HasKey(application => application.Id);
Property(application => application.ClientId)
.HasMaxLength(100)
.HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute
{
IsUnique = true
}));
Property(application => application.ClientId)
.HasMaxLength(100)
.HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute
{
IsUnique = true
}));
Property(application => application.ConcurrencyToken)
.HasMaxLength(50)
.IsConcurrencyToken();
Property(application => application.ConcurrencyToken)
.HasMaxLength(50)
.IsConcurrencyToken();
Property(application => application.ConsentType)
.HasMaxLength(50);
Property(application => application.ConsentType)
.HasMaxLength(50);
Property(application => application.Type)
.HasMaxLength(50);
Property(application => application.Type)
.HasMaxLength(50);
HasMany(application => application.Authorizations)
.WithOptional(authorization => authorization.Application!)
.Map(association =>
{
association.MapKey(nameof(OpenIddictEntityFrameworkAuthorization.Application) +
nameof(OpenIddictEntityFrameworkApplication.Id));
});
HasMany(application => application.Authorizations)
.WithOptional(authorization => authorization.Application!)
.Map(association =>
{
association.MapKey(nameof(OpenIddictEntityFrameworkAuthorization.Application) +
nameof(OpenIddictEntityFrameworkApplication.Id));
});
HasMany(application => application.Tokens)
.WithOptional(token => token.Application!)
.Map(association =>
{
association.MapKey(nameof(OpenIddictEntityFrameworkToken.Application) +
nameof(OpenIddictEntityFrameworkApplication.Id));
});
HasMany(application => application.Tokens)
.WithOptional(token => token.Application!)
.Map(association =>
{
association.MapKey(nameof(OpenIddictEntityFrameworkToken.Application) +
nameof(OpenIddictEntityFrameworkApplication.Id));
});
ToTable("OpenIddictApplications");
}
ToTable("OpenIddictApplications");
}
}

71
src/OpenIddict.EntityFramework/Configurations/OpenIddictEntityFrameworkAuthorizationConfiguration.cs

@ -9,50 +9,49 @@ using System.ComponentModel;
using System.Data.Entity.ModelConfiguration;
using OpenIddict.EntityFramework.Models;
namespace OpenIddict.EntityFramework
namespace OpenIddict.EntityFramework;
/// <summary>
/// Defines a relational mapping for the Authorization entity.
/// </summary>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <typeparam name="TKey">The type of the Key entity.</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
public class OpenIddictEntityFrameworkAuthorizationConfiguration<TAuthorization, TApplication, TToken, TKey> : EntityTypeConfiguration<TAuthorization>
where TAuthorization : OpenIddictEntityFrameworkAuthorization<TKey, TApplication, TToken>
where TApplication : OpenIddictEntityFrameworkApplication<TKey, TAuthorization, TToken>
where TToken : OpenIddictEntityFrameworkToken<TKey, TApplication, TAuthorization>
where TKey : notnull, IEquatable<TKey>
{
/// <summary>
/// Defines a relational mapping for the Authorization entity.
/// </summary>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <typeparam name="TKey">The type of the Key entity.</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
public class OpenIddictEntityFrameworkAuthorizationConfiguration<TAuthorization, TApplication, TToken, TKey> : EntityTypeConfiguration<TAuthorization>
where TAuthorization : OpenIddictEntityFrameworkAuthorization<TKey, TApplication, TToken>
where TApplication : OpenIddictEntityFrameworkApplication<TKey, TAuthorization, TToken>
where TToken : OpenIddictEntityFrameworkToken<TKey, TApplication, TAuthorization>
where TKey : notnull, IEquatable<TKey>
public OpenIddictEntityFrameworkAuthorizationConfiguration()
{
public OpenIddictEntityFrameworkAuthorizationConfiguration()
{
// Warning: optional foreign keys MUST NOT be added as CLR properties because
// Entity Framework would throw an exception due to the TKey generic parameter
// being non-nullable when using value types like short, int, long or Guid.
// Warning: optional foreign keys MUST NOT be added as CLR properties because
// Entity Framework would throw an exception due to the TKey generic parameter
// being non-nullable when using value types like short, int, long or Guid.
HasKey(authorization => authorization.Id);
HasKey(authorization => authorization.Id);
Property(authorization => authorization.ConcurrencyToken)
.HasMaxLength(50)
.IsConcurrencyToken();
Property(authorization => authorization.ConcurrencyToken)
.HasMaxLength(50)
.IsConcurrencyToken();
Property(authorization => authorization.Status)
.HasMaxLength(50);
Property(authorization => authorization.Status)
.HasMaxLength(50);
Property(authorization => authorization.Subject)
.HasMaxLength(400);
Property(authorization => authorization.Subject)
.HasMaxLength(400);
Property(authorization => authorization.Type)
.HasMaxLength(50);
Property(authorization => authorization.Type)
.HasMaxLength(50);
HasMany(authorization => authorization.Tokens)
.WithOptional(token => token.Authorization!)
.Map(association => association.MapKey(nameof(OpenIddictEntityFrameworkToken.Authorization) +
nameof(OpenIddictEntityFrameworkAuthorization.Id)))
.WillCascadeOnDelete();
HasMany(authorization => authorization.Tokens)
.WithOptional(token => token.Authorization!)
.Map(association => association.MapKey(nameof(OpenIddictEntityFrameworkToken.Authorization) +
nameof(OpenIddictEntityFrameworkAuthorization.Id)))
.WillCascadeOnDelete();
ToTable("OpenIddictAuthorizations");
}
ToTable("OpenIddictAuthorizations");
}
}

53
src/OpenIddict.EntityFramework/Configurations/OpenIddictEntityFrameworkScopeConfiguration.cs

@ -11,38 +11,37 @@ using System.Data.Entity.Infrastructure.Annotations;
using System.Data.Entity.ModelConfiguration;
using OpenIddict.EntityFramework.Models;
namespace OpenIddict.EntityFramework
namespace OpenIddict.EntityFramework;
/// <summary>
/// Defines a relational mapping for the Scope entity.
/// </summary>
/// <typeparam name="TScope">The type of the Scope entity.</typeparam>
/// <typeparam name="TKey">The type of the Key entity.</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
public class OpenIddictEntityFrameworkScopeConfiguration<TScope, TKey> : EntityTypeConfiguration<TScope>
where TScope : OpenIddictEntityFrameworkScope<TKey>
where TKey : notnull, IEquatable<TKey>
{
/// <summary>
/// Defines a relational mapping for the Scope entity.
/// </summary>
/// <typeparam name="TScope">The type of the Scope entity.</typeparam>
/// <typeparam name="TKey">The type of the Key entity.</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
public class OpenIddictEntityFrameworkScopeConfiguration<TScope, TKey> : EntityTypeConfiguration<TScope>
where TScope : OpenIddictEntityFrameworkScope<TKey>
where TKey : notnull, IEquatable<TKey>
public OpenIddictEntityFrameworkScopeConfiguration()
{
public OpenIddictEntityFrameworkScopeConfiguration()
{
// Warning: optional foreign keys MUST NOT be added as CLR properties because
// Entity Framework would throw an exception due to the TKey generic parameter
// being non-nullable when using value types like short, int, long or Guid.
// Warning: optional foreign keys MUST NOT be added as CLR properties because
// Entity Framework would throw an exception due to the TKey generic parameter
// being non-nullable when using value types like short, int, long or Guid.
HasKey(scope => scope.Id);
HasKey(scope => scope.Id);
Property(scope => scope.ConcurrencyToken)
.HasMaxLength(50)
.IsConcurrencyToken();
Property(scope => scope.ConcurrencyToken)
.HasMaxLength(50)
.IsConcurrencyToken();
Property(scope => scope.Name)
.HasMaxLength(200)
.HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute
{
IsUnique = true
}));
Property(scope => scope.Name)
.HasMaxLength(200)
.HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute
{
IsUnique = true
}));
ToTable("OpenIddictScopes");
}
ToTable("OpenIddictScopes");
}
}

73
src/OpenIddict.EntityFramework/Configurations/OpenIddictEntityFrameworkTokenConfiguration.cs

@ -11,51 +11,50 @@ using System.Data.Entity.Infrastructure.Annotations;
using System.Data.Entity.ModelConfiguration;
using OpenIddict.EntityFramework.Models;
namespace OpenIddict.EntityFramework
namespace OpenIddict.EntityFramework;
/// <summary>
/// Defines a relational mapping for the Token entity.
/// </summary>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <typeparam name="TKey">The type of the Key entity.</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
public class OpenIddictEntityFrameworkTokenConfiguration<TToken, TApplication, TAuthorization, TKey> : EntityTypeConfiguration<TToken>
where TToken : OpenIddictEntityFrameworkToken<TKey, TApplication, TAuthorization>
where TApplication : OpenIddictEntityFrameworkApplication<TKey, TAuthorization, TToken>
where TAuthorization : OpenIddictEntityFrameworkAuthorization<TKey, TApplication, TToken>
where TKey : notnull, IEquatable<TKey>
{
/// <summary>
/// Defines a relational mapping for the Token entity.
/// </summary>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <typeparam name="TKey">The type of the Key entity.</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
public class OpenIddictEntityFrameworkTokenConfiguration<TToken, TApplication, TAuthorization, TKey> : EntityTypeConfiguration<TToken>
where TToken : OpenIddictEntityFrameworkToken<TKey, TApplication, TAuthorization>
where TApplication : OpenIddictEntityFrameworkApplication<TKey, TAuthorization, TToken>
where TAuthorization : OpenIddictEntityFrameworkAuthorization<TKey, TApplication, TToken>
where TKey : notnull, IEquatable<TKey>
public OpenIddictEntityFrameworkTokenConfiguration()
{
public OpenIddictEntityFrameworkTokenConfiguration()
{
// Warning: optional foreign keys MUST NOT be added as CLR properties because
// Entity Framework would throw an exception due to the TKey generic parameter
// being non-nullable when using value types like short, int, long or Guid.
// Warning: optional foreign keys MUST NOT be added as CLR properties because
// Entity Framework would throw an exception due to the TKey generic parameter
// being non-nullable when using value types like short, int, long or Guid.
HasKey(token => token.Id);
HasKey(token => token.Id);
Property(token => token.ConcurrencyToken)
.HasMaxLength(50)
.IsConcurrencyToken();
Property(token => token.ConcurrencyToken)
.HasMaxLength(50)
.IsConcurrencyToken();
// Warning: the index on the ReferenceId property MUST NOT be declared as
// a unique index, as Entity Framework 6.x doesn't support creating indexes
// with null-friendly WHERE conditions, unlike Entity Framework Core 1.x/2.x.
Property(token => token.ReferenceId)
.HasMaxLength(100)
.HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute()));
// Warning: the index on the ReferenceId property MUST NOT be declared as
// a unique index, as Entity Framework 6.x doesn't support creating indexes
// with null-friendly WHERE conditions, unlike Entity Framework Core 1.x/2.x.
Property(token => token.ReferenceId)
.HasMaxLength(100)
.HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute()));
Property(token => token.Status)
.HasMaxLength(50);
Property(token => token.Status)
.HasMaxLength(50);
Property(token => token.Subject)
.HasMaxLength(400);
Property(token => token.Subject)
.HasMaxLength(400);
Property(token => token.Type)
.HasMaxLength(50);
Property(token => token.Type)
.HasMaxLength(50);
ToTable("OpenIddictTokens");
}
ToTable("OpenIddictTokens");
}
}

185
src/OpenIddict.EntityFramework/OpenIddictEntityFrameworkBuilder.cs

@ -13,117 +13,116 @@ using OpenIddict.EntityFramework;
using OpenIddict.EntityFramework.Models;
using SR = OpenIddict.Abstractions.OpenIddictResources;
namespace Microsoft.Extensions.DependencyInjection
namespace Microsoft.Extensions.DependencyInjection;
/// <summary>
/// Exposes the necessary methods required to configure the OpenIddict Entity Framework 6.x services.
/// </summary>
public class OpenIddictEntityFrameworkBuilder
{
/// <summary>
/// Exposes the necessary methods required to configure the OpenIddict Entity Framework 6.x services.
/// Initializes a new instance of <see cref="OpenIddictEntityFrameworkBuilder"/>.
/// </summary>
/// <param name="services">The services collection.</param>
public OpenIddictEntityFrameworkBuilder(IServiceCollection services)
=> Services = services ?? throw new ArgumentNullException(nameof(services));
/// <summary>
/// Gets the services collection.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public IServiceCollection Services { get; }
/// <summary>
/// Amends the default OpenIddict Entity Framework 6.x configuration.
/// </summary>
public class OpenIddictEntityFrameworkBuilder
/// <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="OpenIddictEntityFrameworkBuilder"/>.</returns>
public OpenIddictEntityFrameworkBuilder Configure(Action<OpenIddictEntityFrameworkOptions> configuration)
{
/// <summary>
/// Initializes a new instance of <see cref="OpenIddictEntityFrameworkBuilder"/>.
/// </summary>
/// <param name="services">The services collection.</param>
public OpenIddictEntityFrameworkBuilder(IServiceCollection services)
=> Services = services ?? throw new ArgumentNullException(nameof(services));
/// <summary>
/// Gets the services collection.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public IServiceCollection Services { get; }
/// <summary>
/// Amends the default OpenIddict Entity Framework 6.x 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="OpenIddictEntityFrameworkBuilder"/>.</returns>
public OpenIddictEntityFrameworkBuilder Configure(Action<OpenIddictEntityFrameworkOptions> configuration)
if (configuration is null)
{
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
throw new ArgumentNullException(nameof(configuration));
}
Services.Configure(configuration);
Services.Configure(configuration);
return this;
}
return this;
}
/// <summary>
/// Configures OpenIddict to use the specified entities, derived
/// from the default OpenIddict Entity Framework 6.x entities.
/// </summary>
/// <returns>The <see cref="OpenIddictEntityFrameworkBuilder"/>.</returns>
public OpenIddictEntityFrameworkBuilder ReplaceDefaultEntities<TApplication, TAuthorization, TScope, TToken, TKey>()
where TApplication : OpenIddictEntityFrameworkApplication<TKey, TAuthorization, TToken>
where TAuthorization : OpenIddictEntityFrameworkAuthorization<TKey, TApplication, TToken>
where TScope : OpenIddictEntityFrameworkScope<TKey>
where TToken : OpenIddictEntityFrameworkToken<TKey, TApplication, TAuthorization>
where TKey : notnull, IEquatable<TKey>
/// <summary>
/// Configures OpenIddict to use the specified entities, derived
/// from the default OpenIddict Entity Framework 6.x entities.
/// </summary>
/// <returns>The <see cref="OpenIddictEntityFrameworkBuilder"/>.</returns>
public OpenIddictEntityFrameworkBuilder ReplaceDefaultEntities<TApplication, TAuthorization, TScope, TToken, TKey>()
where TApplication : OpenIddictEntityFrameworkApplication<TKey, TAuthorization, TToken>
where TAuthorization : OpenIddictEntityFrameworkAuthorization<TKey, TApplication, TToken>
where TScope : OpenIddictEntityFrameworkScope<TKey>
where TToken : OpenIddictEntityFrameworkToken<TKey, TApplication, TAuthorization>
where TKey : notnull, IEquatable<TKey>
{
// Note: unlike Entity Framework Core 1.x/2.x/3.x, Entity Framework 6.x
// always throws an exception when using generic types as entity types.
// To ensure a better exception is thrown, a manual check is made here.
if (typeof(TApplication).IsGenericType || typeof(TAuthorization).IsGenericType ||
typeof(TScope).IsGenericType || typeof(TToken).IsGenericType)
{
// Note: unlike Entity Framework Core 1.x/2.x/3.x, Entity Framework 6.x
// always throws an exception when using generic types as entity types.
// To ensure a better exception is thrown, a manual check is made here.
if (typeof(TApplication).IsGenericType || typeof(TAuthorization).IsGenericType ||
typeof(TScope).IsGenericType || typeof(TToken).IsGenericType)
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0277));
}
Services.Configure<OpenIddictCoreOptions>(options =>
{
options.DefaultApplicationType = typeof(TApplication);
options.DefaultAuthorizationType = typeof(TAuthorization);
options.DefaultScopeType = typeof(TScope);
options.DefaultTokenType = typeof(TToken);
});
return this;
throw new InvalidOperationException(SR.GetResourceString(SR.ID0277));
}
/// <summary>
/// Configures the OpenIddict Entity Framework 6.x stores to use the specified database context type.
/// </summary>
/// <typeparam name="TContext">The type of the <see cref="DbContext"/> used by OpenIddict.</typeparam>
/// <returns>The <see cref="OpenIddictEntityFrameworkBuilder"/>.</returns>
public OpenIddictEntityFrameworkBuilder UseDbContext<TContext>()
where TContext : DbContext
=> UseDbContext(typeof(TContext));
/// <summary>
/// Configures the OpenIddict Entity Framework 6.x stores to use the specified database context type.
/// </summary>
/// <param name="type">The type of the <see cref="DbContext"/> used by OpenIddict.</param>
/// <returns>The <see cref="OpenIddictEntityFrameworkBuilder"/>.</returns>
public OpenIddictEntityFrameworkBuilder UseDbContext(Type type)
Services.Configure<OpenIddictCoreOptions>(options =>
{
if (type is null)
{
throw new ArgumentNullException(nameof(type));
}
options.DefaultApplicationType = typeof(TApplication);
options.DefaultAuthorizationType = typeof(TAuthorization);
options.DefaultScopeType = typeof(TScope);
options.DefaultTokenType = typeof(TToken);
});
if (!typeof(DbContext).IsAssignableFrom(type))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0232), nameof(type));
}
return this;
}
Services.TryAddScoped(type);
/// <summary>
/// Configures the OpenIddict Entity Framework 6.x stores to use the specified database context type.
/// </summary>
/// <typeparam name="TContext">The type of the <see cref="DbContext"/> used by OpenIddict.</typeparam>
/// <returns>The <see cref="OpenIddictEntityFrameworkBuilder"/>.</returns>
public OpenIddictEntityFrameworkBuilder UseDbContext<TContext>()
where TContext : DbContext
=> UseDbContext(typeof(TContext));
return Configure(options => options.DbContextType = type);
/// <summary>
/// Configures the OpenIddict Entity Framework 6.x stores to use the specified database context type.
/// </summary>
/// <param name="type">The type of the <see cref="DbContext"/> used by OpenIddict.</param>
/// <returns>The <see cref="OpenIddictEntityFrameworkBuilder"/>.</returns>
public OpenIddictEntityFrameworkBuilder UseDbContext(Type type)
{
if (type is null)
{
throw new ArgumentNullException(nameof(type));
}
/// <inheritdoc/>
[EditorBrowsable(EditorBrowsableState.Never)]
public override bool Equals(object? obj) => base.Equals(obj);
if (!typeof(DbContext).IsAssignableFrom(type))
{
throw new ArgumentException(SR.GetResourceString(SR.ID0232), nameof(type));
}
/// <inheritdoc/>
[EditorBrowsable(EditorBrowsableState.Never)]
public override int GetHashCode() => base.GetHashCode();
Services.TryAddScoped(type);
/// <inheritdoc/>
[EditorBrowsable(EditorBrowsableState.Never)]
public override string? ToString() => base.ToString();
return Configure(options => options.DbContextType = type);
}
/// <inheritdoc/>
[EditorBrowsable(EditorBrowsableState.Never)]
public override bool Equals(object? obj) => base.Equals(obj);
/// <inheritdoc/>
[EditorBrowsable(EditorBrowsableState.Never)]
public override int GetHashCode() => base.GetHashCode();
/// <inheritdoc/>
[EditorBrowsable(EditorBrowsableState.Never)]
public override string? ToString() => base.ToString();
}

117
src/OpenIddict.EntityFramework/OpenIddictEntityFrameworkExtensions.cs

@ -9,79 +9,78 @@ using Microsoft.Extensions.DependencyInjection.Extensions;
using OpenIddict.EntityFramework;
using OpenIddict.EntityFramework.Models;
namespace Microsoft.Extensions.DependencyInjection
namespace Microsoft.Extensions.DependencyInjection;
/// <summary>
/// Exposes extensions allowing to register the OpenIddict Entity Framework 6.x services.
/// </summary>
public static class OpenIddictEntityFrameworkExtensions
{
/// <summary>
/// Exposes extensions allowing to register the OpenIddict Entity Framework 6.x services.
/// Registers the Entity Framework 6.x stores services in the DI container and
/// configures OpenIddict to use the Entity Framework 6.x entities by default.
/// </summary>
public static class OpenIddictEntityFrameworkExtensions
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
/// <returns>The <see cref="OpenIddictEntityFrameworkBuilder"/>.</returns>
public static OpenIddictEntityFrameworkBuilder UseEntityFramework(this OpenIddictCoreBuilder builder)
{
/// <summary>
/// Registers the Entity Framework 6.x stores services in the DI container and
/// configures OpenIddict to use the Entity Framework 6.x entities by default.
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
/// <returns>The <see cref="OpenIddictEntityFrameworkBuilder"/>.</returns>
public static OpenIddictEntityFrameworkBuilder UseEntityFramework(this OpenIddictCoreBuilder builder)
if (builder is null)
{
if (builder is null)
{
throw new ArgumentNullException(nameof(builder));
}
throw new ArgumentNullException(nameof(builder));
}
// Since Entity Framework 6.x may be used with databases performing case-insensitive
// or culture-sensitive comparisons, ensure the additional filtering logic is enforced
// in case case-sensitive stores were registered before this extension was called.
builder.Configure(options => options.DisableAdditionalFiltering = false);
// Since Entity Framework 6.x may be used with databases performing case-insensitive
// or culture-sensitive comparisons, ensure the additional filtering logic is enforced
// in case case-sensitive stores were registered before this extension was called.
builder.Configure(options => options.DisableAdditionalFiltering = false);
builder.SetDefaultApplicationEntity<OpenIddictEntityFrameworkApplication>()
.SetDefaultAuthorizationEntity<OpenIddictEntityFrameworkAuthorization>()
.SetDefaultScopeEntity<OpenIddictEntityFrameworkScope>()
.SetDefaultTokenEntity<OpenIddictEntityFrameworkToken>();
builder.SetDefaultApplicationEntity<OpenIddictEntityFrameworkApplication>()
.SetDefaultAuthorizationEntity<OpenIddictEntityFrameworkAuthorization>()
.SetDefaultScopeEntity<OpenIddictEntityFrameworkScope>()
.SetDefaultTokenEntity<OpenIddictEntityFrameworkToken>();
builder.ReplaceApplicationStoreResolver<OpenIddictEntityFrameworkApplicationStoreResolver>()
.ReplaceAuthorizationStoreResolver<OpenIddictEntityFrameworkAuthorizationStoreResolver>()
.ReplaceScopeStoreResolver<OpenIddictEntityFrameworkScopeStoreResolver>()
.ReplaceTokenStoreResolver<OpenIddictEntityFrameworkTokenStoreResolver>();
builder.ReplaceApplicationStoreResolver<OpenIddictEntityFrameworkApplicationStoreResolver>()
.ReplaceAuthorizationStoreResolver<OpenIddictEntityFrameworkAuthorizationStoreResolver>()
.ReplaceScopeStoreResolver<OpenIddictEntityFrameworkScopeStoreResolver>()
.ReplaceTokenStoreResolver<OpenIddictEntityFrameworkTokenStoreResolver>();
builder.Services.TryAddSingleton<OpenIddictEntityFrameworkApplicationStoreResolver.TypeResolutionCache>();
builder.Services.TryAddSingleton<OpenIddictEntityFrameworkAuthorizationStoreResolver.TypeResolutionCache>();
builder.Services.TryAddSingleton<OpenIddictEntityFrameworkScopeStoreResolver.TypeResolutionCache>();
builder.Services.TryAddSingleton<OpenIddictEntityFrameworkTokenStoreResolver.TypeResolutionCache>();
builder.Services.TryAddSingleton<OpenIddictEntityFrameworkApplicationStoreResolver.TypeResolutionCache>();
builder.Services.TryAddSingleton<OpenIddictEntityFrameworkAuthorizationStoreResolver.TypeResolutionCache>();
builder.Services.TryAddSingleton<OpenIddictEntityFrameworkScopeStoreResolver.TypeResolutionCache>();
builder.Services.TryAddSingleton<OpenIddictEntityFrameworkTokenStoreResolver.TypeResolutionCache>();
builder.Services.TryAddScoped(typeof(OpenIddictEntityFrameworkApplicationStore<,,,,>));
builder.Services.TryAddScoped(typeof(OpenIddictEntityFrameworkAuthorizationStore<,,,,>));
builder.Services.TryAddScoped(typeof(OpenIddictEntityFrameworkScopeStore<,,>));
builder.Services.TryAddScoped(typeof(OpenIddictEntityFrameworkTokenStore<,,,,>));
builder.Services.TryAddScoped(typeof(OpenIddictEntityFrameworkApplicationStore<,,,,>));
builder.Services.TryAddScoped(typeof(OpenIddictEntityFrameworkAuthorizationStore<,,,,>));
builder.Services.TryAddScoped(typeof(OpenIddictEntityFrameworkScopeStore<,,>));
builder.Services.TryAddScoped(typeof(OpenIddictEntityFrameworkTokenStore<,,,,>));
return new OpenIddictEntityFrameworkBuilder(builder.Services);
}
return new OpenIddictEntityFrameworkBuilder(builder.Services);
}
/// <summary>
/// Registers the Entity Framework 6.x stores services in the DI container and
/// configures OpenIddict to use the Entity Framework 6.x entities by default.
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <param name="configuration">The configuration delegate used to configure the Entity Framework 6.x services.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
/// <returns>The <see cref="OpenIddictCoreBuilder"/>.</returns>
public static OpenIddictCoreBuilder UseEntityFramework(
this OpenIddictCoreBuilder builder, Action<OpenIddictEntityFrameworkBuilder> configuration)
/// <summary>
/// Registers the Entity Framework 6.x stores services in the DI container and
/// configures OpenIddict to use the Entity Framework 6.x entities by default.
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <param name="configuration">The configuration delegate used to configure the Entity Framework 6.x services.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
/// <returns>The <see cref="OpenIddictCoreBuilder"/>.</returns>
public static OpenIddictCoreBuilder UseEntityFramework(
this OpenIddictCoreBuilder builder, Action<OpenIddictEntityFrameworkBuilder> configuration)
{
if (builder is null)
{
if (builder is null)
{
throw new ArgumentNullException(nameof(builder));
}
throw new ArgumentNullException(nameof(builder));
}
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
configuration(builder.UseEntityFramework());
configuration(builder.UseEntityFramework());
return builder;
}
return builder;
}
}
}

121
src/OpenIddict.EntityFramework/OpenIddictEntityFrameworkHelpers.cs

@ -12,79 +12,78 @@ using Microsoft.Extensions.DependencyInjection;
using OpenIddict.EntityFramework;
using OpenIddict.EntityFramework.Models;
namespace System.Data.Entity
namespace System.Data.Entity;
/// <summary>
/// Exposes extensions simplifying the integration between OpenIddict and Entity Framework 6.x.
/// </summary>
public static class OpenIddictEntityFrameworkHelpers
{
/// <summary>
/// Exposes extensions simplifying the integration between OpenIddict and Entity Framework 6.x.
/// Registers the OpenIddict entity sets in the Entity Framework 6.x context
/// using the default OpenIddict models and the default key type (string).
/// </summary>
public static class OpenIddictEntityFrameworkHelpers
{
/// <summary>
/// Registers the OpenIddict entity sets in the Entity Framework 6.x context
/// using the default OpenIddict models and the default key type (string).
/// </summary>
/// <param name="builder">The builder used to configure the Entity Framework context.</param>
/// <returns>The Entity Framework context builder.</returns>
public static DbModelBuilder UseOpenIddict(this DbModelBuilder builder)
=> builder.UseOpenIddict<OpenIddictEntityFrameworkApplication,
OpenIddictEntityFrameworkAuthorization,
OpenIddictEntityFrameworkScope,
OpenIddictEntityFrameworkToken, string>();
/// <param name="builder">The builder used to configure the Entity Framework context.</param>
/// <returns>The Entity Framework context builder.</returns>
public static DbModelBuilder UseOpenIddict(this DbModelBuilder builder)
=> builder.UseOpenIddict<OpenIddictEntityFrameworkApplication,
OpenIddictEntityFrameworkAuthorization,
OpenIddictEntityFrameworkScope,
OpenIddictEntityFrameworkToken, string>();
/// <summary>
/// Registers the OpenIddict entity sets in the Entity Framework 6.x
/// context using the specified entities and the specified key type.
/// </summary>
/// <remarks>
/// Note: when using custom entities, the new entities MUST be registered by calling
/// <see cref="OpenIddictEntityFrameworkBuilder.ReplaceDefaultEntities{TApplication, TAuthorization, TScope, TToken, TKey}"/>.
/// </remarks>
/// <param name="builder">The builder used to configure the Entity Framework context.</param>
/// <returns>The Entity Framework context builder.</returns>
public static DbModelBuilder UseOpenIddict<TApplication, TAuthorization, TScope, TToken, TKey>(this DbModelBuilder builder)
where TApplication : OpenIddictEntityFrameworkApplication<TKey, TAuthorization, TToken>
where TAuthorization : OpenIddictEntityFrameworkAuthorization<TKey, TApplication, TToken>
where TScope : OpenIddictEntityFrameworkScope<TKey>
where TToken : OpenIddictEntityFrameworkToken<TKey, TApplication, TAuthorization>
where TKey : notnull, IEquatable<TKey>
/// <summary>
/// Registers the OpenIddict entity sets in the Entity Framework 6.x
/// context using the specified entities and the specified key type.
/// </summary>
/// <remarks>
/// Note: when using custom entities, the new entities MUST be registered by calling
/// <see cref="OpenIddictEntityFrameworkBuilder.ReplaceDefaultEntities{TApplication, TAuthorization, TScope, TToken, TKey}"/>.
/// </remarks>
/// <param name="builder">The builder used to configure the Entity Framework context.</param>
/// <returns>The Entity Framework context builder.</returns>
public static DbModelBuilder UseOpenIddict<TApplication, TAuthorization, TScope, TToken, TKey>(this DbModelBuilder builder)
where TApplication : OpenIddictEntityFrameworkApplication<TKey, TAuthorization, TToken>
where TAuthorization : OpenIddictEntityFrameworkAuthorization<TKey, TApplication, TToken>
where TScope : OpenIddictEntityFrameworkScope<TKey>
where TToken : OpenIddictEntityFrameworkToken<TKey, TApplication, TAuthorization>
where TKey : notnull, IEquatable<TKey>
{
if (builder is null)
{
if (builder is null)
{
throw new ArgumentNullException(nameof(builder));
}
throw new ArgumentNullException(nameof(builder));
}
builder.Configurations
.Add(new OpenIddictEntityFrameworkApplicationConfiguration<TApplication, TAuthorization, TToken, TKey>())
.Add(new OpenIddictEntityFrameworkAuthorizationConfiguration<TAuthorization, TApplication, TToken, TKey>())
.Add(new OpenIddictEntityFrameworkScopeConfiguration<TScope, TKey>())
.Add(new OpenIddictEntityFrameworkTokenConfiguration<TToken, TApplication, TAuthorization, TKey>());
builder.Configurations
.Add(new OpenIddictEntityFrameworkApplicationConfiguration<TApplication, TAuthorization, TToken, TKey>())
.Add(new OpenIddictEntityFrameworkAuthorizationConfiguration<TAuthorization, TApplication, TToken, TKey>())
.Add(new OpenIddictEntityFrameworkScopeConfiguration<TScope, TKey>())
.Add(new OpenIddictEntityFrameworkTokenConfiguration<TToken, TApplication, TAuthorization, TKey>());
return builder;
}
return builder;
}
/// <summary>
/// Executes the query and returns the results as a non-streamed async enumeration.
/// </summary>
/// <typeparam name="T">The type of the returned entities.</typeparam>
/// <param name="source">The query source.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The non-streamed async enumeration containing the results.</returns>
internal static IAsyncEnumerable<T> AsAsyncEnumerable<T>(this IQueryable<T> source, CancellationToken cancellationToken)
/// <summary>
/// Executes the query and returns the results as a non-streamed async enumeration.
/// </summary>
/// <typeparam name="T">The type of the returned entities.</typeparam>
/// <param name="source">The query source.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The non-streamed async enumeration containing the results.</returns>
internal static IAsyncEnumerable<T> AsAsyncEnumerable<T>(this IQueryable<T> source, CancellationToken cancellationToken)
{
if (source is null)
{
if (source is null)
{
throw new ArgumentNullException(nameof(source));
}
throw new ArgumentNullException(nameof(source));
}
return ExecuteAsync(source, cancellationToken);
return ExecuteAsync(source, cancellationToken);
static async IAsyncEnumerable<T> ExecuteAsync(IQueryable<T> source, [EnumeratorCancellation] CancellationToken cancellationToken)
static async IAsyncEnumerable<T> ExecuteAsync(IQueryable<T> source, [EnumeratorCancellation] CancellationToken cancellationToken)
{
foreach (var element in await source.ToListAsync(cancellationToken))
{
foreach (var element in await source.ToListAsync(cancellationToken))
{
yield return element;
}
yield return element;
}
}
}
}
}

23
src/OpenIddict.EntityFramework/OpenIddictEntityFrameworkOptions.cs

@ -7,19 +7,18 @@
using System;
using System.Data.Entity;
namespace OpenIddict.EntityFramework
namespace OpenIddict.EntityFramework;
/// <summary>
/// Provides various settings needed to configure
/// the OpenIddict Entity Framework 6.x integration.
/// </summary>
public class OpenIddictEntityFrameworkOptions
{
/// <summary>
/// Provides various settings needed to configure
/// the OpenIddict Entity Framework 6.x integration.
/// Gets or sets the concrete type of the <see cref="DbContext"/> used by the
/// OpenIddict Entity Framework 6.x stores. If this property is not populated,
/// an exception is thrown at runtime when trying to use the stores.
/// </summary>
public class OpenIddictEntityFrameworkOptions
{
/// <summary>
/// Gets or sets the concrete type of the <see cref="DbContext"/> used by the
/// OpenIddict Entity Framework 6.x stores. If this property is not populated,
/// an exception is thrown at runtime when trying to use the stores.
/// </summary>
public Type? DbContextType { get; set; }
}
public Type? DbContextType { get; set; }
}

101
src/OpenIddict.EntityFramework/Resolvers/OpenIddictEntityFrameworkApplicationStoreResolver.cs

@ -13,70 +13,69 @@ using OpenIddict.EntityFramework.Models;
using OpenIddict.Extensions;
using SR = OpenIddict.Abstractions.OpenIddictResources;
namespace OpenIddict.EntityFramework
namespace OpenIddict.EntityFramework;
/// <summary>
/// Exposes a method allowing to resolve an application store.
/// </summary>
public class OpenIddictEntityFrameworkApplicationStoreResolver : IOpenIddictApplicationStoreResolver
{
private readonly TypeResolutionCache _cache;
private readonly IOptionsMonitor<OpenIddictEntityFrameworkOptions> _options;
private readonly IServiceProvider _provider;
public OpenIddictEntityFrameworkApplicationStoreResolver(
TypeResolutionCache cache,
IOptionsMonitor<OpenIddictEntityFrameworkOptions> options,
IServiceProvider provider)
{
_cache = cache;
_options = options;
_provider = provider;
}
/// <summary>
/// Exposes a method allowing to resolve an application store.
/// Returns an application store compatible with the specified application type or throws an
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
/// </summary>
public class OpenIddictEntityFrameworkApplicationStoreResolver : IOpenIddictApplicationStoreResolver
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <returns>An <see cref="IOpenIddictApplicationStore{TApplication}"/>.</returns>
public IOpenIddictApplicationStore<TApplication> Get<TApplication>() where TApplication : class
{
private readonly TypeResolutionCache _cache;
private readonly IOptionsMonitor<OpenIddictEntityFrameworkOptions> _options;
private readonly IServiceProvider _provider;
public OpenIddictEntityFrameworkApplicationStoreResolver(
TypeResolutionCache cache,
IOptionsMonitor<OpenIddictEntityFrameworkOptions> options,
IServiceProvider provider)
var store = _provider.GetService<IOpenIddictApplicationStore<TApplication>>();
if (store is not null)
{
_cache = cache;
_options = options;
_provider = provider;
return store;
}
/// <summary>
/// Returns an application store compatible with the specified application type or throws an
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
/// </summary>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <returns>An <see cref="IOpenIddictApplicationStore{TApplication}"/>.</returns>
public IOpenIddictApplicationStore<TApplication> Get<TApplication>() where TApplication : class
var type = _cache.GetOrAdd(typeof(TApplication), key =>
{
var store = _provider.GetService<IOpenIddictApplicationStore<TApplication>>();
if (store is not null)
var root = OpenIddictHelpers.FindGenericBaseType(key, typeof(OpenIddictEntityFrameworkApplication<,,>));
if (root is null)
{
return store;
throw new InvalidOperationException(SR.GetResourceString(SR.ID0234));
}
var type = _cache.GetOrAdd(typeof(TApplication), key =>
var context = _options.CurrentValue.DbContextType;
if (context is null)
{
var root = OpenIddictHelpers.FindGenericBaseType(key, typeof(OpenIddictEntityFrameworkApplication<,,>));
if (root is null)
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0234));
}
var context = _options.CurrentValue.DbContextType;
if (context is null)
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0235));
}
throw new InvalidOperationException(SR.GetResourceString(SR.ID0235));
}
return typeof(OpenIddictEntityFrameworkApplicationStore<,,,,>).MakeGenericType(
/* TApplication: */ key,
/* TAuthorization: */ root.GenericTypeArguments[1],
/* TToken: */ root.GenericTypeArguments[2],
/* TContext: */ context,
/* TKey: */ root.GenericTypeArguments[0]);
});
return typeof(OpenIddictEntityFrameworkApplicationStore<,,,,>).MakeGenericType(
/* TApplication: */ key,
/* TAuthorization: */ root.GenericTypeArguments[1],
/* TToken: */ root.GenericTypeArguments[2],
/* TContext: */ context,
/* TKey: */ root.GenericTypeArguments[0]);
});
return (IOpenIddictApplicationStore<TApplication>) _provider.GetRequiredService(type);
}
// Note: Entity Framework resolvers are registered as scoped dependencies as their inner
// service provider must be able to resolve scoped services (typically, the store they return).
// To avoid having to declare a static type resolution cache, a special cache service is used
// here and registered as a singleton dependency so that its content persists beyond the scope.
public class TypeResolutionCache : ConcurrentDictionary<Type, Type> { }
return (IOpenIddictApplicationStore<TApplication>) _provider.GetRequiredService(type);
}
// Note: Entity Framework resolvers are registered as scoped dependencies as their inner
// service provider must be able to resolve scoped services (typically, the store they return).
// To avoid having to declare a static type resolution cache, a special cache service is used
// here and registered as a singleton dependency so that its content persists beyond the scope.
public class TypeResolutionCache : ConcurrentDictionary<Type, Type> { }
}

101
src/OpenIddict.EntityFramework/Resolvers/OpenIddictEntityFrameworkAuthorizationStoreResolver.cs

@ -13,70 +13,69 @@ using OpenIddict.EntityFramework.Models;
using OpenIddict.Extensions;
using SR = OpenIddict.Abstractions.OpenIddictResources;
namespace OpenIddict.EntityFramework
namespace OpenIddict.EntityFramework;
/// <summary>
/// Exposes a method allowing to resolve an authorization store.
/// </summary>
public class OpenIddictEntityFrameworkAuthorizationStoreResolver : IOpenIddictAuthorizationStoreResolver
{
private readonly TypeResolutionCache _cache;
private readonly IOptionsMonitor<OpenIddictEntityFrameworkOptions> _options;
private readonly IServiceProvider _provider;
public OpenIddictEntityFrameworkAuthorizationStoreResolver(
TypeResolutionCache cache,
IOptionsMonitor<OpenIddictEntityFrameworkOptions> options,
IServiceProvider provider)
{
_cache = cache;
_options = options;
_provider = provider;
}
/// <summary>
/// Exposes a method allowing to resolve an authorization store.
/// Returns an authorization store compatible with the specified authorization type or throws an
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
/// </summary>
public class OpenIddictEntityFrameworkAuthorizationStoreResolver : IOpenIddictAuthorizationStoreResolver
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <returns>An <see cref="IOpenIddictAuthorizationStore{TAuthorization}"/>.</returns>
public IOpenIddictAuthorizationStore<TAuthorization> Get<TAuthorization>() where TAuthorization : class
{
private readonly TypeResolutionCache _cache;
private readonly IOptionsMonitor<OpenIddictEntityFrameworkOptions> _options;
private readonly IServiceProvider _provider;
public OpenIddictEntityFrameworkAuthorizationStoreResolver(
TypeResolutionCache cache,
IOptionsMonitor<OpenIddictEntityFrameworkOptions> options,
IServiceProvider provider)
var store = _provider.GetService<IOpenIddictAuthorizationStore<TAuthorization>>();
if (store is not null)
{
_cache = cache;
_options = options;
_provider = provider;
return store;
}
/// <summary>
/// Returns an authorization store compatible with the specified authorization type or throws an
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
/// </summary>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <returns>An <see cref="IOpenIddictAuthorizationStore{TAuthorization}"/>.</returns>
public IOpenIddictAuthorizationStore<TAuthorization> Get<TAuthorization>() where TAuthorization : class
var type = _cache.GetOrAdd(typeof(TAuthorization), key =>
{
var store = _provider.GetService<IOpenIddictAuthorizationStore<TAuthorization>>();
if (store is not null)
var root = OpenIddictHelpers.FindGenericBaseType(key, typeof(OpenIddictEntityFrameworkAuthorization<,,>));
if (root is null)
{
return store;
throw new InvalidOperationException(SR.GetResourceString(SR.ID0236));
}
var type = _cache.GetOrAdd(typeof(TAuthorization), key =>
var context = _options.CurrentValue.DbContextType;
if (context is null)
{
var root = OpenIddictHelpers.FindGenericBaseType(key, typeof(OpenIddictEntityFrameworkAuthorization<,,>));
if (root is null)
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0236));
}
var context = _options.CurrentValue.DbContextType;
if (context is null)
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0235));
}
throw new InvalidOperationException(SR.GetResourceString(SR.ID0235));
}
return typeof(OpenIddictEntityFrameworkAuthorizationStore<,,,,>).MakeGenericType(
/* TAuthorization: */ key,
/* TApplication: */ root.GenericTypeArguments[1],
/* TToken: */ root.GenericTypeArguments[2],
/* TContext: */ context,
/* TKey: */ root.GenericTypeArguments[0]);
});
return typeof(OpenIddictEntityFrameworkAuthorizationStore<,,,,>).MakeGenericType(
/* TAuthorization: */ key,
/* TApplication: */ root.GenericTypeArguments[1],
/* TToken: */ root.GenericTypeArguments[2],
/* TContext: */ context,
/* TKey: */ root.GenericTypeArguments[0]);
});
return (IOpenIddictAuthorizationStore<TAuthorization>) _provider.GetRequiredService(type);
}
// Note: Entity Framework resolvers are registered as scoped dependencies as their inner
// service provider must be able to resolve scoped services (typically, the store they return).
// To avoid having to declare a static type resolution cache, a special cache service is used
// here and registered as a singleton dependency so that its content persists beyond the scope.
public class TypeResolutionCache : ConcurrentDictionary<Type, Type> { }
return (IOpenIddictAuthorizationStore<TAuthorization>) _provider.GetRequiredService(type);
}
// Note: Entity Framework resolvers are registered as scoped dependencies as their inner
// service provider must be able to resolve scoped services (typically, the store they return).
// To avoid having to declare a static type resolution cache, a special cache service is used
// here and registered as a singleton dependency so that its content persists beyond the scope.
public class TypeResolutionCache : ConcurrentDictionary<Type, Type> { }
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save