Browse Source

Adhere to the ASP.NET team coding style guidelines

pull/369/head
Kévin Chalet 10 years ago
parent
commit
e36a80bc14
  1. 54
      README.md
  2. 15
      samples/Mvc.Client/Controllers/AuthenticationController.cs
  3. 18
      samples/Mvc.Client/Controllers/HomeController.cs
  4. 9
      samples/Mvc.Client/Program.cs
  5. 21
      samples/Mvc.Client/Startup.cs
  6. 4
      samples/Mvc.Client/web.config
  7. 192
      samples/Mvc.Server/Controllers/AccountController.cs
  8. 114
      samples/Mvc.Server/Controllers/AuthorizationController.cs
  9. 15
      samples/Mvc.Server/Controllers/ErrorController.cs
  10. 18
      samples/Mvc.Server/Controllers/HomeController.cs
  11. 132
      samples/Mvc.Server/Controllers/ManageController.cs
  12. 15
      samples/Mvc.Server/Controllers/ResourceController.cs
  13. 27
      samples/Mvc.Server/Controllers/UserinfoController.cs
  14. 27
      samples/Mvc.Server/Extensions/AppBuilderExtensions.cs
  15. 21
      samples/Mvc.Server/Helpers/FormValueRequiredAttribute.cs
  16. 9
      samples/Mvc.Server/Models/ApplicationDbContext.cs
  17. 3
      samples/Mvc.Server/Models/ApplicationUser.cs
  18. 9
      samples/Mvc.Server/Program.cs
  19. 6
      samples/Mvc.Server/Services/IEmailSender.cs
  20. 6
      samples/Mvc.Server/Services/ISmsSender.cs
  21. 12
      samples/Mvc.Server/Services/MessageServices.cs
  22. 50
      samples/Mvc.Server/Startup.cs
  23. 3
      samples/Mvc.Server/ViewModels/Account/ExternalLoginConfirmationViewModel.cs
  24. 6
      samples/Mvc.Server/ViewModels/Account/ForgotPasswordViewModel.cs
  25. 6
      samples/Mvc.Server/ViewModels/Account/LoginViewModel.cs
  26. 6
      samples/Mvc.Server/ViewModels/Account/RegisterViewModel.cs
  27. 6
      samples/Mvc.Server/ViewModels/Account/ResetPasswordViewModel.cs
  28. 6
      samples/Mvc.Server/ViewModels/Account/SendCodeViewModel.cs
  29. 6
      samples/Mvc.Server/ViewModels/Account/VerifyCodeViewModel.cs
  30. 6
      samples/Mvc.Server/ViewModels/Authorization/AuthorizeViewModel.cs
  31. 6
      samples/Mvc.Server/ViewModels/Authorization/LogoutViewModel.cs
  32. 6
      samples/Mvc.Server/ViewModels/Manage/AddPhoneNumberViewModel.cs
  33. 6
      samples/Mvc.Server/ViewModels/Manage/ChangePasswordViewModel.cs
  34. 6
      samples/Mvc.Server/ViewModels/Manage/ConfigureTwoFactorViewModel.cs
  35. 6
      samples/Mvc.Server/ViewModels/Manage/FactorViewModel.cs
  36. 6
      samples/Mvc.Server/ViewModels/Manage/IndexViewModel.cs
  37. 3
      samples/Mvc.Server/ViewModels/Manage/ManageLoginsViewModel.cs
  38. 6
      samples/Mvc.Server/ViewModels/Manage/RemoveLoginViewModel.cs
  39. 6
      samples/Mvc.Server/ViewModels/Manage/SetPasswordViewModel.cs
  40. 6
      samples/Mvc.Server/ViewModels/Manage/VerifyPhoneNumberViewModel.cs
  41. 6
      samples/Mvc.Server/ViewModels/Shared/ErrorViewModel.cs
  42. 4
      samples/Mvc.Server/web.config
  43. 183
      src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs
  44. 36
      src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
  45. 9
      src/OpenIddict.Core/Managers/OpenIddictScopeManager.cs
  46. 78
      src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs
  47. 84
      src/OpenIddict.Core/OpenIddictBuilder.cs
  48. 24
      src/OpenIddict.Core/OpenIddictConstants.cs
  49. 36
      src/OpenIddict.Core/OpenIddictExtensions.cs
  50. 6
      src/OpenIddict.Core/Stores/IOpenIddictApplicationStore.cs
  51. 6
      src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs
  52. 3
      src/OpenIddict.Core/Stores/IOpenIddictScopeStore.cs
  53. 6
      src/OpenIddict.Core/Stores/IOpenIddictTokenStore.cs
  54. 15
      src/OpenIddict.EntityFrameworkCore/OpenIddictCustomizer.cs
  55. 12
      src/OpenIddict.EntityFrameworkCore/OpenIddictExtension.cs
  56. 78
      src/OpenIddict.EntityFrameworkCore/OpenIddictExtensions.cs
  57. 132
      src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs
  58. 54
      src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs
  59. 30
      src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictScopeStore.cs
  60. 120
      src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs
  61. 15
      src/OpenIddict.Models/OpenIddictApplication.cs
  62. 15
      src/OpenIddict.Models/OpenIddictAuthorization.cs
  63. 12
      src/OpenIddict.Models/OpenIddictScope.cs
  64. 15
      src/OpenIddict.Models/OpenIddictToken.cs
  65. 21
      src/OpenIddict.Mvc/OpenIddictExtensions.cs
  66. 39
      src/OpenIddict.Mvc/OpenIddictModelBinder.cs
  67. 326
      src/OpenIddict/OpenIddictExtensions.cs
  68. 9
      src/OpenIddict/OpenIddictOptions.cs
  69. 105
      src/OpenIddict/OpenIddictProvider.Authentication.cs
  70. 13
      src/OpenIddict/OpenIddictProvider.Discovery.cs
  71. 66
      src/OpenIddict/OpenIddictProvider.Exchange.cs
  72. 39
      src/OpenIddict/OpenIddictProvider.Introspection.cs
  73. 42
      src/OpenIddict/OpenIddictProvider.Revocation.cs
  74. 51
      src/OpenIddict/OpenIddictProvider.Serialization.cs
  75. 60
      src/OpenIddict/OpenIddictProvider.Session.cs
  76. 9
      src/OpenIddict/OpenIddictProvider.Userinfo.cs
  77. 6
      src/OpenIddict/OpenIddictProvider.cs
  78. 5
      test/OpenIddict.Core.Tests/OpenIddict.Core.Tests.xproj
  79. 54
      test/OpenIddict.Core.Tests/OpenIddictBuilderTests.cs
  80. 15
      test/OpenIddict.Core.Tests/OpenIddictExtensionsTests.cs
  81. 5
      test/OpenIddict.EntityFrameworkCore.Tests/OpenIddict.EntityFrameworkCore.Tests.xproj
  82. 45
      test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictExtensionsTests.cs
  83. 7
      test/OpenIddict.Mvc.Tests/OpenIddict.Mvc.Tests.xproj
  84. 9
      test/OpenIddict.Mvc.Tests/OpenIddictExtensionsTests.cs
  85. 66
      test/OpenIddict.Mvc.Tests/OpenIddictModelBinderTests.cs
  86. 7
      test/OpenIddict.Tests/OpenIddict.Tests.xproj
  87. 120
      test/OpenIddict.Tests/OpenIddictExtensionsTests.cs
  88. 192
      test/OpenIddict.Tests/OpenIddictProviderTests.Authentication.cs
  89. 36
      test/OpenIddict.Tests/OpenIddictProviderTests.Discovery.cs
  90. 204
      test/OpenIddict.Tests/OpenIddictProviderTests.Exchange.cs
  91. 120
      test/OpenIddict.Tests/OpenIddictProviderTests.Introspection.cs
  92. 108
      test/OpenIddict.Tests/OpenIddictProviderTests.Revocation.cs
  93. 117
      test/OpenIddict.Tests/OpenIddictProviderTests.Serialization.cs
  94. 69
      test/OpenIddict.Tests/OpenIddictProviderTests.Session.cs
  95. 12
      test/OpenIddict.Tests/OpenIddictProviderTests.Userinfo.cs
  96. 57
      test/OpenIddict.Tests/OpenIddictProviderTests.cs

54
README.md

@ -69,10 +69,12 @@ To use OpenIddict, you need to:
- **Configure the OpenIddict services** in `Startup.ConfigureServices`:
```csharp
public void ConfigureServices(IServiceCollection services) {
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext<ApplicationDbContext>(options => {
services.AddDbContext<ApplicationDbContext>(options =>
{
// Configure the context to use Microsoft SQL Server.
options.UseSqlServer(configuration["Data:DefaultConnection:ConnectionString"]);
@ -90,23 +92,25 @@ public void ConfigureServices(IServiceCollection services) {
// Register the OpenIddict services.
// Note: use the generic overload if you need
// to replace the default OpenIddict entities.
services.AddOpenIddict()
services.AddOpenIddict(options =>
{
// Register the Entity Framework stores.
.AddEntityFrameworkCoreStores<ApplicationDbContext>()
options.AddEntityFrameworkCoreStores<ApplicationDbContext>();
// Register the ASP.NET Core MVC binder used by OpenIddict.
// Note: if you don't call this method, you won't be able to
// bind OpenIdConnectRequest or OpenIdConnectResponse parameters.
.AddMvcBinders()
options.AddMvcBinders();
// Enable the token endpoint (required to use the password flow).
.EnableTokenEndpoint("/connect/token")
options.EnableTokenEndpoint("/connect/token");
// Allow client applications to use the grant_type=password flow.
.AllowPasswordFlow()
options.AllowPasswordFlow();
// During development, you can disable the HTTPS requirement.
.DisableHttpsRequirement();
options.DisableHttpsRequirement();
});
}
```
@ -133,7 +137,8 @@ public void Configure(IApplicationBuilder app) {
- **Update your Entity Framework context registration to register the OpenIddict entities**:
```csharp
services.AddDbContext<ApplicationDbContext>(options => {
services.AddDbContext<ApplicationDbContext>(options =>
{
// Configure the context to use Microsoft SQL Server.
options.UseSqlServer(configuration["Data:DefaultConnection:ConnectionString"]);
@ -151,7 +156,8 @@ services.AddDbContext<ApplicationDbContext>(options => {
services.AddOpenIddict<Guid>()
.AddEntityFrameworkCoreStores<ApplicationDbContext>()
services.AddDbContext<ApplicationDbContext>(options => {
services.AddDbContext<ApplicationDbContext>(options =>
{
// Configure the context to use Microsoft SQL Server.
options.UseSqlServer(configuration["Data:DefaultConnection:ConnectionString"]);
@ -169,28 +175,31 @@ The **Mvc.Server sample comes with an [`AuthorizationController` that supports b
- **Enable the corresponding flows in the OpenIddict options**:
```csharp
public void ConfigureServices(IServiceCollection services) {
public void ConfigureServices(IServiceCollection services)
{
// Register the OpenIddict services.
// Note: use the generic overload if you need
// to replace the default OpenIddict entities.
services.AddOpenIddict()
services.AddOpenIddict(options =>
{
// Register the Entity Framework stores.
.AddEntityFrameworkCoreStores<ApplicationDbContext>()
options.AddEntityFrameworkCoreStores<ApplicationDbContext>();
// Register the ASP.NET Core MVC binder used by OpenIddict.
// Note: if you don't call this method, you won't be able to
// bind OpenIdConnectRequest or OpenIdConnectResponse parameters.
.AddMvcBinders()
options.AddMvcBinders();
// Enable the authorization and token endpoints (required to use the code flow).
.EnableAuthorizationEndpoint("/connect/authorize")
.EnableTokenEndpoint("/connect/token")
options.EnableAuthorizationEndpoint("/connect/authorize")
.EnableTokenEndpoint("/connect/token");
// Allow client applications to use the code flow.
.AllowAuthorizationCodeFlow()
options.AllowAuthorizationCodeFlow();
// During development, you can disable the HTTPS requirement.
.DisableHttpsRequirement();
options.DisableHttpsRequirement();
});
}
```
@ -198,15 +207,18 @@ public void ConfigureServices(IServiceCollection services) {
```csharp
// Create a new service scope to ensure the database context is correctly disposed when this methods returns.
using (var scope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope()) {
using (var scope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
await context.Database.EnsureCreatedAsync();
// Note: when using a custom entity or a custom key type, replace OpenIddictApplication by the appropriate type.
var manager = scope.ServiceProvider.GetRequiredService<OpenIddictApplicationManager<OpenIddictApplication>>();
if (await manager.FindByClientIdAsync("[client identifier]", cancellationToken) == null) {
var application = new OpenIddictApplication {
if (await manager.FindByClientIdAsync("[client identifier]", cancellationToken) == null)
{
var application = new OpenIddictApplication
{
ClientId = "[client identifier]",
RedirectUri = "[redirect uri]"
};

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

@ -4,19 +4,24 @@ using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.Mvc;
namespace Mvc.Client.Controllers {
public class AuthenticationController : Controller {
namespace Mvc.Client.Controllers
{
public class AuthenticationController : Controller
{
[HttpGet("~/signin")]
public ActionResult SignIn() {
public ActionResult SignIn()
{
// 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 new ChallengeResult(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties {
return new ChallengeResult(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties
{
RedirectUri = "/"
});
}
[HttpGet("~/signout"), HttpPost("~/signout")]
public async Task SignOut() {
public async Task SignOut()
{
// 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.
await HttpContext.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);

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

@ -8,23 +8,29 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Mvc.Client.Controllers {
public class HomeController : Controller {
namespace Mvc.Client.Controllers
{
public class HomeController : Controller
{
private readonly HttpClient _client;
public HomeController(HttpClient client) {
public HomeController(HttpClient client)
{
_client = client;
}
[HttpGet("~/")]
public ActionResult Index() {
public ActionResult Index()
{
return View("Home");
}
[Authorize, HttpPost("~/")]
public async Task<ActionResult> Index(CancellationToken cancellationToken) {
public async Task<ActionResult> Index(CancellationToken cancellationToken)
{
var token = await HttpContext.Authentication.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
if (string.IsNullOrEmpty(token)) {
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.");
}

9
samples/Mvc.Client/Program.cs

@ -2,9 +2,12 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Mvc.Client {
public static class Program {
public static void Main(string[] args) {
namespace Mvc.Client
{
public static class Program
{
public static void Main(string[] args)
{
var configuration = new ConfigurationBuilder()
.AddEnvironmentVariables()
.AddCommandLine(args)

21
samples/Mvc.Client/Startup.cs

@ -6,10 +6,14 @@ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Mvc.Client {
public class Startup {
public void ConfigureServices(IServiceCollection services) {
services.AddAuthentication(options => {
namespace Mvc.Client
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
});
@ -18,20 +22,23 @@ namespace Mvc.Client {
services.AddSingleton<HttpClient>();
}
public void Configure(IApplicationBuilder app) {
public void Configure(IApplicationBuilder app)
{
app.UseDeveloperExceptionPage();
app.UseStaticFiles();
// Insert a new cookies middleware in the pipeline to store the user
// identity after he has been redirected from the identity provider.
app.UseCookieAuthentication(new CookieAuthenticationOptions {
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
LoginPath = new PathString("/signin")
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions {
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
// Note: these settings must match the application details
// inserted in the database at the server level.
ClientId = "mvc",

4
samples/Mvc.Client/web.config

@ -2,8 +2,8 @@
<configuration>
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/>
</handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/>
</system.webServer>
</configuration>

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

@ -9,9 +9,11 @@ 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 {
public class AccountController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IEmailSender _emailSender;
@ -24,7 +26,8 @@ namespace Mvc.Server.Controllers {
SignInManager<ApplicationUser> signInManager,
IEmailSender emailSender,
ISmsSender smsSender,
ApplicationDbContext applicationDbContext) {
ApplicationDbContext applicationDbContext)
{
_userManager = userManager;
_signInManager = signInManager;
_emailSender = emailSender;
@ -36,7 +39,8 @@ namespace Mvc.Server.Controllers {
// GET: /Account/Login
[HttpGet]
[AllowAnonymous]
public IActionResult Login(string returnUrl = null) {
public IActionResult Login(string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
return View();
}
@ -46,23 +50,29 @@ namespace Mvc.Server.Controllers {
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null) {
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
EnsureDatabaseCreated(_applicationDbContext);
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid) {
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) {
if (result.Succeeded)
{
return RedirectToLocal(returnUrl);
}
if (result.RequiresTwoFactor) {
if (result.RequiresTwoFactor)
{
return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
}
if (result.IsLockedOut) {
if (result.IsLockedOut)
{
return View("Lockout");
}
else {
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return View(model);
}
@ -76,7 +86,8 @@ namespace Mvc.Server.Controllers {
// GET: /Account/Register
[HttpGet]
[AllowAnonymous]
public IActionResult Register(string returnUrl = null) {
public IActionResult Register(string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
return View();
}
@ -86,13 +97,16 @@ namespace Mvc.Server.Controllers {
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null) {
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
{
EnsureDatabaseCreated(_applicationDbContext);
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid) {
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded) {
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);
@ -113,7 +127,8 @@ namespace Mvc.Server.Controllers {
// POST: /Account/LogOff
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> LogOff() {
public async Task<IActionResult> LogOff()
{
await _signInManager.SignOutAsync();
return RedirectToAction(nameof(HomeController.Index), "Home");
}
@ -123,7 +138,8 @@ namespace Mvc.Server.Controllers {
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public IActionResult ExternalLogin(string provider, string returnUrl = null) {
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 });
@ -135,24 +151,30 @@ namespace Mvc.Server.Controllers {
// GET: /Account/ExternalLoginCallback
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null) {
public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null)
{
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null) {
if (info == null)
{
return RedirectToAction(nameof(Login));
}
// 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) {
if (result.Succeeded)
{
return RedirectToLocal(returnUrl);
}
if (result.RequiresTwoFactor) {
if (result.RequiresTwoFactor)
{
return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl });
}
if (result.IsLockedOut) {
if (result.IsLockedOut)
{
return View("Lockout");
}
else {
else
{
// If the user does not have an account, then ask the user to create an account.
ViewData["ReturnUrl"] = returnUrl;
ViewData["LoginProvider"] = info.LoginProvider;
@ -166,18 +188,23 @@ namespace Mvc.Server.Controllers {
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl = null) {
if (ModelState.IsValid) {
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 == null) {
if (info == null)
{
return View("ExternalLoginFailure");
}
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await _userManager.CreateAsync(user);
if (result.Succeeded) {
if (result.Succeeded)
{
result = await _userManager.AddLoginAsync(user, info);
if (result.Succeeded) {
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToLocal(returnUrl);
}
@ -192,12 +219,15 @@ namespace Mvc.Server.Controllers {
// GET: /Account/ConfirmEmail
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> ConfirmEmail(string userId, string code) {
if (userId == null || code == null) {
public async Task<IActionResult> ConfirmEmail(string userId, string code)
{
if (userId == null || code == null)
{
return View("Error");
}
var user = await _userManager.FindByIdAsync(userId);
if (user == null) {
if (user == null)
{
return View("Error");
}
var result = await _userManager.ConfirmEmailAsync(user, code);
@ -208,7 +238,8 @@ namespace Mvc.Server.Controllers {
// GET: /Account/ForgotPassword
[HttpGet]
[AllowAnonymous]
public IActionResult ForgotPassword() {
public IActionResult ForgotPassword()
{
return View();
}
@ -217,10 +248,13 @@ namespace Mvc.Server.Controllers {
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model) {
if (ModelState.IsValid) {
public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
if (ModelState.IsValid)
{
var user = await _userManager.FindByNameAsync(model.Email);
if (user == null || !(await _userManager.IsEmailConfirmedAsync(user))) {
if (user == null || !(await _userManager.IsEmailConfirmedAsync(user)))
{
// Don't reveal that the user does not exist or is not confirmed
return View("ForgotPasswordConfirmation");
}
@ -242,7 +276,8 @@ namespace Mvc.Server.Controllers {
// GET: /Account/ForgotPasswordConfirmation
[HttpGet]
[AllowAnonymous]
public IActionResult ForgotPasswordConfirmation() {
public IActionResult ForgotPasswordConfirmation()
{
return View();
}
@ -250,7 +285,8 @@ namespace Mvc.Server.Controllers {
// GET: /Account/ResetPassword
[HttpGet]
[AllowAnonymous]
public IActionResult ResetPassword(string code = null) {
public IActionResult ResetPassword(string code = null)
{
return code == null ? View("Error") : View();
}
@ -259,17 +295,21 @@ namespace Mvc.Server.Controllers {
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ResetPassword(ResetPasswordViewModel model) {
if (!ModelState.IsValid) {
public async Task<IActionResult> ResetPassword(ResetPasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await _userManager.FindByNameAsync(model.Email);
if (user == null) {
if (user == 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) {
if (result.Succeeded)
{
return RedirectToAction(nameof(AccountController.ResetPasswordConfirmation), "Account");
}
AddErrors(result);
@ -280,7 +320,8 @@ namespace Mvc.Server.Controllers {
// GET: /Account/ResetPasswordConfirmation
[HttpGet]
[AllowAnonymous]
public IActionResult ResetPasswordConfirmation() {
public IActionResult ResetPasswordConfirmation()
{
return View();
}
@ -288,9 +329,11 @@ namespace Mvc.Server.Controllers {
// GET: /Account/SendCode
[HttpGet]
[AllowAnonymous]
public async Task<ActionResult> SendCode(string returnUrl = null, bool rememberMe = false) {
public async Task<ActionResult> SendCode(string returnUrl = null, bool rememberMe = false)
{
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null) {
if (user == null)
{
return View("Error");
}
var userFactors = await _userManager.GetValidTwoFactorProvidersAsync(user);
@ -303,27 +346,33 @@ namespace Mvc.Server.Controllers {
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SendCode(SendCodeViewModel model) {
if (!ModelState.IsValid) {
public async Task<IActionResult> SendCode(SendCodeViewModel model)
{
if (!ModelState.IsValid)
{
return View();
}
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null) {
if (user == null)
{
return View("Error");
}
// Generate the token and send it
var code = await _userManager.GenerateTwoFactorTokenAsync(user, model.SelectedProvider);
if (string.IsNullOrWhiteSpace(code)) {
if (string.IsNullOrWhiteSpace(code))
{
return View("Error");
}
var message = "Your security code is: " + code;
if (model.SelectedProvider == "Email") {
if (model.SelectedProvider == "Email")
{
await _emailSender.SendEmailAsync(await _userManager.GetEmailAsync(user), "Security Code", message);
}
else if (model.SelectedProvider == "Phone") {
else if (model.SelectedProvider == "Phone")
{
await _smsSender.SendSmsAsync(await _userManager.GetPhoneNumberAsync(user), message);
}
@ -334,10 +383,12 @@ namespace Mvc.Server.Controllers {
// GET: /Account/VerifyCode
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> VerifyCode(string provider, bool rememberMe, string returnUrl = null) {
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 == null) {
if (user == null)
{
return View("Error");
}
return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe });
@ -348,8 +399,10 @@ namespace Mvc.Server.Controllers {
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> VerifyCode(VerifyCodeViewModel model) {
if (!ModelState.IsValid) {
public async Task<IActionResult> VerifyCode(VerifyCodeViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
@ -357,13 +410,16 @@ namespace Mvc.Server.Controllers {
// 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 (result.Succeeded)
{
return RedirectToLocal(model.ReturnUrl);
}
if (result.IsLockedOut) {
if (result.IsLockedOut)
{
return View("Lockout");
}
else {
else
{
ModelState.AddModelError("", "Invalid code.");
return View(model);
}
@ -376,28 +432,36 @@ namespace Mvc.Server.Controllers {
// 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) {
private static void EnsureDatabaseCreated(ApplicationDbContext context)
{
if (!_databaseChecked)
{
_databaseChecked = true;
context.Database.EnsureCreated();
}
}
private void AddErrors(IdentityResult result) {
foreach (var error in result.Errors) {
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
private async Task<ApplicationUser> GetCurrentUserAsync() {
private async Task<ApplicationUser> GetCurrentUserAsync()
{
return await _userManager.GetUserAsync(User);
}
private IActionResult RedirectToLocal(string returnUrl) {
if (Url.IsLocalUrl(returnUrl)) {
private IActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else {
else
{
return RedirectToAction(nameof(HomeController.Index), "Home");
}
}

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

@ -22,8 +22,10 @@ using Mvc.Server.ViewModels.Shared;
using OpenIddict.Core;
using OpenIddict.Models;
namespace Mvc.Server {
public class AuthorizationController : Controller {
namespace Mvc.Server
{
public class AuthorizationController : Controller
{
private readonly OpenIddictApplicationManager<OpenIddictApplication> _applicationManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly UserManager<ApplicationUser> _userManager;
@ -31,7 +33,8 @@ namespace Mvc.Server {
public AuthorizationController(
OpenIddictApplicationManager<OpenIddictApplication> applicationManager,
SignInManager<ApplicationUser> signInManager,
UserManager<ApplicationUser> userManager) {
UserManager<ApplicationUser> userManager)
{
_applicationManager = applicationManager;
_signInManager = signInManager;
_userManager = userManager;
@ -42,15 +45,18 @@ namespace Mvc.Server {
// you must provide your own authorization endpoint action:
[Authorize, HttpGet("~/connect/authorize")]
public async Task<IActionResult> Authorize(OpenIdConnectRequest request) {
public async Task<IActionResult> Authorize(OpenIdConnectRequest request)
{
Debug.Assert(request.IsAuthorizationRequest(),
"The OpenIddict binder for ASP.NET Core MVC is not registered. " +
"Make sure services.AddOpenIddict().AddMvcBinders() is correctly called.");
// Retrieve the application details from the database.
var application = await _applicationManager.FindByClientIdAsync(request.ClientId, HttpContext.RequestAborted);
if (application == null) {
return View("Error", new ErrorViewModel {
if (application == null)
{
return View("Error", new ErrorViewModel
{
Error = OpenIdConnectConstants.Errors.InvalidClient,
ErrorDescription = "Details concerning the calling client application cannot be found in the database"
});
@ -58,7 +64,8 @@ namespace Mvc.Server {
// Flow the request_id to allow OpenIddict to restore
// the original authorization request from the cache.
return View(new AuthorizeViewModel {
return View(new AuthorizeViewModel
{
ApplicationName = application.DisplayName,
RequestId = request.RequestId,
Scope = request.Scope
@ -67,15 +74,18 @@ namespace Mvc.Server {
[Authorize, FormValueRequired("submit.Accept")]
[HttpPost("~/connect/authorize"), ValidateAntiForgeryToken]
public async Task<IActionResult> Accept(OpenIdConnectRequest request) {
public async Task<IActionResult> Accept(OpenIdConnectRequest request)
{
Debug.Assert(request.IsAuthorizationRequest(),
"The OpenIddict binder for ASP.NET Core MVC is not registered. " +
"Make sure services.AddOpenIddict().AddMvcBinders() is correctly called.");
// Retrieve the profile of the logged in user.
var user = await _userManager.GetUserAsync(User);
if (user == null) {
return View("Error", new ErrorViewModel {
if (user == null)
{
return View("Error", new ErrorViewModel
{
Error = OpenIdConnectConstants.Errors.ServerError,
ErrorDescription = "An internal error has occurred"
});
@ -90,7 +100,8 @@ namespace Mvc.Server {
[Authorize, FormValueRequired("submit.Deny")]
[HttpPost("~/connect/authorize"), ValidateAntiForgeryToken]
public IActionResult Deny() {
public IActionResult Deny()
{
// 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.
return Forbid(OpenIdConnectServerDefaults.AuthenticationScheme);
@ -100,20 +111,23 @@ namespace Mvc.Server {
// flows like the authorization code flow or the implicit flow.
[HttpGet("~/connect/logout")]
public IActionResult Logout(OpenIdConnectRequest request) {
public IActionResult Logout(OpenIdConnectRequest request)
{
Debug.Assert(request.IsLogoutRequest(),
"The OpenIddict binder for ASP.NET Core MVC is not registered. " +
"Make sure services.AddOpenIddict().AddMvcBinders() is correctly called.");
// Flow the request_id to allow OpenIddict to restore
// the original logout request from the distributed cache.
return View(new LogoutViewModel {
return View(new LogoutViewModel
{
RequestId = request.RequestId
});
}
[HttpPost("~/connect/logout"), ValidateAntiForgeryToken]
public async Task<IActionResult> Logout() {
public async Task<IActionResult> Logout()
{
// 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).
@ -130,57 +144,71 @@ namespace Mvc.Server {
// you must provide your own token endpoint action:
[HttpPost("~/connect/token"), Produces("application/json")]
public async Task<IActionResult> Exchange(OpenIdConnectRequest request) {
public async Task<IActionResult> Exchange(OpenIdConnectRequest request)
{
Debug.Assert(request.IsTokenRequest(),
"The OpenIddict binder for ASP.NET Core MVC is not registered. " +
"Make sure services.AddOpenIddict().AddMvcBinders() is correctly called.");
if (request.IsPasswordGrantType()) {
if (request.IsPasswordGrantType())
{
var user = await _userManager.FindByNameAsync(request.Username);
if (user == null) {
return BadRequest(new OpenIdConnectResponse {
if (user == null)
{
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The username/password couple is invalid."
});
}
// Ensure the user is allowed to sign in.
if (!await _signInManager.CanSignInAsync(user)) {
return BadRequest(new OpenIdConnectResponse {
if (!await _signInManager.CanSignInAsync(user))
{
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The specified user is not allowed to sign in."
});
}
// Reject the token request if two-factor authentication has been enabled by the user.
if (_userManager.SupportsUserTwoFactor && await _userManager.GetTwoFactorEnabledAsync(user)) {
return BadRequest(new OpenIdConnectResponse {
if (_userManager.SupportsUserTwoFactor && await _userManager.GetTwoFactorEnabledAsync(user))
{
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The specified user is not allowed to sign in."
});
}
// Ensure the user is not already locked out.
if (_userManager.SupportsUserLockout && await _userManager.IsLockedOutAsync(user)) {
return BadRequest(new OpenIdConnectResponse {
if (_userManager.SupportsUserLockout && await _userManager.IsLockedOutAsync(user))
{
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The username/password couple is invalid."
});
}
// Ensure the password is valid.
if (!await _userManager.CheckPasswordAsync(user, request.Password)) {
if (_userManager.SupportsUserLockout) {
if (!await _userManager.CheckPasswordAsync(user, request.Password))
{
if (_userManager.SupportsUserLockout)
{
await _userManager.AccessFailedAsync(user);
}
return BadRequest(new OpenIdConnectResponse {
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The username/password couple is invalid."
});
}
if (_userManager.SupportsUserLockout) {
if (_userManager.SupportsUserLockout)
{
await _userManager.ResetAccessFailedCountAsync(user);
}
@ -190,23 +218,28 @@ namespace Mvc.Server {
return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
}
else if (request.IsAuthorizationCodeGrantType() || request.IsRefreshTokenGrantType()) {
else if (request.IsAuthorizationCodeGrantType() || request.IsRefreshTokenGrantType())
{
// Retrieve the claims principal stored in the authorization code/refresh token.
var info = await HttpContext.Authentication.GetAuthenticateInfoAsync(
OpenIdConnectServerDefaults.AuthenticationScheme);
// Retrieve the user profile corresponding to the authorization code/refresh token.
var user = await _userManager.GetUserAsync(info.Principal);
if (user == null) {
return BadRequest(new OpenIdConnectResponse {
if (user == null)
{
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The token is no longer valid."
});
}
// Ensure the user is still allowed to sign in.
if (!await _signInManager.CanSignInAsync(user)) {
return BadRequest(new OpenIdConnectResponse {
if (!await _signInManager.CanSignInAsync(user))
{
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The user is no longer allowed to sign in."
});
@ -219,7 +252,8 @@ namespace Mvc.Server {
return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
}
return BadRequest(new OpenIdConnectResponse {
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.UnsupportedGrantType,
ErrorDescription = "The specified grant type is not supported."
});
@ -228,7 +262,8 @@ namespace Mvc.Server {
private async Task<AuthenticationTicket> CreateTicketAsync(
OpenIdConnectRequest request, ApplicationUser user,
AuthenticationProperties properties = null) {
AuthenticationProperties properties = null)
{
// Create a new ClaimsPrincipal containing the claims that
// will be used to create an id_token, a token or a code.
var principal = await _signInManager.CreateUserPrincipalAsync(user);
@ -237,7 +272,8 @@ namespace Mvc.Server {
// To allow OpenIddict to serialize them, you must attach them a destination, that specifies
// whether they should be included in access tokens, in identity tokens or in both.
foreach (var claim in principal.Claims) {
foreach (var claim in principal.Claims)
{
// In this sample, every claim is serialized in both the access and the identity tokens.
// In a real world application, you'd probably want to exclude confidential claims
// or apply a claims policy based on the scopes requested by the client application.
@ -249,11 +285,13 @@ namespace Mvc.Server {
var ticket = new AuthenticationTicket(principal, properties,
OpenIdConnectServerDefaults.AuthenticationScheme);
if (!request.IsAuthorizationCodeGrantType() && !request.IsRefreshTokenGrantType()) {
if (!request.IsAuthorizationCodeGrantType() && !request.IsRefreshTokenGrantType())
{
// Set the list of scopes granted to the client application.
// Note: the offline_access scope must be granted
// to allow OpenIddict to return a refresh token.
ticket.SetScopes(new[] {
ticket.SetScopes(new[]
{
OpenIdConnectConstants.Scopes.OpenId,
OpenIdConnectConstants.Scopes.Email,
OpenIdConnectConstants.Scopes.Profile,

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

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

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

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

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

@ -8,9 +8,11 @@ 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 {
public class ManageController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IEmailSender _emailSender;
@ -22,7 +24,8 @@ namespace Mvc.Server.Controllers {
SignInManager<ApplicationUser> signInManager,
IEmailSender emailSender,
ISmsSender smsSender,
ILoggerFactory loggerFactory) {
ILoggerFactory loggerFactory)
{
_userManager = userManager;
_signInManager = signInManager;
_emailSender = emailSender;
@ -33,7 +36,8 @@ namespace Mvc.Server.Controllers {
//
// GET: /Manage/Index
[HttpGet]
public async Task<IActionResult> Index(ManageMessageId? message = null) {
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."
@ -44,7 +48,8 @@ namespace Mvc.Server.Controllers {
: "";
var user = await GetCurrentUserAsync();
var model = new IndexViewModel {
var model = new IndexViewModel
{
HasPassword = await _userManager.HasPasswordAsync(user),
PhoneNumber = await _userManager.GetPhoneNumberAsync(user),
TwoFactor = await _userManager.GetTwoFactorEnabledAsync(user),
@ -58,12 +63,15 @@ namespace Mvc.Server.Controllers {
// POST: /Manage/RemoveLogin
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> RemoveLogin(RemoveLoginViewModel account) {
public async Task<IActionResult> RemoveLogin(RemoveLoginViewModel account)
{
ManageMessageId? message = ManageMessageId.Error;
var user = await GetCurrentUserAsync();
if (user != null) {
if (user != null)
{
var result = await _userManager.RemoveLoginAsync(user, account.LoginProvider, account.ProviderKey);
if (result.Succeeded) {
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
message = ManageMessageId.RemoveLoginSuccess;
}
@ -73,7 +81,8 @@ namespace Mvc.Server.Controllers {
//
// GET: /Manage/AddPhoneNumber
public IActionResult AddPhoneNumber() {
public IActionResult AddPhoneNumber()
{
return View();
}
@ -81,8 +90,10 @@ namespace Mvc.Server.Controllers {
// POST: /Manage/AddPhoneNumber
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddPhoneNumber(AddPhoneNumberViewModel model) {
if (!ModelState.IsValid) {
public async Task<IActionResult> AddPhoneNumber(AddPhoneNumberViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Generate the token and send it
@ -96,9 +107,11 @@ namespace Mvc.Server.Controllers {
// POST: /Manage/EnableTwoFactorAuthentication
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EnableTwoFactorAuthentication() {
public async Task<IActionResult> EnableTwoFactorAuthentication()
{
var user = await GetCurrentUserAsync();
if (user != null) {
if (user != null)
{
await _userManager.SetTwoFactorEnabledAsync(user, true);
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation(1, "User enabled two-factor authentication.");
@ -110,9 +123,11 @@ namespace Mvc.Server.Controllers {
// POST: /Manage/DisableTwoFactorAuthentication
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DisableTwoFactorAuthentication() {
public async Task<IActionResult> DisableTwoFactorAuthentication()
{
var user = await GetCurrentUserAsync();
if (user != null) {
if (user != null)
{
await _userManager.SetTwoFactorEnabledAsync(user, false);
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation(2, "User disabled two-factor authentication.");
@ -123,7 +138,8 @@ namespace Mvc.Server.Controllers {
//
// GET: /Manage/VerifyPhoneNumber
[HttpGet]
public async Task<IActionResult> VerifyPhoneNumber(string phoneNumber) {
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 == null ? View("Error") : View(new VerifyPhoneNumberViewModel { PhoneNumber = phoneNumber });
@ -133,14 +149,18 @@ namespace Mvc.Server.Controllers {
// POST: /Manage/VerifyPhoneNumber
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> VerifyPhoneNumber(VerifyPhoneNumberViewModel model) {
if (!ModelState.IsValid) {
public async Task<IActionResult> VerifyPhoneNumber(VerifyPhoneNumberViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await GetCurrentUserAsync();
if (user != null) {
if (user != null)
{
var result = await _userManager.ChangePhoneNumberAsync(user, model.PhoneNumber, model.Code);
if (result.Succeeded) {
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.AddPhoneSuccess });
}
@ -154,11 +174,14 @@ namespace Mvc.Server.Controllers {
// GET: /Manage/RemovePhoneNumber
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> RemovePhoneNumber() {
public async Task<IActionResult> RemovePhoneNumber()
{
var user = await GetCurrentUserAsync();
if (user != null) {
if (user != null)
{
var result = await _userManager.SetPhoneNumberAsync(user, null);
if (result.Succeeded) {
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.RemovePhoneSuccess });
}
@ -169,7 +192,8 @@ namespace Mvc.Server.Controllers {
//
// GET: /Manage/ChangePassword
[HttpGet]
public IActionResult ChangePassword() {
public IActionResult ChangePassword()
{
return View();
}
@ -177,14 +201,18 @@ namespace Mvc.Server.Controllers {
// POST: /Manage/ChangePassword
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ChangePassword(ChangePasswordViewModel model) {
if (!ModelState.IsValid) {
public async Task<IActionResult> ChangePassword(ChangePasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await GetCurrentUserAsync();
if (user != null) {
if (user != null)
{
var result = await _userManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword);
if (result.Succeeded) {
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 });
@ -198,7 +226,8 @@ namespace Mvc.Server.Controllers {
//
// GET: /Manage/SetPassword
[HttpGet]
public IActionResult SetPassword() {
public IActionResult SetPassword()
{
return View();
}
@ -206,15 +235,19 @@ namespace Mvc.Server.Controllers {
// POST: /Manage/SetPassword
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SetPassword(SetPasswordViewModel model) {
if (!ModelState.IsValid) {
public async Task<IActionResult> SetPassword(SetPasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await GetCurrentUserAsync();
if (user != null) {
if (user != null)
{
var result = await _userManager.AddPasswordAsync(user, model.NewPassword);
if (result.Succeeded) {
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.SetPasswordSuccess });
}
@ -226,20 +259,23 @@ namespace Mvc.Server.Controllers {
//GET: /Manage/ManageLogins
[HttpGet]
public async Task<IActionResult> ManageLogins(ManageMessageId? message = null) {
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 == null) {
if (user == null)
{
return View("Error");
}
var userLogins = await _userManager.GetLoginsAsync(user);
var otherLogins = _signInManager.GetExternalAuthenticationSchemes().Where(auth => userLogins.All(ul => auth.AuthenticationScheme != ul.LoginProvider)).ToList();
ViewData["ShowRemoveButton"] = user.PasswordHash != null || userLogins.Count > 1;
return View(new ManageLoginsViewModel {
return View(new ManageLoginsViewModel
{
CurrentLogins = userLogins,
OtherLogins = otherLogins
});
@ -249,7 +285,8 @@ namespace Mvc.Server.Controllers {
// POST: /Manage/LinkLogin
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult LinkLogin(string provider) {
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));
@ -259,13 +296,16 @@ namespace Mvc.Server.Controllers {
//
// GET: /Manage/LinkLoginCallback
[HttpGet]
public async Task<ActionResult> LinkLoginCallback() {
public async Task<ActionResult> LinkLoginCallback()
{
var user = await GetCurrentUserAsync();
if (user == null) {
if (user == null)
{
return View("Error");
}
var info = await _signInManager.GetExternalLoginInfoAsync(await _userManager.GetUserIdAsync(user));
if (info == null) {
if (info == null)
{
return RedirectToAction(nameof(ManageLogins), new { Message = ManageMessageId.Error });
}
var result = await _userManager.AddLoginAsync(user, info);
@ -275,13 +315,16 @@ namespace Mvc.Server.Controllers {
#region Helpers
private void AddErrors(IdentityResult result) {
foreach (var error in result.Errors) {
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
public enum ManageMessageId {
public enum ManageMessageId
{
AddPhoneSuccess,
AddLoginSuccess,
ChangePasswordSuccess,
@ -292,7 +335,8 @@ namespace Mvc.Server.Controllers {
Error
}
private Task<ApplicationUser> GetCurrentUserAsync() {
private Task<ApplicationUser> GetCurrentUserAsync()
{
return _userManager.GetUserAsync(User);
}

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

@ -5,20 +5,25 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Mvc.Server.Models;
namespace Mvc.Server.Controllers {
namespace Mvc.Server.Controllers
{
[Route("api")]
public class ResourceController : Controller {
public class ResourceController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
public ResourceController(UserManager<ApplicationUser> userManager) {
public ResourceController(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
[Authorize(ActiveAuthenticationSchemes = OAuthValidationDefaults.AuthenticationScheme)]
[HttpGet("message")]
public async Task<IActionResult> GetMessage() {
public async Task<IActionResult> GetMessage()
{
var user = await _userManager.GetUserAsync(User);
if (user == null) {
if (user == null)
{
return BadRequest();
}

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

@ -8,12 +8,15 @@ using Mvc.Server.Models;
using Newtonsoft.Json.Linq;
using OpenIddict.Core;
namespace Mvc.Server.Controllers {
namespace Mvc.Server.Controllers
{
[Route("api")]
public class UserinfoController : Controller {
public class UserinfoController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
public UserinfoController(UserManager<ApplicationUser> userManager) {
public UserinfoController(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
@ -21,10 +24,13 @@ namespace Mvc.Server.Controllers {
// GET: /api/userinfo
[Authorize(ActiveAuthenticationSchemes = OAuthValidationDefaults.AuthenticationScheme)]
[HttpGet("userinfo"), Produces("application/json")]
public async Task<IActionResult> Userinfo() {
public async Task<IActionResult> Userinfo()
{
var user = await _userManager.GetUserAsync(User);
if (user == null) {
return BadRequest(new OpenIdConnectResponse {
if (user == null)
{
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The user profile is no longer available."
});
@ -35,17 +41,20 @@ namespace Mvc.Server.Controllers {
// Note: the "sub" claim is a mandatory claim and must be included in the JSON response.
claims[OpenIdConnectConstants.Claims.Subject] = await _userManager.GetUserIdAsync(user);
if (User.HasClaim(OpenIdConnectConstants.Claims.Scope, OpenIdConnectConstants.Scopes.Email)) {
if (User.HasClaim(OpenIdConnectConstants.Claims.Scope, OpenIdConnectConstants.Scopes.Email))
{
claims[OpenIdConnectConstants.Claims.Email] = await _userManager.GetEmailAsync(user);
claims[OpenIdConnectConstants.Claims.EmailVerified] = await _userManager.IsEmailConfirmedAsync(user);
}
if (User.HasClaim(OpenIdConnectConstants.Claims.Scope, OpenIdConnectConstants.Scopes.Phone)) {
if (User.HasClaim(OpenIdConnectConstants.Claims.Scope, OpenIdConnectConstants.Scopes.Phone))
{
claims[OpenIdConnectConstants.Claims.PhoneNumber] = await _userManager.GetPhoneNumberAsync(user);
claims[OpenIdConnectConstants.Claims.PhoneNumberVerified] = await _userManager.IsPhoneNumberConfirmedAsync(user);
}
if (User.HasClaim(OpenIdConnectConstants.Claims.Scope, OpenIddictConstants.Scopes.Roles)) {
if (User.HasClaim(OpenIdConnectConstants.Claims.Scope, OpenIddictConstants.Scopes.Roles))
{
claims[OpenIddictConstants.Claims.Roles] = JArray.FromObject(await _userManager.GetRolesAsync(user));
}

27
samples/Mvc.Server/Extensions/AppBuilderExtensions.cs

@ -3,32 +3,41 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
namespace Mvc.Server.Extensions {
public static class AppBuilderExtensions {
namespace Mvc.Server.Extensions
{
public static class AppBuilderExtensions
{
public static IApplicationBuilder UseWhen(this IApplicationBuilder app,
Func<HttpContext, bool> condition, Action<IApplicationBuilder> configuration) {
if (app == null) {
Func<HttpContext, bool> condition, Action<IApplicationBuilder> configuration)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
if (condition == null) {
if (condition == null)
{
throw new ArgumentNullException(nameof(condition));
}
if (configuration == null) {
if (configuration == null)
{
throw new ArgumentNullException(nameof(configuration));
}
var builder = app.New();
configuration(builder);
return app.Use(next => {
return app.Use(next =>
{
builder.Run(next);
var branch = builder.Build();
return context => {
if (condition(context)) {
return context =>
{
if (condition(context))
{
return branch(context);
}

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

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

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

@ -1,12 +1,15 @@
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace Mvc.Server.Models {
public class ApplicationDbContext : IdentityDbContext<ApplicationUser> {
namespace Mvc.Server.Models
{
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions options)
: base(options) { }
protected override void OnModelCreating(ModelBuilder builder) {
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.

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

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

9
samples/Mvc.Server/Program.cs

@ -2,9 +2,12 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Mvc.Server {
public static class Program {
public static void Main(string[] args) {
namespace Mvc.Server
{
public static class Program
{
public static void Main(string[] args)
{
var configuration = new ConfigurationBuilder()
.AddEnvironmentVariables()
.AddCommandLine(args)

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

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

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

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

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

@ -1,16 +1,20 @@
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 {
public Task SendEmailAsync(string email, string subject, string message) {
public class AuthMessageSender : IEmailSender, ISmsSender
{
public Task SendEmailAsync(string email, string subject, string message)
{
// Plug in your email service here to send an email.
return Task.FromResult(0);
}
public Task SendSmsAsync(string number, string message) {
public Task SendSmsAsync(string number, string message)
{
// Plug in your SMS service here to send a text message.
return Task.FromResult(0);
}

50
samples/Mvc.Server/Startup.cs

@ -12,9 +12,12 @@ using Mvc.Server.Services;
using OpenIddict.Core;
using OpenIddict.Models;
namespace Mvc.Server {
public class Startup {
public void ConfigureServices(IServiceCollection services) {
namespace Mvc.Server
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
var configuration = new ConfigurationBuilder()
.AddJsonFile("config.json")
.AddEnvironmentVariables()
@ -22,7 +25,8 @@ namespace Mvc.Server {
services.AddMvc();
services.AddDbContext<ApplicationDbContext>(options => {
services.AddDbContext<ApplicationDbContext>(options =>
{
// Configure the context to use Microsoft SQL Server.
options.UseSqlServer(configuration["Data:DefaultConnection:ConnectionString"]);
@ -65,6 +69,8 @@ namespace Mvc.Server {
// During development, you can disable the HTTPS requirement.
.DisableHttpsRequirement()
.SetAccessTokenLifetime(TimeSpan.FromSeconds(15))
// When request caching is enabled, authorization and logout requests
// are stored in the distributed cache by OpenIddict and the user agent
// is redirected to the same page with a single parameter (request_id).
@ -76,12 +82,14 @@ namespace Mvc.Server {
services.AddTransient<ISmsSender, AuthMessageSender>();
}
public void Configure(IApplicationBuilder app) {
public void Configure(IApplicationBuilder app)
{
app.UseDeveloperExceptionPage();
app.UseStaticFiles();
app.UseWhen(context => context.Request.Path.StartsWithSegments("/api"), branch => {
app.UseWhen(context => context.Request.Path.StartsWithSegments("/api"), branch =>
{
// Add a middleware used to validate access
// tokens and protect the API endpoints.
branch.UseOAuthValidation();
@ -90,7 +98,8 @@ namespace Mvc.Server {
// Using it is recommended if your resource server is in a
// different application/separated from the authorization server.
//
// branch.UseOAuthIntrospection(options => {
// branch.UseOAuthIntrospection(options =>
// {
// options.AutomaticAuthenticate = true;
// options.AutomaticChallenge = true;
// options.Authority = "http://localhost:54540/";
@ -100,17 +109,20 @@ namespace Mvc.Server {
// });
});
app.UseWhen(context => !context.Request.Path.StartsWithSegments("/api"), branch => {
app.UseWhen(context => !context.Request.Path.StartsWithSegments("/api"), branch =>
{
branch.UseStatusCodePagesWithReExecute("/error");
branch.UseIdentity();
branch.UseGoogleAuthentication(new GoogleOptions {
branch.UseGoogleAuthentication(new GoogleOptions
{
ClientId = "560027070069-37ldt4kfuohhu3m495hk2j4pjp92d382.apps.googleusercontent.com",
ClientSecret = "n2Q-GEw9RQjzcRbU3qhfTj8f"
});
branch.UseTwitterAuthentication(new TwitterOptions {
branch.UseTwitterAuthentication(new TwitterOptions
{
ConsumerKey = "6XaCTaLbMqfj6ww3zvZ5g",
ConsumerSecret = "Il2eFzGIrYhz6BWjYhVXBPQSfZuS4xoHpSSyD9PI"
});
@ -125,16 +137,20 @@ namespace Mvc.Server {
InitializeAsync(app.ApplicationServices, CancellationToken.None).GetAwaiter().GetResult();
}
private async Task InitializeAsync(IServiceProvider services, CancellationToken cancellationToken) {
private async Task InitializeAsync(IServiceProvider services, CancellationToken cancellationToken)
{
// Create a new service scope to ensure the database context is correctly disposed when this methods returns.
using (var scope = services.GetRequiredService<IServiceScopeFactory>().CreateScope()) {
using (var scope = services.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
await context.Database.EnsureCreatedAsync();
var manager = scope.ServiceProvider.GetRequiredService<OpenIddictApplicationManager<OpenIddictApplication>>();
if (await manager.FindByClientIdAsync("mvc", cancellationToken) == null) {
var application = new OpenIddictApplication {
if (await manager.FindByClientIdAsync("mvc", cancellationToken) == null)
{
var application = new OpenIddictApplication
{
ClientId = "mvc",
DisplayName = "MVC client application",
LogoutRedirectUri = "http://localhost:53507/",
@ -153,8 +169,10 @@ namespace Mvc.Server {
// * Scope: openid email profile roles
// * Grant type: authorization code
// * Request access token locally: yes
if (await manager.FindByClientIdAsync("postman", cancellationToken) == null) {
var application = new OpenIddictApplication {
if (await manager.FindByClientIdAsync("postman", cancellationToken) == null)
{
var application = new OpenIddictApplication
{
ClientId = "postman",
DisplayName = "Postman",
RedirectUri = "https://www.getpostman.com/oauth2/callback"

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

@ -1,6 +1,7 @@
using System.ComponentModel.DataAnnotations;
namespace Mvc.Server.ViewModels.Account {
namespace Mvc.Server.ViewModels.Account
{
public class ExternalLoginConfirmationViewModel
{
[Required]

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

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

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

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

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

@ -1,7 +1,9 @@
using System.ComponentModel.DataAnnotations;
namespace Mvc.Server.ViewModels.Account {
public class RegisterViewModel {
namespace Mvc.Server.ViewModels.Account
{
public class RegisterViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]

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

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

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

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

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

@ -1,7 +1,9 @@
using System.ComponentModel.DataAnnotations;
namespace Mvc.Server.ViewModels.Account {
public class VerifyCodeViewModel {
namespace Mvc.Server.ViewModels.Account
{
public class VerifyCodeViewModel
{
[Required]
public string Provider { get; set; }

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

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

6
samples/Mvc.Server/ViewModels/Authorization/LogoutViewModel.cs

@ -1,7 +1,9 @@
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Mvc.Server.ViewModels.Authorization {
public class LogoutViewModel {
namespace Mvc.Server.ViewModels.Authorization
{
public class LogoutViewModel
{
[BindNever]
public string RequestId { get; set; }
}

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

@ -1,7 +1,9 @@
using System.ComponentModel.DataAnnotations;
namespace Mvc.Server.ViewModels.Manage {
public class AddPhoneNumberViewModel {
namespace Mvc.Server.ViewModels.Manage
{
public class AddPhoneNumberViewModel
{
[Required]
[Phone]
[Display(Name = "Phone number")]

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

@ -1,7 +1,9 @@
using System.ComponentModel.DataAnnotations;
namespace Mvc.Server.ViewModels.Manage {
public class ChangePasswordViewModel {
namespace Mvc.Server.ViewModels.Manage
{
public class ChangePasswordViewModel
{
[Required]
[DataType(DataType.Password)]
[Display(Name = "Current password")]

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

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

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

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

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

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

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

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

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

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

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

@ -1,7 +1,9 @@
using System.ComponentModel.DataAnnotations;
namespace Mvc.Server.ViewModels.Manage {
public class SetPasswordViewModel {
namespace Mvc.Server.ViewModels.Manage
{
public class SetPasswordViewModel
{
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]

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

@ -1,7 +1,9 @@
using System.ComponentModel.DataAnnotations;
namespace Mvc.Server.ViewModels.Manage {
public class VerifyPhoneNumberViewModel {
namespace Mvc.Server.ViewModels.Manage
{
public class VerifyPhoneNumberViewModel
{
[Required]
public string Code { get; set; }

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

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

4
samples/Mvc.Server/web.config

@ -2,8 +2,8 @@
<configuration>
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/>
</handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/>
</system.webServer>
</configuration>

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

@ -12,15 +12,18 @@ using CryptoHelper;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
namespace OpenIddict.Core {
namespace OpenIddict.Core
{
/// <summary>
/// Provides methods allowing to manage the applications stored in the store.
/// </summary>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
public class OpenIddictApplicationManager<TApplication> where TApplication : class {
public class OpenIddictApplicationManager<TApplication> where TApplication : class
{
public OpenIddictApplicationManager(
[NotNull] IOpenIddictApplicationStore<TApplication> store,
[NotNull] ILogger<OpenIddictApplicationManager<TApplication>> logger) {
[NotNull] ILogger<OpenIddictApplicationManager<TApplication>> logger)
{
Store = store;
Logger = logger;
}
@ -44,18 +47,22 @@ namespace OpenIddict.Core {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the application.
/// </returns>
public virtual async Task<TApplication> CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
if (application == null) {
public virtual async Task<TApplication> CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
if (!string.IsNullOrEmpty(await Store.GetHashedSecretAsync(application, cancellationToken))) {
if (!string.IsNullOrEmpty(await Store.GetHashedSecretAsync(application, cancellationToken)))
{
throw new ArgumentException("The client secret hash cannot be directly set on the application entity. " +
"To create a confidential application, use the CreateAsync() overload accepting a secret parameter.");
}
// If no client type was specified, assume it's a public application.
if (string.IsNullOrEmpty(await Store.GetClientTypeAsync(application, cancellationToken))) {
if (string.IsNullOrEmpty(await Store.GetClientTypeAsync(application, cancellationToken)))
{
await Store.SetClientTypeAsync(application, OpenIddictConstants.ClientTypes.Public, cancellationToken);
}
@ -75,18 +82,22 @@ namespace OpenIddict.Core {
/// </returns>
public virtual async Task<TApplication> CreateAsync(
[NotNull] TApplication application,
[NotNull] string secret, CancellationToken cancellationToken) {
if (application == null) {
[NotNull] string secret, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
if (!string.IsNullOrEmpty(await Store.GetHashedSecretAsync(application, cancellationToken))) {
if (!string.IsNullOrEmpty(await Store.GetHashedSecretAsync(application, cancellationToken)))
{
throw new ArgumentException("The client secret hash cannot be directly set on the application entity.");
}
var type = await Store.GetClientTypeAsync(application, cancellationToken);
if (!string.IsNullOrEmpty(type) &&
!string.Equals(type, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase)) {
!string.Equals(type, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException("The client type must be set to 'confidential' when creating an application with a client secret." +
"To create a public application, use the CreateAsync() overload that doesn't take a secret parameter.");
}
@ -106,8 +117,10 @@ namespace OpenIddict.Core {
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
public virtual Task DeleteAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
if (application == null) {
public virtual Task DeleteAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
@ -123,7 +136,8 @@ namespace OpenIddict.Core {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client application corresponding to the identifier.
/// </returns>
public virtual Task<TApplication> FindByIdAsync(string identifier, CancellationToken cancellationToken) {
public virtual Task<TApplication> FindByIdAsync(string identifier, CancellationToken cancellationToken)
{
return Store.FindByIdAsync(identifier, cancellationToken);
}
@ -136,7 +150,8 @@ namespace OpenIddict.Core {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client application corresponding to the identifier.
/// </returns>
public virtual Task<TApplication> FindByClientIdAsync(string identifier, CancellationToken cancellationToken) {
public virtual Task<TApplication> FindByClientIdAsync(string identifier, CancellationToken cancellationToken)
{
return Store.FindByClientIdAsync(identifier, cancellationToken);
}
@ -149,7 +164,8 @@ namespace OpenIddict.Core {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation, whose result
/// returns the client application corresponding to the post_logout_redirect_uri.
/// </returns>
public virtual Task<TApplication> FindByLogoutRedirectUri(string url, CancellationToken cancellationToken) {
public virtual Task<TApplication> FindByLogoutRedirectUri(string url, CancellationToken cancellationToken)
{
return Store.FindByLogoutRedirectUri(url, cancellationToken);
}
@ -162,8 +178,10 @@ namespace OpenIddict.Core {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client type of the application (by default, "public").
/// </returns>
public virtual async Task<string> GetClientTypeAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
if (application == null) {
public virtual async Task<string> GetClientTypeAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
@ -171,7 +189,8 @@ namespace OpenIddict.Core {
// Ensure the application type returned by the store is supported by the manager.
if (!string.Equals(type, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase) &&
!string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) {
!string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException("Only 'confidential' or 'public' applications are " +
"supported by the default application manager.");
}
@ -188,8 +207,10 @@ namespace OpenIddict.Core {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the display name associated with the application.
/// </returns>
public virtual Task<string> GetDisplayNameAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
if (application == null) {
public virtual Task<string> GetDisplayNameAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
@ -205,8 +226,10 @@ namespace OpenIddict.Core {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the application.
/// </returns>
public virtual Task<string> GetIdAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
if (application == null) {
public virtual Task<string> GetIdAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
@ -222,8 +245,10 @@ namespace OpenIddict.Core {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the tokens associated with the application.
/// </returns>
public virtual Task<IEnumerable<string>> GetTokensAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
if (application == null) {
public virtual Task<IEnumerable<string>> GetTokensAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
@ -239,8 +264,10 @@ namespace OpenIddict.Core {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns a boolean indicating whether a redirect_uri is registered.
/// </returns>
public virtual async Task<bool> HasRedirectUriAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
if (application == null) {
public virtual async Task<bool> HasRedirectUriAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
@ -256,8 +283,10 @@ namespace OpenIddict.Core {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns a boolean indicating whether a client secret is registered.
/// </returns>
public virtual async Task<bool> HasClientSecretAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
if (application == null) {
public virtual async Task<bool> HasClientSecretAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
@ -270,13 +299,16 @@ namespace OpenIddict.Core {
/// <param name="application">The application.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the application is a confidential client, <c>false</c> otherwise.</returns>
public async Task<bool> IsConfidentialAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
if (application == null) {
public async Task<bool> IsConfidentialAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
var type = await GetClientTypeAsync(application, cancellationToken);
if (string.IsNullOrEmpty(type)) {
if (string.IsNullOrEmpty(type))
{
return false;
}
@ -289,14 +321,17 @@ namespace OpenIddict.Core {
/// <param name="application">The application.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the application is a public client, <c>false</c> otherwise.</returns>
public async Task<bool> IsPublicAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
if (application == null) {
public async Task<bool> IsPublicAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
// Assume client applications are public if their type is not explicitly set.
var type = await GetClientTypeAsync(application, cancellationToken);
if (string.IsNullOrEmpty(type)) {
if (string.IsNullOrEmpty(type))
{
return true;
}
@ -312,12 +347,15 @@ namespace OpenIddict.Core {
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
public virtual async Task SetClientSecretAsync([NotNull] TApplication application, [NotNull] string secret, CancellationToken cancellationToken) {
if (application == null) {
public virtual async Task SetClientSecretAsync([NotNull] TApplication application, [NotNull] string secret, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
if (string.IsNullOrEmpty(secret)) {
if (string.IsNullOrEmpty(secret))
{
throw new ArgumentException("The client secret cannot be null or empty.", nameof(secret));
}
@ -333,8 +371,10 @@ namespace OpenIddict.Core {
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
public virtual async Task UpdateAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
if (application == null) {
public virtual async Task UpdateAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
@ -350,23 +390,28 @@ namespace OpenIddict.Core {
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
protected virtual async Task ValidateAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
if (application == null) {
protected virtual async Task ValidateAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
if (string.IsNullOrEmpty(await Store.GetClientIdAsync(application, cancellationToken))) {
if (string.IsNullOrEmpty(await Store.GetClientIdAsync(application, cancellationToken)))
{
throw new ArgumentException("The client identifier cannot be null or empty.", nameof(application));
}
var type = await Store.GetClientTypeAsync(application, cancellationToken);
if (string.IsNullOrEmpty(type)) {
if (string.IsNullOrEmpty(type))
{
throw new ArgumentException("The client type cannot be null or empty.", nameof(application));
}
// Ensure the application type is supported by the manager.
if (!string.Equals(type, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase) &&
!string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) {
!string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase))
{
throw new ArgumentException("Only 'confidential' or 'public' applications are " +
"supported by the default application manager.", nameof(application));
}
@ -375,43 +420,51 @@ namespace OpenIddict.Core {
// Ensure a client secret was specified if the client is a confidential application.
if (string.IsNullOrEmpty(hash) &&
string.Equals(type, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase)) {
string.Equals(type, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase))
{
throw new ArgumentException("The client secret cannot be null or empty for a confidential application.", nameof(application));
}
// Ensure no client secret was specified if the client is a public application.
else if (!string.IsNullOrEmpty(hash) &&
string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) {
string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase))
{
throw new ArgumentException("A client secret cannot be associated with a public application.", nameof(application));
}
// When a redirect_uri is specified, ensure it is valid and spec-compliant.
// See https://tools.ietf.org/html/rfc6749#section-3.1 for more information.
var address = await Store.GetRedirectUriAsync(application, cancellationToken);
if (!string.IsNullOrEmpty(address)) {
if (!string.IsNullOrEmpty(address))
{
Uri uri;
// Ensure the redirect_uri is a valid and absolute URL.
if (!Uri.TryCreate(address, UriKind.Absolute, out uri)) {
if (!Uri.TryCreate(address, UriKind.Absolute, out uri))
{
throw new ArgumentException("The redirect_uri must be an absolute URL.");
}
// Ensure the redirect_uri doesn't contain a fragment.
if (!string.IsNullOrEmpty(uri.Fragment)) {
if (!string.IsNullOrEmpty(uri.Fragment))
{
throw new ArgumentException("The redirect_uri cannot contain a fragment.");
}
}
// When a post_logout_redirect_uri is specified, ensure it is valid.
address = await Store.GetLogoutRedirectUriAsync(application, cancellationToken);
if (!string.IsNullOrEmpty(address)) {
if (!string.IsNullOrEmpty(address))
{
Uri uri;
// Ensure the post_logout_redirect_uri is a valid and absolute URL.
if (!Uri.TryCreate(address, UriKind.Absolute, out uri)) {
if (!Uri.TryCreate(address, UriKind.Absolute, out uri))
{
throw new ArgumentException("The post_logout_redirect_uri must be an absolute URL.");
}
// Ensure the post_logout_redirect_uri doesn't contain a fragment.
if (!string.IsNullOrEmpty(uri.Fragment)) {
if (!string.IsNullOrEmpty(uri.Fragment))
{
throw new ArgumentException("The post_logout_redirect_uri cannot contain a fragment.");
}
}
@ -427,12 +480,15 @@ namespace OpenIddict.Core {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns a boolean indicating whether the redirect_uri was valid.
/// </returns>
public virtual async Task<bool> ValidateRedirectUriAsync([NotNull] TApplication application, string address, CancellationToken cancellationToken) {
if (application == null) {
public virtual async Task<bool> ValidateRedirectUriAsync([NotNull] TApplication application, string address, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
if (!string.Equals(address, await Store.GetRedirectUriAsync(application, cancellationToken), StringComparison.Ordinal)) {
if (!string.Equals(address, await Store.GetRedirectUriAsync(application, cancellationToken), StringComparison.Ordinal))
{
Logger.LogWarning("Client validation failed because {RedirectUri} was not a valid redirect_uri " +
"for {Client}", address, await GetDisplayNameAsync(application, cancellationToken));
@ -453,26 +509,31 @@ namespace OpenIddict.Core {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns a boolean indicating whether the client secret was valid.
/// </returns>
public virtual async Task<bool> ValidateClientSecretAsync([NotNull] TApplication application, string secret, CancellationToken cancellationToken) {
if (application == null) {
public virtual async Task<bool> ValidateClientSecretAsync([NotNull] TApplication application, string secret, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
if (!await IsConfidentialAsync(application, cancellationToken)) {
if (!await IsConfidentialAsync(application, cancellationToken))
{
Logger.LogWarning("Client authentication cannot be enforced for non-confidential applications.");
return false;
}
var hash = await Store.GetHashedSecretAsync(application, cancellationToken);
if (string.IsNullOrEmpty(hash)) {
if (string.IsNullOrEmpty(hash))
{
Logger.LogError("Client authentication failed for {Client} because " +
"no client secret was associated with the application.");
return false;
}
if (!Crypto.VerifyHashedPassword(hash, secret)) {
if (!Crypto.VerifyHashedPassword(hash, secret))
{
Logger.LogWarning("Client authentication failed for {Client}.",
await GetDisplayNameAsync(application, cancellationToken));

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

@ -10,15 +10,18 @@ using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
namespace OpenIddict.Core {
namespace OpenIddict.Core
{
/// <summary>
/// Provides methods allowing to manage the authorizations stored in the store.
/// </summary>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
public class OpenIddictAuthorizationManager<TAuthorization> where TAuthorization : class {
public class OpenIddictAuthorizationManager<TAuthorization> where TAuthorization : class
{
public OpenIddictAuthorizationManager(
[NotNull] IOpenIddictAuthorizationStore<TAuthorization> store,
[NotNull] ILogger<OpenIddictAuthorizationManager<TAuthorization>> logger) {
[NotNull] ILogger<OpenIddictAuthorizationManager<TAuthorization>> logger)
{
Logger = logger;
Store = store;
}
@ -41,8 +44,10 @@ namespace OpenIddict.Core {
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
public virtual Task CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) {
if (authorization == null) {
public virtual Task CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
@ -59,7 +64,8 @@ namespace OpenIddict.Core {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the authorization corresponding to the subject/client.
/// </returns>
public virtual Task<TAuthorization> FindAsync(string subject, string client, CancellationToken cancellationToken) {
public virtual Task<TAuthorization> FindAsync(string subject, string client, CancellationToken cancellationToken)
{
return Store.FindAsync(subject, client, cancellationToken);
}
@ -72,7 +78,8 @@ namespace OpenIddict.Core {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the authorization corresponding to the identifier.
/// </returns>
public virtual Task<TAuthorization> FindByIdAsync(string identifier, CancellationToken cancellationToken) {
public virtual Task<TAuthorization> FindByIdAsync(string identifier, CancellationToken cancellationToken)
{
return Store.FindByIdAsync(identifier, cancellationToken);
}
@ -85,8 +92,10 @@ namespace OpenIddict.Core {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the authorization.
/// </returns>
public virtual Task<string> GetIdAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) {
if (authorization == null) {
public virtual Task<string> GetIdAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
@ -101,12 +110,15 @@ namespace OpenIddict.Core {
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
protected virtual async Task ValidateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) {
if (authorization == null) {
protected virtual async Task ValidateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
if (string.IsNullOrEmpty(await Store.GetSubjectAsync(authorization, cancellationToken))) {
if (string.IsNullOrEmpty(await Store.GetSubjectAsync(authorization, cancellationToken)))
{
throw new ArgumentException("The subject cannot be null or empty.");
}
}

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

@ -7,15 +7,18 @@
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
namespace OpenIddict.Core {
namespace OpenIddict.Core
{
/// <summary>
/// Provides methods allowing to manage the scopes stored in the store.
/// </summary>
/// <typeparam name="TScope">The type of the Scope entity.</typeparam>
public class OpenIddictScopeManager<TScope> where TScope : class {
public class OpenIddictScopeManager<TScope> where TScope : class
{
public OpenIddictScopeManager(
[NotNull] IOpenIddictScopeStore<TScope> store,
[NotNull] ILogger<OpenIddictScopeManager<TScope>> logger) {
[NotNull] ILogger<OpenIddictScopeManager<TScope>> logger)
{
Logger = logger;
Store = store;
}

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

@ -11,15 +11,18 @@ using AspNet.Security.OpenIdConnect.Primitives;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
namespace OpenIddict.Core {
namespace OpenIddict.Core
{
/// <summary>
/// Provides methods allowing to manage the tokens stored in the store.
/// </summary>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
public class OpenIddictTokenManager<TToken> where TToken : class {
public class OpenIddictTokenManager<TToken> where TToken : class
{
public OpenIddictTokenManager(
[NotNull] IOpenIddictTokenStore<TToken> store,
[NotNull] ILogger<OpenIddictTokenManager<TToken>> logger) {
[NotNull] ILogger<OpenIddictTokenManager<TToken>> logger)
{
Logger = logger;
Store = store;
}
@ -42,8 +45,10 @@ namespace OpenIddict.Core {
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation, whose result returns the token.
/// </returns>
public virtual async Task<TToken> CreateAsync([NotNull] TToken token, CancellationToken cancellationToken) {
if (token == null) {
public virtual async Task<TToken> CreateAsync([NotNull] TToken token, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
@ -60,17 +65,21 @@ namespace OpenIddict.Core {
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation, whose result returns the token.
/// </returns>
public virtual async Task<TToken> CreateAsync([NotNull] string type, [NotNull] string subject, CancellationToken cancellationToken) {
if (string.IsNullOrEmpty(type)) {
public virtual async Task<TToken> CreateAsync([NotNull] string type, [NotNull] string subject, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(type))
{
throw new ArgumentException("The token type cannot be null or empty.", nameof(type));
}
if (!string.Equals(type, OpenIdConnectConstants.TokenTypeHints.AuthorizationCode, StringComparison.OrdinalIgnoreCase) &&
!string.Equals(type, OpenIdConnectConstants.TokenTypeHints.RefreshToken, StringComparison.OrdinalIgnoreCase)) {
!string.Equals(type, OpenIdConnectConstants.TokenTypeHints.RefreshToken, StringComparison.OrdinalIgnoreCase))
{
throw new ArgumentException("The specified token type is not supported by the default token manager.");
}
if (string.IsNullOrEmpty(subject)) {
if (string.IsNullOrEmpty(subject))
{
throw new ArgumentException("The subject cannot be null or empty.");
}
@ -86,7 +95,8 @@ namespace OpenIddict.Core {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the token corresponding to the unique identifier.
/// </returns>
public virtual Task<TToken> FindByIdAsync(string identifier, CancellationToken cancellationToken) {
public virtual Task<TToken> FindByIdAsync(string identifier, CancellationToken cancellationToken)
{
return Store.FindByIdAsync(identifier, cancellationToken);
}
@ -99,7 +109,8 @@ namespace OpenIddict.Core {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the tokens corresponding to the specified subject.
/// </returns>
public virtual Task<TToken[]> FindBySubjectAsync(string subject, CancellationToken cancellationToken) {
public virtual Task<TToken[]> FindBySubjectAsync(string subject, CancellationToken cancellationToken)
{
return Store.FindBySubjectAsync(subject, cancellationToken);
}
@ -112,8 +123,10 @@ namespace OpenIddict.Core {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the token.
/// </returns>
public virtual Task<string> GetIdAsync([NotNull] TToken token, CancellationToken cancellationToken) {
if (token == null) {
public virtual Task<string> GetIdAsync([NotNull] TToken token, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
@ -126,8 +139,10 @@ namespace OpenIddict.Core {
/// <param name="token">The token to revoke.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public virtual Task RevokeAsync([NotNull] TToken token, CancellationToken cancellationToken) {
if (token == null) {
public virtual Task RevokeAsync([NotNull] TToken token, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
@ -143,8 +158,10 @@ namespace OpenIddict.Core {
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
public virtual async Task SetAuthorizationAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) {
if (token == null) {
public virtual async Task SetAuthorizationAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
@ -161,8 +178,10 @@ namespace OpenIddict.Core {
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
public virtual async Task SetClientAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) {
if (token == null) {
public virtual async Task SetClientAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
@ -178,8 +197,10 @@ namespace OpenIddict.Core {
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
public virtual Task UpdateAsync([NotNull] TToken token, CancellationToken cancellationToken) {
if (token == null) {
public virtual Task UpdateAsync([NotNull] TToken token, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
@ -194,22 +215,27 @@ namespace OpenIddict.Core {
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
protected virtual async Task ValidateAsync([NotNull] TToken token, CancellationToken cancellationToken) {
if (token == null) {
protected virtual async Task ValidateAsync([NotNull] TToken token, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
var type = await Store.GetTokenTypeAsync(token, cancellationToken);
if (string.IsNullOrEmpty(type)) {
if (string.IsNullOrEmpty(type))
{
throw new ArgumentException("The token type cannot be null or empty.", nameof(token));
}
if (!string.Equals(type, OpenIdConnectConstants.TokenTypeHints.AuthorizationCode, StringComparison.OrdinalIgnoreCase) &&
!string.Equals(type, OpenIdConnectConstants.TokenTypeHints.RefreshToken, StringComparison.OrdinalIgnoreCase)) {
!string.Equals(type, OpenIdConnectConstants.TokenTypeHints.RefreshToken, StringComparison.OrdinalIgnoreCase))
{
throw new ArgumentException("The specified token type is not supported by the default token manager.");
}
if (string.IsNullOrEmpty(await Store.GetSubjectAsync(token, cancellationToken))) {
if (string.IsNullOrEmpty(await Store.GetSubjectAsync(token, cancellationToken)))
{
throw new ArgumentException("The subject cannot be null or empty.");
}
}

84
src/OpenIddict.Core/OpenIddictBuilder.cs

@ -14,17 +14,21 @@ using OpenIddict.Models;
using System.Reflection;
#endif
namespace Microsoft.Extensions.DependencyInjection {
namespace Microsoft.Extensions.DependencyInjection
{
/// <summary>
/// Exposes the necessary methods required to configure OpenIddict.
/// </summary>
public class OpenIddictBuilder {
public class OpenIddictBuilder
{
/// <summary>
/// Initializes a new instance of <see cref="OpenIddictBuilder"/>.
/// </summary>
/// <param name="services">The services collection.</param>
public OpenIddictBuilder([NotNull] IServiceCollection services) {
if (services == null) {
public OpenIddictBuilder([NotNull] IServiceCollection services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
@ -74,13 +78,16 @@ namespace Microsoft.Extensions.DependencyInjection {
/// </summary>
/// <param name="type">The type of the custom manager.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AddApplicationManager([NotNull] Type type) {
if (type == null) {
public virtual OpenIddictBuilder AddApplicationManager([NotNull] Type type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
var contract = typeof(OpenIddictApplicationManager<>).MakeGenericType(ApplicationType);
if (!contract.IsAssignableFrom(type)) {
if (!contract.IsAssignableFrom(type))
{
throw new InvalidOperationException("Custom managers must be derived from OpenIddictApplicationManager.");
}
@ -102,13 +109,16 @@ namespace Microsoft.Extensions.DependencyInjection {
/// </summary>
/// <param name="type">The type of the custom store.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AddApplicationStore([NotNull] Type type) {
if (type == null) {
public virtual OpenIddictBuilder AddApplicationStore([NotNull] Type type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
var contract = typeof(IOpenIddictApplicationStore<>).MakeGenericType(ApplicationType);
if (!contract.IsAssignableFrom(type)) {
if (!contract.IsAssignableFrom(type))
{
throw new InvalidOperationException("Custom stores must implement IOpenIddictApplicationStore.");
}
@ -130,13 +140,16 @@ namespace Microsoft.Extensions.DependencyInjection {
/// </summary>
/// <param name="type">The type of the custom manager.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AddAuthorizationManager([NotNull] Type type) {
if (type == null) {
public virtual OpenIddictBuilder AddAuthorizationManager([NotNull] Type type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
var contract = typeof(OpenIddictAuthorizationManager<>).MakeGenericType(AuthorizationType);
if (!contract.IsAssignableFrom(type)) {
if (!contract.IsAssignableFrom(type))
{
throw new InvalidOperationException("Custom managers must be derived from OpenIddictAuthorizationManager.");
}
@ -158,13 +171,16 @@ namespace Microsoft.Extensions.DependencyInjection {
/// </summary>
/// <param name="type">The type of the custom store.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AddAuthorizationStore([NotNull] Type type) {
if (type == null) {
public virtual OpenIddictBuilder AddAuthorizationStore([NotNull] Type type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
var contract = typeof(IOpenIddictAuthorizationStore<>).MakeGenericType(AuthorizationType);
if (!contract.IsAssignableFrom(type)) {
if (!contract.IsAssignableFrom(type))
{
throw new InvalidOperationException("Custom stores must implement IOpenIddictAuthorizationStore.");
}
@ -186,13 +202,16 @@ namespace Microsoft.Extensions.DependencyInjection {
/// </summary>
/// <param name="type">The type of the custom manager.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AddScopeManager([NotNull] Type type) {
if (type == null) {
public virtual OpenIddictBuilder AddScopeManager([NotNull] Type type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
var contract = typeof(OpenIddictScopeManager<>).MakeGenericType(ScopeType);
if (!contract.IsAssignableFrom(type)) {
if (!contract.IsAssignableFrom(type))
{
throw new InvalidOperationException("Custom managers must be derived from OpenIddictScopeManager.");
}
@ -214,13 +233,16 @@ namespace Microsoft.Extensions.DependencyInjection {
/// </summary>
/// <param name="type">The type of the custom store.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AddScopeStore([NotNull] Type type) {
if (type == null) {
public virtual OpenIddictBuilder AddScopeStore([NotNull] Type type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
var contract = typeof(IOpenIddictScopeStore<>).MakeGenericType(ScopeType);
if (!contract.IsAssignableFrom(type)) {
if (!contract.IsAssignableFrom(type))
{
throw new InvalidOperationException("Custom stores must implement IOpenIddictScopeStore.");
}
@ -242,13 +264,16 @@ namespace Microsoft.Extensions.DependencyInjection {
/// </summary>
/// <param name="type">The type of the custom manager.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AddTokenManager([NotNull] Type type) {
if (type == null) {
public virtual OpenIddictBuilder AddTokenManager([NotNull] Type type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
var contract = typeof(OpenIddictTokenManager<>).MakeGenericType(TokenType);
if (!contract.IsAssignableFrom(type)) {
if (!contract.IsAssignableFrom(type))
{
throw new InvalidOperationException("Custom managers must be derived from OpenIddictTokenManager.");
}
@ -270,13 +295,16 @@ namespace Microsoft.Extensions.DependencyInjection {
/// </summary>
/// <param name="type">The type of the custom store.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AddTokenStore([NotNull] Type type) {
if (type == null) {
public virtual OpenIddictBuilder AddTokenStore([NotNull] Type type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
var contract = typeof(IOpenIddictTokenStore<>).MakeGenericType(TokenType);
if (!contract.IsAssignableFrom(type)) {
if (!contract.IsAssignableFrom(type))
{
throw new InvalidOperationException("Custom stores must implement IOpenIddictTokenStore.");
}

24
src/OpenIddict.Core/OpenIddictConstants.cs

@ -4,31 +4,39 @@
* the license and the contributors participating to this project.
*/
namespace OpenIddict.Core {
public static class OpenIddictConstants {
public static class Claims {
namespace OpenIddict.Core
{
public static class OpenIddictConstants
{
public static class Claims
{
public const string Roles = "roles";
}
public static class ClientTypes {
public static class ClientTypes
{
public const string Confidential = "confidential";
public const string Public = "public";
}
public static class Environment {
public static class Environment
{
public const string AuthorizationRequest = "openiddict-authorization-request:";
public const string LogoutRequest = "openiddict-logout-request:";
}
public static class Metadata {
public static class Metadata
{
public const string ExternalProvidersSupported = "external_providers_supported";
}
public static class Properties {
public static class Properties
{
public const string AuthorizationId = ".authorization_id";
}
public static class Scopes {
public static class Scopes
{
public const string Roles = "roles";
}
}

36
src/OpenIddict.Core/OpenIddictExtensions.cs

@ -10,15 +10,18 @@ using Microsoft.Extensions.DependencyInjection.Extensions;
using OpenIddict.Core;
using OpenIddict.Models;
namespace Microsoft.Extensions.DependencyInjection {
public static class OpenIddictExtensions {
namespace Microsoft.Extensions.DependencyInjection
{
public static class OpenIddictExtensions
{
/// <summary>
/// Registers the default OpenIddict services in the DI container,
/// using the default entities and the default entity key type.
/// </summary>
/// <param name="services">The services collection.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddOpenIddict([NotNull] this IServiceCollection services) {
public static OpenIddictBuilder AddOpenIddict([NotNull] this IServiceCollection services)
{
return services.AddOpenIddict<OpenIddictApplication,
OpenIddictAuthorization,
OpenIddictScope,
@ -33,7 +36,8 @@ namespace Microsoft.Extensions.DependencyInjection {
/// <param name="services">The services collection.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddOpenIddict<TKey>([NotNull] this IServiceCollection services)
where TKey : IEquatable<TKey> {
where TKey : IEquatable<TKey>
{
return services.AddOpenIddict<OpenIddictApplication<TKey>,
OpenIddictAuthorization<TKey>,
OpenIddictScope<TKey>,
@ -53,14 +57,17 @@ namespace Microsoft.Extensions.DependencyInjection {
where TApplication : class
where TAuthorization : class
where TScope : class
where TToken : class {
if (services == null) {
where TToken : class
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
services.AddOptions();
var builder = new OpenIddictBuilder(services) {
var builder = new OpenIddictBuilder(services)
{
ApplicationType = typeof(TApplication),
AuthorizationType = typeof(TAuthorization),
ScopeType = typeof(TScope),
@ -86,7 +93,8 @@ namespace Microsoft.Extensions.DependencyInjection {
/// <returns>The <see cref="IServiceCollection"/>.</returns>
public static IServiceCollection AddOpenIddict(
[NotNull] this IServiceCollection services,
[NotNull] Action<OpenIddictBuilder> configuration) {
[NotNull] Action<OpenIddictBuilder> configuration)
{
return services.AddOpenIddict<OpenIddictApplication,
OpenIddictAuthorization,
OpenIddictScope,
@ -104,7 +112,8 @@ namespace Microsoft.Extensions.DependencyInjection {
public static IServiceCollection AddOpenIddict<TKey>(
[NotNull] this IServiceCollection services,
[NotNull] Action<OpenIddictBuilder> configuration)
where TKey : IEquatable<TKey> {
where TKey : IEquatable<TKey>
{
return services.AddOpenIddict<OpenIddictApplication<TKey>,
OpenIddictAuthorization<TKey>,
OpenIddictScope<TKey>,
@ -127,12 +136,15 @@ namespace Microsoft.Extensions.DependencyInjection {
where TApplication : class
where TAuthorization : class
where TScope : class
where TToken : class {
if (services == null) {
where TToken : class
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
if (configuration == null) {
if (configuration == null)
{
throw new ArgumentNullException(nameof(configuration));
}

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

@ -9,12 +9,14 @@ using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace OpenIddict.Core {
namespace OpenIddict.Core
{
/// <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 {
public interface IOpenIddictApplicationStore<TApplication> where TApplication : class
{
/// <summary>
/// Creates a new application.
/// </summary>

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

@ -8,12 +8,14 @@ using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace OpenIddict.Core {
namespace OpenIddict.Core
{
/// <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 {
public interface IOpenIddictAuthorizationStore<TAuthorization> where TAuthorization : class
{
/// <summary>
/// Creates a new authorization.
/// </summary>

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

@ -4,7 +4,8 @@
* the license and the contributors participating to this project.
*/
namespace OpenIddict.Core {
namespace OpenIddict.Core
{
/// <summary>
/// Provides methods allowing to manage the scopes stored in a database.
/// </summary>

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

@ -8,12 +8,14 @@ using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace OpenIddict.Core {
namespace OpenIddict.Core
{
/// <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 {
public interface IOpenIddictTokenStore<TToken> where TToken : class
{
/// <summary>
/// Creates a new token.
/// </summary>

15
src/OpenIddict.EntityFrameworkCore/OpenIddictCustomizer.cs

@ -11,7 +11,8 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using OpenIddict.Models;
namespace OpenIddict.EntityFrameworkCore {
namespace OpenIddict.EntityFrameworkCore
{
/// <summary>
/// Represents a model customizer able to register the entity sets
/// required by the OpenIddict stack in an Entity Framework context.
@ -21,13 +22,17 @@ namespace OpenIddict.EntityFrameworkCore {
where TAuthorization : OpenIddictAuthorization<TKey, TApplication, TToken>, new()
where TScope : OpenIddictScope<TKey>, new()
where TToken : OpenIddictToken<TKey, TApplication, TAuthorization>, new()
where TKey : IEquatable<TKey> {
public override void Customize([NotNull] ModelBuilder builder, [NotNull] DbContext context) {
if (builder == null) {
where TKey : IEquatable<TKey>
{
public override void Customize([NotNull] ModelBuilder builder, [NotNull] DbContext context)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (context == null) {
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}

12
src/OpenIddict.EntityFrameworkCore/OpenIddictExtension.cs

@ -10,15 +10,19 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using OpenIddict.Models;
namespace OpenIddict.EntityFrameworkCore {
namespace OpenIddict.EntityFrameworkCore
{
public class OpenIddictExtension<TApplication, TAuthorization, TScope, TToken, TKey> : IDbContextOptionsExtension
where TApplication : OpenIddictApplication<TKey, TAuthorization, TToken>, new()
where TAuthorization : OpenIddictAuthorization<TKey, TApplication, TToken>, new()
where TScope : OpenIddictScope<TKey>, new()
where TToken : OpenIddictToken<TKey, TApplication, TAuthorization>, new()
where TKey : IEquatable<TKey> {
public void ApplyServices([NotNull] IServiceCollection services) {
if (services == null) {
where TKey : IEquatable<TKey>
{
public void ApplyServices([NotNull] IServiceCollection services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}

78
src/OpenIddict.EntityFrameworkCore/OpenIddictExtensions.cs

@ -16,8 +16,10 @@ using OpenIddict.Core;
using OpenIddict.EntityFrameworkCore;
using OpenIddict.Models;
namespace Microsoft.Extensions.DependencyInjection {
public static class OpenIddictExtensions {
namespace Microsoft.Extensions.DependencyInjection
{
public static class OpenIddictExtensions
{
/// <summary>
/// Registers the Entity Framework Core stores. Note: when using the Entity Framework Core stores,
/// the entities MUST be derived from the models contained in the OpenIddict.Models package.
@ -25,8 +27,10 @@ namespace Microsoft.Extensions.DependencyInjection {
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddEntityFrameworkCoreStores<TContext>([NotNull] this OpenIddictBuilder builder)
where TContext : DbContext {
if (builder == null) {
where TContext : DbContext
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
@ -36,32 +40,37 @@ namespace Microsoft.Extensions.DependencyInjection {
builder.TokenType != null, "The entity types exposed by OpenIddictBuilder shouldn't be null.");
var application = FindGenericBaseType(builder.ApplicationType, typeof(OpenIddictApplication<,,>));
if (application == null) {
if (application == null)
{
throw new InvalidOperationException("The Entity Framework Core stores can only be used " +
"with the built-in OpenIddictApplication entity.");
}
var authorization = FindGenericBaseType(builder.AuthorizationType, typeof(OpenIddictAuthorization<,,>));
if (authorization == null) {
if (authorization == null)
{
throw new InvalidOperationException("The Entity Framework Core stores can only be used " +
"with the built-in OpenIddictAuthorization entity.");
}
var scope = FindGenericBaseType(builder.ScopeType, typeof(OpenIddictScope<>));
if (scope == null) {
if (scope == null)
{
throw new InvalidOperationException("The Entity Framework Core stores can only be used " +
"with the built-in OpenIddictScope entity.");
}
var token = FindGenericBaseType(builder.TokenType, typeof(OpenIddictToken<,,>));
if (token == null) {
if (token == null)
{
throw new InvalidOperationException("The Entity Framework Core stores can only be used " +
"with the built-in OpenIddictToken entity.");
}
var converter = TypeDescriptor.GetConverter(application.GenericTypeArguments[0]);
if (converter == null || !converter.CanConvertFrom(typeof(string)) ||
!converter.CanConvertTo(typeof(string))) {
!converter.CanConvertTo(typeof(string)))
{
throw new InvalidOperationException("The specified entity key type is not supported.");
}
@ -112,7 +121,8 @@ namespace Microsoft.Extensions.DependencyInjection {
/// </summary>
/// <param name="builder">The builder used to configure the Entity Framework context.</param>
/// <returns>The Entity Framework context builder.</returns>
public static DbContextOptionsBuilder UseOpenIddict([NotNull] this DbContextOptionsBuilder builder) {
public static DbContextOptionsBuilder UseOpenIddict([NotNull] this DbContextOptionsBuilder builder)
{
return builder.UseOpenIddict<OpenIddictApplication,
OpenIddictAuthorization,
OpenIddictScope,
@ -125,7 +135,8 @@ namespace Microsoft.Extensions.DependencyInjection {
/// </summary>
/// <param name="builder">The builder used to configure the Entity Framework context.</param>
/// <returns>The Entity Framework context builder.</returns>
public static DbContextOptionsBuilder UseOpenIddict<TKey>([NotNull] this DbContextOptionsBuilder builder) where TKey : IEquatable<TKey> {
public static DbContextOptionsBuilder UseOpenIddict<TKey>([NotNull] this DbContextOptionsBuilder builder) where TKey : IEquatable<TKey>
{
return builder.UseOpenIddict<OpenIddictApplication<TKey>,
OpenIddictAuthorization<TKey>,
OpenIddictScope<TKey>,
@ -143,8 +154,10 @@ namespace Microsoft.Extensions.DependencyInjection {
where TAuthorization : OpenIddictAuthorization<TKey, TApplication, TToken>, new()
where TScope : OpenIddictScope<TKey>, new()
where TToken : OpenIddictToken<TKey, TApplication, TAuthorization>, new()
where TKey : IEquatable<TKey> {
if (builder == null) {
where TKey : IEquatable<TKey>
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
@ -160,7 +173,8 @@ namespace Microsoft.Extensions.DependencyInjection {
/// </summary>
/// <param name="builder">The builder used to configure the Entity Framework context.</param>
/// <returns>The Entity Framework context builder.</returns>
public static ModelBuilder UseOpenIddict([NotNull] this ModelBuilder builder) {
public static ModelBuilder UseOpenIddict([NotNull] this ModelBuilder builder)
{
return builder.UseOpenIddict<OpenIddictApplication,
OpenIddictAuthorization,
OpenIddictScope,
@ -173,7 +187,8 @@ namespace Microsoft.Extensions.DependencyInjection {
/// </summary>
/// <param name="builder">The builder used to configure the Entity Framework context.</param>
/// <returns>The Entity Framework context builder.</returns>
public static ModelBuilder UseOpenIddict<TKey>([NotNull] this ModelBuilder builder) where TKey : IEquatable<TKey> {
public static ModelBuilder UseOpenIddict<TKey>([NotNull] this ModelBuilder builder) where TKey : IEquatable<TKey>
{
return builder.UseOpenIddict<OpenIddictApplication<TKey>,
OpenIddictAuthorization<TKey>,
OpenIddictScope<TKey>,
@ -191,8 +206,10 @@ namespace Microsoft.Extensions.DependencyInjection {
where TAuthorization : OpenIddictAuthorization<TKey, TApplication, TToken>, new()
where TScope : OpenIddictScope<TKey>, new()
where TToken : OpenIddictToken<TKey, TApplication, TAuthorization>, new()
where TKey : IEquatable<TKey> {
if (builder == null) {
where TKey : IEquatable<TKey>
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
@ -201,7 +218,8 @@ namespace Microsoft.Extensions.DependencyInjection {
// being non-nullable when using value types like short, int, long or Guid.
// Configure the TApplication entity.
builder.Entity<TApplication>(entity => {
builder.Entity<TApplication>(entity =>
{
entity.HasKey(application => application.Id);
entity.HasIndex(application => application.ClientId)
@ -221,7 +239,8 @@ namespace Microsoft.Extensions.DependencyInjection {
});
// Configure the TAuthorization entity.
builder.Entity<TAuthorization>(entity => {
builder.Entity<TAuthorization>(entity =>
{
entity.HasKey(authorization => authorization.Id);
entity.HasMany(application => application.Tokens)
@ -233,14 +252,16 @@ namespace Microsoft.Extensions.DependencyInjection {
});
// Configure the TScope entity.
builder.Entity<TScope>(entity => {
builder.Entity<TScope>(entity =>
{
entity.HasKey(scope => scope.Id);
entity.ToTable("OpenIddictScopes");
});
// Configure the TToken entity.
builder.Entity<TToken>(entity => {
builder.Entity<TToken>(entity =>
{
entity.HasKey(token => token.Id);
entity.ToTable("OpenIddictTokens");
@ -249,17 +270,22 @@ namespace Microsoft.Extensions.DependencyInjection {
return builder;
}
private static TypeInfo FindGenericBaseType(Type type, Type definition) {
if (type == null) {
private static TypeInfo FindGenericBaseType(Type type, Type definition)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
if (definition == null) {
if (definition == null)
{
throw new ArgumentNullException(nameof(definition));
}
for (var candidate = type.GetTypeInfo(); candidate != null; candidate = candidate.BaseType?.GetTypeInfo()) {
if (candidate.IsGenericType && candidate.GetGenericTypeDefinition() == definition) {
for (var candidate = type.GetTypeInfo(); candidate != null; candidate = candidate.BaseType?.GetTypeInfo())
{
if (candidate.IsGenericType && candidate.GetGenericTypeDefinition() == definition)
{
return candidate;
}
}

132
src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs

@ -15,7 +15,8 @@ using Microsoft.EntityFrameworkCore;
using OpenIddict.Core;
using OpenIddict.Models;
namespace OpenIddict.EntityFrameworkCore {
namespace OpenIddict.EntityFrameworkCore
{
/// <summary>
/// Provides methods allowing to manage the applications stored in a database.
/// </summary>
@ -23,7 +24,8 @@ namespace OpenIddict.EntityFrameworkCore {
public class OpenIddictApplicationStore<TContext> : OpenIddictApplicationStore<OpenIddictApplication,
OpenIddictAuthorization,
OpenIddictToken, TContext, string>
where TContext : DbContext {
where TContext : DbContext
{
public OpenIddictApplicationStore([NotNull] TContext context) : base(context) { }
}
@ -36,7 +38,8 @@ namespace OpenIddict.EntityFrameworkCore {
OpenIddictAuthorization<TKey>,
OpenIddictToken<TKey>, TContext, TKey>
where TContext : DbContext
where TKey : IEquatable<TKey> {
where TKey : IEquatable<TKey>
{
public OpenIddictApplicationStore([NotNull] TContext context) : base(context) { }
}
@ -53,9 +56,12 @@ namespace OpenIddict.EntityFrameworkCore {
where TAuthorization : OpenIddictAuthorization<TKey, TApplication, TToken>, new()
where TToken : OpenIddictToken<TKey, TApplication, TAuthorization>, new()
where TContext : DbContext
where TKey : IEquatable<TKey> {
public OpenIddictApplicationStore([NotNull] TContext context) {
if (context == null) {
where TKey : IEquatable<TKey>
{
public OpenIddictApplicationStore([NotNull] TContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
@ -80,8 +86,10 @@ namespace OpenIddict.EntityFrameworkCore {
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation, whose result returns the application.
/// </returns>
public virtual async Task<TApplication> CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
if (application == null) {
public virtual async Task<TApplication> CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
@ -100,14 +108,17 @@ namespace OpenIddict.EntityFrameworkCore {
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
public virtual async Task DeleteAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
if (application == null) {
public virtual async Task DeleteAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
Context.Remove(application);
try {
try
{
await Context.SaveChangesAsync(cancellationToken);
}
@ -123,7 +134,8 @@ namespace OpenIddict.EntityFrameworkCore {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client application corresponding to the identifier.
/// </returns>
public virtual Task<TApplication> FindByIdAsync(string identifier, CancellationToken cancellationToken) {
public virtual Task<TApplication> FindByIdAsync(string identifier, CancellationToken cancellationToken)
{
var key = ConvertIdentifierFromString(identifier);
return Applications.SingleOrDefaultAsync(application => application.Id.Equals(key), cancellationToken);
@ -138,7 +150,8 @@ namespace OpenIddict.EntityFrameworkCore {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client application corresponding to the identifier.
/// </returns>
public virtual Task<TApplication> FindByClientIdAsync(string identifier, CancellationToken cancellationToken) {
public virtual Task<TApplication> FindByClientIdAsync(string identifier, CancellationToken cancellationToken)
{
return Applications.SingleOrDefaultAsync(application => application.ClientId.Equals(identifier), cancellationToken);
}
@ -151,7 +164,8 @@ namespace OpenIddict.EntityFrameworkCore {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation, whose result
/// returns the client application corresponding to the post_logout_redirect_uri.
/// </returns>
public virtual Task<TApplication> FindByLogoutRedirectUri(string url, CancellationToken cancellationToken) {
public virtual Task<TApplication> FindByLogoutRedirectUri(string url, CancellationToken cancellationToken)
{
return Applications.SingleOrDefaultAsync(application => application.LogoutRedirectUri == url, cancellationToken);
}
@ -164,8 +178,10 @@ namespace OpenIddict.EntityFrameworkCore {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client identifier associated with the application.
/// </returns>
public virtual Task<string> GetClientIdAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
if (application == null) {
public virtual Task<string> GetClientIdAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
@ -181,8 +197,10 @@ namespace OpenIddict.EntityFrameworkCore {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client type of the application (by default, "public").
/// </returns>
public virtual Task<string> GetClientTypeAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
if (application == null) {
public virtual Task<string> GetClientTypeAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
@ -198,8 +216,10 @@ namespace OpenIddict.EntityFrameworkCore {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the display name associated with the application.
/// </returns>
public virtual Task<string> GetDisplayNameAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
if (application == null) {
public virtual Task<string> GetDisplayNameAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
@ -215,8 +235,10 @@ namespace OpenIddict.EntityFrameworkCore {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the hashed secret associated with the application.
/// </returns>
public virtual Task<string> GetHashedSecretAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
if (application == null) {
public virtual Task<string> GetHashedSecretAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
@ -232,8 +254,10 @@ namespace OpenIddict.EntityFrameworkCore {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the application.
/// </returns>
public virtual Task<string> GetIdAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
if (application == null) {
public virtual Task<string> GetIdAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
@ -249,8 +273,10 @@ namespace OpenIddict.EntityFrameworkCore {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the post_logout_redirect_uri associated with the application.
/// </returns>
public virtual Task<string> GetLogoutRedirectUriAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
if (application == null) {
public virtual Task<string> GetLogoutRedirectUriAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
@ -266,8 +292,10 @@ namespace OpenIddict.EntityFrameworkCore {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the redirect_uri associated with the application.
/// </returns>
public virtual Task<string> GetRedirectUriAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
if (application == null) {
public virtual Task<string> GetRedirectUriAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
@ -283,8 +311,10 @@ namespace OpenIddict.EntityFrameworkCore {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the tokens associated with the application.
/// </returns>
public virtual async Task<IEnumerable<string>> GetTokensAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
if (application == null) {
public virtual async Task<IEnumerable<string>> GetTokensAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
@ -295,7 +325,8 @@ namespace OpenIddict.EntityFrameworkCore {
var tokens = new List<string>();
foreach (var identifier in await query.ToArrayAsync()) {
foreach (var identifier in await query.ToArrayAsync())
{
tokens.Add(ConvertIdentifierToString(identifier));
}
@ -311,12 +342,15 @@ namespace OpenIddict.EntityFrameworkCore {
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
public virtual Task SetClientTypeAsync([NotNull] TApplication application, [NotNull] string type, CancellationToken cancellationToken) {
if (application == null) {
public virtual Task SetClientTypeAsync([NotNull] TApplication application, [NotNull] string type, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
if (string.IsNullOrEmpty(type)) {
if (string.IsNullOrEmpty(type))
{
throw new ArgumentException("The client type cannot be null or empty.", nameof(type));
}
@ -334,12 +368,15 @@ namespace OpenIddict.EntityFrameworkCore {
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
public virtual Task SetHashedSecretAsync([NotNull] TApplication application, [NotNull] string hash, CancellationToken cancellationToken) {
if (application == null) {
public virtual Task SetHashedSecretAsync([NotNull] TApplication application, [NotNull] string hash, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
if (string.IsNullOrEmpty(hash)) {
if (string.IsNullOrEmpty(hash))
{
throw new ArgumentException("The client secret hash cannot be null or empty.", nameof(hash));
}
@ -356,15 +393,18 @@ namespace OpenIddict.EntityFrameworkCore {
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
public virtual async Task UpdateAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
if (application == null) {
public virtual async Task UpdateAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
Context.Attach(application);
Context.Update(application);
try {
try
{
await Context.SaveChangesAsync(cancellationToken);
}
@ -376,8 +416,10 @@ namespace OpenIddict.EntityFrameworkCore {
/// </summary>
/// <param name="identifier">The identifier to convert.</param>
/// <returns>An instance of <typeparamref name="TKey"/> representing the provided identifier.</returns>
public virtual TKey ConvertIdentifierFromString([CanBeNull] string identifier) {
if (string.IsNullOrEmpty(identifier)) {
public virtual TKey ConvertIdentifierFromString([CanBeNull] string identifier)
{
if (string.IsNullOrEmpty(identifier))
{
return default(TKey);
}
@ -390,8 +432,10 @@ namespace OpenIddict.EntityFrameworkCore {
/// </summary>
/// <param name="identifier">The identifier to convert.</param>
/// <returns>A <see cref="string"/> representation of the provided identifier.</returns>
public virtual string ConvertIdentifierToString([CanBeNull] TKey identifier) {
if (Equals(identifier, default(TKey))) {
public virtual string ConvertIdentifierToString([CanBeNull] TKey identifier)
{
if (Equals(identifier, default(TKey)))
{
return null;
}

54
src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs

@ -14,7 +14,8 @@ using Microsoft.EntityFrameworkCore;
using OpenIddict.Core;
using OpenIddict.Models;
namespace OpenIddict.EntityFrameworkCore {
namespace OpenIddict.EntityFrameworkCore
{
/// <summary>
/// Provides methods allowing to manage the authorizations stored in a database.
/// </summary>
@ -22,7 +23,8 @@ namespace OpenIddict.EntityFrameworkCore {
public class OpenIddictAuthorizationStore<TContext> : OpenIddictAuthorizationStore<OpenIddictAuthorization,
OpenIddictApplication,
OpenIddictToken, TContext, string>
where TContext : DbContext {
where TContext : DbContext
{
public OpenIddictAuthorizationStore([NotNull] TContext context) : base(context) { }
}
@ -35,7 +37,8 @@ namespace OpenIddict.EntityFrameworkCore {
OpenIddictApplication<TKey>,
OpenIddictToken<TKey>, TContext, TKey>
where TContext : DbContext
where TKey : IEquatable<TKey> {
where TKey : IEquatable<TKey>
{
public OpenIddictAuthorizationStore([NotNull] TContext context) : base(context) { }
}
@ -52,9 +55,12 @@ namespace OpenIddict.EntityFrameworkCore {
where TApplication : OpenIddictApplication<TKey, TAuthorization, TToken>, new()
where TToken : OpenIddictToken<TKey, TApplication, TAuthorization>, new()
where TContext : DbContext
where TKey : IEquatable<TKey> {
public OpenIddictAuthorizationStore([NotNull] TContext context) {
if (context == null) {
where TKey : IEquatable<TKey>
{
public OpenIddictAuthorizationStore([NotNull] TContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
@ -84,8 +90,10 @@ namespace OpenIddict.EntityFrameworkCore {
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation, whose result returns the authorization.
/// </returns>
public virtual async Task<TAuthorization> CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) {
if (authorization == null) {
public virtual async Task<TAuthorization> CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
@ -106,7 +114,8 @@ namespace OpenIddict.EntityFrameworkCore {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the authorization corresponding to the subject/client.
/// </returns>
public virtual Task<TAuthorization> FindAsync(string subject, string client, CancellationToken cancellationToken) {
public virtual Task<TAuthorization> FindAsync(string subject, string client, CancellationToken cancellationToken)
{
var key = ConvertIdentifierFromString(client);
return (from application in Applications
@ -125,7 +134,8 @@ namespace OpenIddict.EntityFrameworkCore {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the authorization corresponding to the identifier.
/// </returns>
public virtual Task<TAuthorization> FindByIdAsync(string identifier, CancellationToken cancellationToken) {
public virtual Task<TAuthorization> FindByIdAsync(string identifier, CancellationToken cancellationToken)
{
var key = ConvertIdentifierFromString(identifier);
return Authorizations.SingleOrDefaultAsync(authorization => authorization.Id.Equals(key), cancellationToken);
@ -140,8 +150,10 @@ namespace OpenIddict.EntityFrameworkCore {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the authorization.
/// </returns>
public virtual Task<string> GetIdAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) {
if (authorization == null) {
public virtual Task<string> GetIdAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
@ -157,8 +169,10 @@ namespace OpenIddict.EntityFrameworkCore {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the subject associated with the specified authorization.
/// </returns>
public virtual Task<string> GetSubjectAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) {
if (authorization == null) {
public virtual Task<string> GetSubjectAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
@ -170,8 +184,10 @@ namespace OpenIddict.EntityFrameworkCore {
/// </summary>
/// <param name="identifier">The identifier to convert.</param>
/// <returns>An instance of <typeparamref name="TKey"/> representing the provided identifier.</returns>
public virtual TKey ConvertIdentifierFromString([CanBeNull] string identifier) {
if (string.IsNullOrEmpty(identifier)) {
public virtual TKey ConvertIdentifierFromString([CanBeNull] string identifier)
{
if (string.IsNullOrEmpty(identifier))
{
return default(TKey);
}
@ -184,8 +200,10 @@ namespace OpenIddict.EntityFrameworkCore {
/// </summary>
/// <param name="identifier">The identifier to convert.</param>
/// <returns>A <see cref="string"/> representation of the provided identifier.</returns>
public virtual string ConvertIdentifierToString([CanBeNull] TKey identifier) {
if (Equals(identifier, default(TKey))) {
public virtual string ConvertIdentifierToString([CanBeNull] TKey identifier)
{
if (Equals(identifier, default(TKey)))
{
return null;
}

30
src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictScopeStore.cs

@ -11,13 +11,15 @@ using Microsoft.EntityFrameworkCore;
using OpenIddict.Core;
using OpenIddict.Models;
namespace OpenIddict.EntityFrameworkCore {
namespace OpenIddict.EntityFrameworkCore
{
/// <summary>
/// Provides methods allowing to manage the scopes stored in a database.
/// </summary>
/// <typeparam name="TContext">The type of the Entity Framework database context.</typeparam>
public class OpenIddictScopeStore<TContext> : OpenIddictScopeStore<OpenIddictScope, TContext, string>
where TContext : DbContext {
where TContext : DbContext
{
public OpenIddictScopeStore([NotNull] TContext context) : base(context) { }
}
@ -28,7 +30,8 @@ namespace OpenIddict.EntityFrameworkCore {
/// <typeparam name="TKey">The type of the entity primary keys.</typeparam>
public class OpenIddictScopeStore<TContext, TKey> : OpenIddictScopeStore<OpenIddictScope<TKey>, TContext, TKey>
where TContext : DbContext
where TKey : IEquatable<TKey> {
where TKey : IEquatable<TKey>
{
public OpenIddictScopeStore([NotNull] TContext context) : base(context) { }
}
@ -41,9 +44,12 @@ namespace OpenIddict.EntityFrameworkCore {
public class OpenIddictScopeStore<TScope, TContext, TKey> : IOpenIddictScopeStore<TScope>
where TScope : OpenIddictScope<TKey>, new()
where TContext : DbContext
where TKey : IEquatable<TKey> {
public OpenIddictScopeStore([NotNull] TContext context) {
if (context == null) {
where TKey : IEquatable<TKey>
{
public OpenIddictScopeStore([NotNull] TContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
@ -65,8 +71,10 @@ namespace OpenIddict.EntityFrameworkCore {
/// </summary>
/// <param name="identifier">The identifier to convert.</param>
/// <returns>An instance of <typeparamref name="TKey"/> representing the provided identifier.</returns>
public virtual TKey ConvertIdentifierFromString([CanBeNull] string identifier) {
if (string.IsNullOrEmpty(identifier)) {
public virtual TKey ConvertIdentifierFromString([CanBeNull] string identifier)
{
if (string.IsNullOrEmpty(identifier))
{
return default(TKey);
}
@ -79,8 +87,10 @@ namespace OpenIddict.EntityFrameworkCore {
/// </summary>
/// <param name="identifier">The identifier to convert.</param>
/// <returns>A <see cref="string"/> representation of the provided identifier.</returns>
public virtual string ConvertIdentifierToString([CanBeNull] TKey identifier) {
if (Equals(identifier, default(TKey))) {
public virtual string ConvertIdentifierToString([CanBeNull] TKey identifier)
{
if (Equals(identifier, default(TKey)))
{
return null;
}

120
src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs

@ -14,7 +14,8 @@ using Microsoft.EntityFrameworkCore;
using OpenIddict.Core;
using OpenIddict.Models;
namespace OpenIddict.EntityFrameworkCore {
namespace OpenIddict.EntityFrameworkCore
{
/// <summary>
/// Provides methods allowing to manage the tokens stored in a database.
/// </summary>
@ -22,7 +23,8 @@ namespace OpenIddict.EntityFrameworkCore {
public class OpenIddictTokenStore<TContext> : OpenIddictTokenStore<OpenIddictToken,
OpenIddictApplication,
OpenIddictAuthorization, TContext, string>
where TContext : DbContext {
where TContext : DbContext
{
public OpenIddictTokenStore([NotNull] TContext context) : base(context) { }
}
@ -35,7 +37,8 @@ namespace OpenIddict.EntityFrameworkCore {
OpenIddictApplication<TKey>,
OpenIddictAuthorization<TKey>, TContext, TKey>
where TContext : DbContext
where TKey : IEquatable<TKey> {
where TKey : IEquatable<TKey>
{
public OpenIddictTokenStore([NotNull] TContext context) : base(context) { }
}
@ -52,9 +55,12 @@ namespace OpenIddict.EntityFrameworkCore {
where TApplication : OpenIddictApplication<TKey, TAuthorization, TToken>, new()
where TAuthorization : OpenIddictAuthorization<TKey, TApplication, TToken>, new()
where TContext : DbContext
where TKey : IEquatable<TKey> {
public OpenIddictTokenStore([NotNull] TContext context) {
if (context == null) {
where TKey : IEquatable<TKey>
{
public OpenIddictTokenStore([NotNull] TContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
@ -89,8 +95,10 @@ namespace OpenIddict.EntityFrameworkCore {
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation, whose result returns the token.
/// </returns>
public virtual async Task<TToken> CreateAsync([NotNull] TToken token, CancellationToken cancellationToken) {
if (token == null) {
public virtual async Task<TToken> CreateAsync([NotNull] TToken token, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
@ -110,8 +118,10 @@ namespace OpenIddict.EntityFrameworkCore {
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation, whose result returns the token.
/// </returns>
public virtual Task<TToken> CreateAsync([NotNull] string type, [NotNull] string subject, CancellationToken cancellationToken) {
if (string.IsNullOrEmpty(type)) {
public virtual Task<TToken> CreateAsync([NotNull] string type, [NotNull] string subject, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(type))
{
throw new ArgumentException("The token type cannot be null or empty.");
}
@ -127,7 +137,8 @@ namespace OpenIddict.EntityFrameworkCore {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the token corresponding to the unique identifier.
/// </returns>
public virtual Task<TToken> FindByIdAsync(string identifier, CancellationToken cancellationToken) {
public virtual Task<TToken> FindByIdAsync(string identifier, CancellationToken cancellationToken)
{
var key = ConvertIdentifierFromString(identifier);
return Tokens.SingleOrDefaultAsync(token => token.Id.Equals(key), cancellationToken);
@ -142,7 +153,8 @@ namespace OpenIddict.EntityFrameworkCore {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the tokens corresponding to the specified subject.
/// </returns>
public virtual Task<TToken[]> FindBySubjectAsync(string subject, CancellationToken cancellationToken) {
public virtual Task<TToken[]> FindBySubjectAsync(string subject, CancellationToken cancellationToken)
{
return Tokens.Where(token => token.Subject == subject).ToArrayAsync();
}
@ -155,8 +167,10 @@ namespace OpenIddict.EntityFrameworkCore {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the token.
/// </returns>
public virtual Task<string> GetIdAsync([NotNull] TToken token, CancellationToken cancellationToken) {
if (token == null) {
public virtual Task<string> GetIdAsync([NotNull] TToken token, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
@ -172,8 +186,10 @@ namespace OpenIddict.EntityFrameworkCore {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the token type associated with the specified token.
/// </returns>
public virtual Task<string> GetTokenTypeAsync([NotNull] TToken token, CancellationToken cancellationToken) {
if (token == null) {
public virtual Task<string> GetTokenTypeAsync([NotNull] TToken token, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
@ -189,8 +205,10 @@ namespace OpenIddict.EntityFrameworkCore {
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the subject associated with the specified token.
/// </returns>
public virtual Task<string> GetSubjectAsync([NotNull] TToken token, CancellationToken cancellationToken) {
if (token == null) {
public virtual Task<string> GetSubjectAsync([NotNull] TToken token, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
@ -203,14 +221,17 @@ namespace OpenIddict.EntityFrameworkCore {
/// <param name="token">The token to revoke.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public virtual async Task RevokeAsync([NotNull] TToken token, CancellationToken cancellationToken) {
if (token == null) {
public virtual async Task RevokeAsync([NotNull] TToken token, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
Context.Remove(token);
try {
try
{
await Context.SaveChangesAsync(cancellationToken);
}
@ -226,29 +247,35 @@ namespace OpenIddict.EntityFrameworkCore {
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
public virtual async Task SetAuthorizationAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) {
if (token == null) {
public virtual async Task SetAuthorizationAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
if (!string.IsNullOrEmpty(identifier)) {
if (!string.IsNullOrEmpty(identifier))
{
var key = ConvertIdentifierFromString(identifier);
var authorization = await Authorizations.SingleOrDefaultAsync(element => element.Id.Equals(key));
if (authorization == null) {
if (authorization == null)
{
throw new InvalidOperationException("The authorization associated with the token cannot be found.");
}
authorization.Tokens.Add(token);
}
else {
else
{
var key = await GetIdAsync(token, cancellationToken);
// Try to retrieve the authorization associated with the token.
// If none can be found, assume that no authorization is attached.
var authorization = await Authorizations.SingleOrDefaultAsync(element => element.Tokens.Any(t => t.Id.Equals(key)));
if (authorization != null) {
if (authorization != null)
{
authorization.Tokens.Remove(token);
}
}
@ -263,29 +290,35 @@ namespace OpenIddict.EntityFrameworkCore {
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
public virtual async Task SetClientAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) {
if (token == null) {
public virtual async Task SetClientAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
if (!string.IsNullOrEmpty(identifier)) {
if (!string.IsNullOrEmpty(identifier))
{
var key = ConvertIdentifierFromString(identifier);
var application = await Applications.SingleOrDefaultAsync(element => element.Id.Equals(key));
if (application == null) {
if (application == null)
{
throw new InvalidOperationException("The application associated with the token cannot be found.");
}
application.Tokens.Add(token);
}
else {
else
{
var key = await GetIdAsync(token, cancellationToken);
// Try to retrieve the application associated with the token.
// If none can be found, assume that no application is attached.
var application = await Applications.SingleOrDefaultAsync(element => element.Tokens.Any(t => t.Id.Equals(key)));
if (application != null) {
if (application != null)
{
application.Tokens.Remove(token);
}
}
@ -299,15 +332,18 @@ namespace OpenIddict.EntityFrameworkCore {
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
public virtual async Task UpdateAsync([NotNull] TToken token, CancellationToken cancellationToken) {
if (token == null) {
public virtual async Task UpdateAsync([NotNull] TToken token, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
Context.Attach(token);
Context.Update(token);
try {
try
{
await Context.SaveChangesAsync(cancellationToken);
}
@ -319,8 +355,10 @@ namespace OpenIddict.EntityFrameworkCore {
/// </summary>
/// <param name="identifier">The identifier to convert.</param>
/// <returns>An instance of <typeparamref name="TKey"/> representing the provided identifier.</returns>
public virtual TKey ConvertIdentifierFromString([CanBeNull] string identifier) {
if (string.IsNullOrEmpty(identifier)) {
public virtual TKey ConvertIdentifierFromString([CanBeNull] string identifier)
{
if (string.IsNullOrEmpty(identifier))
{
return default(TKey);
}
@ -333,8 +371,10 @@ namespace OpenIddict.EntityFrameworkCore {
/// </summary>
/// <param name="identifier">The identifier to convert.</param>
/// <returns>A <see cref="string"/> representation of the provided identifier.</returns>
public virtual string ConvertIdentifierToString([CanBeNull] TKey identifier) {
if (Equals(identifier, default(TKey))) {
public virtual string ConvertIdentifierToString([CanBeNull] TKey identifier)
{
if (Equals(identifier, default(TKey)))
{
return null;
}

15
src/OpenIddict.Models/OpenIddictApplication.cs

@ -7,12 +7,15 @@
using System;
using System.Collections.Generic;
namespace OpenIddict.Models {
namespace OpenIddict.Models
{
/// <summary>
/// Represents an OpenIddict application.
/// </summary>
public class OpenIddictApplication : OpenIddictApplication<string, OpenIddictAuthorization, OpenIddictToken> {
public OpenIddictApplication() {
public class OpenIddictApplication : OpenIddictApplication<string, OpenIddictAuthorization, OpenIddictToken>
{
public OpenIddictApplication()
{
// Generate a new string identifier.
Id = Guid.NewGuid().ToString();
}
@ -22,12 +25,14 @@ namespace OpenIddict.Models {
/// Represents an OpenIddict application.
/// </summary>
public class OpenIddictApplication<TKey> : OpenIddictApplication<TKey, OpenIddictAuthorization<TKey>, OpenIddictToken<TKey>>
where TKey : IEquatable<TKey> { }
where TKey : IEquatable<TKey>
{ }
/// <summary>
/// Represents an OpenIddict application.
/// </summary>
public class OpenIddictApplication<TKey, TAuthorization, TToken> where TKey : IEquatable<TKey> {
public class OpenIddictApplication<TKey, TAuthorization, TToken> where TKey : IEquatable<TKey>
{
/// <summary>
/// Gets the list of the authorizations associated with this application.
/// </summary>

15
src/OpenIddict.Models/OpenIddictAuthorization.cs

@ -7,12 +7,15 @@
using System;
using System.Collections.Generic;
namespace OpenIddict.Models {
namespace OpenIddict.Models
{
/// <summary>
/// Represents an OpenIddict authorization.
/// </summary>
public class OpenIddictAuthorization : OpenIddictAuthorization<string, OpenIddictApplication, OpenIddictToken> {
public OpenIddictAuthorization() {
public class OpenIddictAuthorization : OpenIddictAuthorization<string, OpenIddictApplication, OpenIddictToken>
{
public OpenIddictAuthorization()
{
// Generate a new string identifier.
Id = Guid.NewGuid().ToString();
}
@ -22,12 +25,14 @@ namespace OpenIddict.Models {
/// Represents an OpenIddict authorization.
/// </summary>
public class OpenIddictAuthorization<TKey> : OpenIddictAuthorization<TKey, OpenIddictApplication<TKey>, OpenIddictToken<TKey>>
where TKey : IEquatable<TKey> { }
where TKey : IEquatable<TKey>
{ }
/// <summary>
/// Represents an OpenIddict authorization.
/// </summary>
public class OpenIddictAuthorization<TKey, TApplication, TToken> where TKey : IEquatable<TKey> {
public class OpenIddictAuthorization<TKey, TApplication, TToken> where TKey : IEquatable<TKey>
{
/// <summary>
/// Gets or sets the application associated with the current authorization.
/// </summary>

12
src/OpenIddict.Models/OpenIddictScope.cs

@ -6,12 +6,15 @@
using System;
namespace OpenIddict.Models {
namespace OpenIddict.Models
{
/// <summary>
/// Represents an OpenIddict scope.
/// </summary>
public class OpenIddictScope : OpenIddictScope<string> {
public OpenIddictScope() {
public class OpenIddictScope : OpenIddictScope<string>
{
public OpenIddictScope()
{
// Generate a new string identifier.
Id = Guid.NewGuid().ToString();
}
@ -20,7 +23,8 @@ namespace OpenIddict.Models {
/// <summary>
/// Represents an OpenIddict scope.
/// </summary>
public class OpenIddictScope<TKey> where TKey : IEquatable<TKey> {
public class OpenIddictScope<TKey> where TKey : IEquatable<TKey>
{
/// <summary>
/// Gets or sets the public description
/// associated with the current scope.

15
src/OpenIddict.Models/OpenIddictToken.cs

@ -6,12 +6,15 @@
using System;
namespace OpenIddict.Models {
namespace OpenIddict.Models
{
/// <summary>
/// Represents an OpenIddict token.
/// </summary>
public class OpenIddictToken : OpenIddictToken<string, OpenIddictApplication, OpenIddictAuthorization> {
public OpenIddictToken() {
public class OpenIddictToken : OpenIddictToken<string, OpenIddictApplication, OpenIddictAuthorization>
{
public OpenIddictToken()
{
// Generate a new string identifier.
Id = Guid.NewGuid().ToString();
}
@ -21,13 +24,15 @@ namespace OpenIddict.Models {
/// Represents an OpenIddict token.
/// </summary>
public class OpenIddictToken<TKey> : OpenIddictToken<TKey, OpenIddictApplication<TKey>, OpenIddictAuthorization<TKey>>
where TKey : IEquatable<TKey> {
where TKey : IEquatable<TKey>
{
}
/// <summary>
/// Represents an OpenIddict token.
/// </summary>
public class OpenIddictToken<TKey, TApplication, TAuthorization> where TKey : IEquatable<TKey> {
public class OpenIddictToken<TKey, TApplication, TAuthorization> where TKey : IEquatable<TKey>
{
/// <summary>
/// Gets or sets the application associated with the current token.
/// </summary>

21
src/OpenIddict.Mvc/OpenIddictExtensions.cs

@ -9,23 +9,30 @@ using JetBrains.Annotations;
using Microsoft.AspNetCore.Mvc;
using OpenIddict.Mvc;
namespace Microsoft.Extensions.DependencyInjection {
public static class OpenIddictExtensions {
namespace Microsoft.Extensions.DependencyInjection
{
public static class OpenIddictExtensions
{
/// <summary>
/// Registers the ASP.NET Core MVC model binders used by OpenIddict.
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddMvcBinders([NotNull] this OpenIddictBuilder builder) {
if (builder == null) {
public static OpenIddictBuilder AddMvcBinders([NotNull] this OpenIddictBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
builder.Services.Configure<MvcOptions>(options => {
builder.Services.Configure<MvcOptions>(options =>
{
// Skip the binder registration if it was already added to the providers collection.
for (var index = 0; index < options.ModelBinderProviders.Count; index++) {
for (var index = 0; index < options.ModelBinderProviders.Count; index++)
{
var provider = options.ModelBinderProviders[index];
if (provider is OpenIddictModelBinder) {
if (provider is OpenIddictModelBinder)
{
return;
}
}

39
src/OpenIddict.Mvc/OpenIddictModelBinder.cs

@ -6,26 +6,32 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
namespace OpenIddict.Mvc {
namespace OpenIddict.Mvc
{
/// <summary>
/// Represents an ASP.NET Core MVC model binder that is able to bind
/// <see cref="OpenIdConnectRequest"/> and
/// <see cref="OpenIdConnectResponse"/> instances.
/// </summary>
public class OpenIddictModelBinder : IModelBinder, IModelBinderProvider {
public class OpenIddictModelBinder : IModelBinder, IModelBinderProvider
{
/// <summary>
/// Tries to bind a model from the request.
/// </summary>
/// <param name="context">The model binding context.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public Task BindModelAsync([NotNull] ModelBindingContext context) {
if (context == null) {
public Task BindModelAsync([NotNull] ModelBindingContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.ModelType == typeof(OpenIdConnectRequest)) {
if (context.ModelType == typeof(OpenIdConnectRequest))
{
var request = context.HttpContext.GetOpenIdConnectRequest();
if (request == null) {
if (request == null)
{
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved from the ASP.NET context. " +
"Make sure that 'app.UseOpenIddict()' is called before 'app.UseMvc()' and " +
"that the action route corresponds to the endpoint path registered via " +
@ -34,7 +40,8 @@ namespace OpenIddict.Mvc {
// Add a new validation state entry to prevent the built-in
// model validators from validating the OpenID Connect request.
context.ValidationState.Add(request, new ValidationStateEntry {
context.ValidationState.Add(request, new ValidationStateEntry
{
SuppressValidation = true
});
@ -43,12 +50,15 @@ namespace OpenIddict.Mvc {
return Task.FromResult(0);
}
else if (context.ModelType == typeof(OpenIdConnectResponse)) {
else if (context.ModelType == typeof(OpenIdConnectResponse))
{
var response = context.HttpContext.GetOpenIdConnectResponse();
if (response != null) {
if (response != null)
{
// Add a new validation state entry to prevent the built-in
// model validators from validating the OpenID Connect response.
context.ValidationState.Add(response, new ValidationStateEntry {
context.ValidationState.Add(response, new ValidationStateEntry
{
SuppressValidation = true
});
}
@ -66,13 +76,16 @@ namespace OpenIddict.Mvc {
/// </summary>
/// <param name="context">The model binding context.</param>
/// <returns>The current instance or <c>null</c> if the model is not supported.</returns>
public IModelBinder GetBinder([NotNull] ModelBinderProviderContext context) {
if (context == null) {
public IModelBinder GetBinder([NotNull] ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.ModelType == typeof(OpenIdConnectRequest) ||
context.Metadata.ModelType == typeof(OpenIdConnectResponse)) {
context.Metadata.ModelType == typeof(OpenIdConnectResponse))
{
return this;
}

326
src/OpenIddict/OpenIddictExtensions.cs

@ -21,22 +21,27 @@ using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using OpenIddict;
namespace Microsoft.AspNetCore.Builder {
public static class OpenIddictExtensions {
namespace Microsoft.AspNetCore.Builder
{
public static class OpenIddictExtensions
{
/// <summary>
/// Registers OpenIddict in the ASP.NET Core pipeline.
/// </summary>
/// <param name="app">The application builder used to register middleware instances.</param>
/// <returns>The <see cref="IApplicationBuilder"/>.</returns>
public static IApplicationBuilder UseOpenIddict([NotNull] this IApplicationBuilder app) {
if (app == null) {
public static IApplicationBuilder UseOpenIddict([NotNull] this IApplicationBuilder app)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
// Resolve the OpenIddict builder from the DI container.
// If it cannot be found, throw an invalid operation exception.
var builder = app.ApplicationServices.GetService<OpenIddictBuilder>();
if (builder == null) {
if (builder == null)
{
throw new InvalidOperationException("The OpenIddict services cannot be resolved from the dependency injection container. " +
"Make sure 'services.AddOpenIddict()' is correctly called from 'ConfigureServices()'.");
}
@ -46,7 +51,8 @@ namespace Microsoft.AspNetCore.Builder {
// When no authorization provider has been registered in the options,
// create a new OpenIddictProvider instance using the specified entities.
if (options.Provider == null) {
if (options.Provider == null)
{
options.Provider = (OpenIdConnectServerProvider) Activator.CreateInstance(
typeof(OpenIddictProvider<,,,>).MakeGenericType(
/* TApplication: */ builder.ApplicationType,
@ -57,24 +63,28 @@ namespace Microsoft.AspNetCore.Builder {
// When no distributed cache has been registered in the options,
// try to resolve it from the dependency injection container.
if (options.Cache == null) {
if (options.Cache == null)
{
options.Cache = app.ApplicationServices.GetService<IDistributedCache>();
if (options.EnableRequestCaching && options.Cache == null) {
if (options.EnableRequestCaching && options.Cache == null)
{
throw new InvalidOperationException("A distributed cache implementation must be registered in the OpenIddict options " +
"or in the dependency injection container when enabling request caching support.");
}
}
// Ensure at least one flow has been enabled.
if (options.GrantTypes.Count == 0) {
if (options.GrantTypes.Count == 0)
{
throw new InvalidOperationException("At least one OAuth2/OpenID Connect flow must be enabled.");
}
// Ensure the authorization endpoint has been enabled when
// the authorization code or implicit grants are supported.
if (!options.AuthorizationEndpointPath.HasValue && (options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.AuthorizationCode) ||
options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.Implicit))) {
options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.Implicit)))
{
throw new InvalidOperationException("The authorization endpoint must be enabled to use " +
"the authorization code and implicit flows.");
}
@ -84,19 +94,22 @@ namespace Microsoft.AspNetCore.Builder {
if (!options.TokenEndpointPath.HasValue && (options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.AuthorizationCode) ||
options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.ClientCredentials) ||
options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.Password) ||
options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.RefreshToken))) {
options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.RefreshToken)))
{
throw new InvalidOperationException("The token endpoint must be enabled to use the authorization code, " +
"client credentials, password and refresh token flows.");
}
if (options.RevocationEndpointPath.HasValue && options.DisableTokenRevocation) {
if (options.RevocationEndpointPath.HasValue && options.DisableTokenRevocation)
{
throw new InvalidOperationException("The revocation endpoint cannot be enabled when token revocation is disabled.");
}
// Ensure at least one asymmetric signing certificate/key was registered if the implicit flow was enabled.
if (!options.SigningCredentials.Any(credentials => credentials.Key is AsymmetricSecurityKey) &&
options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.Implicit)) {
throw new InvalidOperationException("At least one asymmetric signing key must be registered when enabling the implicit flow. "+
options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.Implicit))
{
throw new InvalidOperationException("At least one asymmetric signing key must be registered when enabling the implicit flow. " +
"Consider registering a X.509 certificate using 'services.AddOpenIddict().AddSigningCertificate()' " +
"or call 'services.AddOpenIddict().AddEphemeralSigningKey()' to use an ephemeral key.");
}
@ -113,12 +126,15 @@ namespace Microsoft.AspNetCore.Builder {
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder Configure(
[NotNull] this OpenIddictBuilder builder,
[NotNull] Action<OpenIddictOptions> configuration) {
if (builder == null) {
[NotNull] Action<OpenIddictOptions> configuration)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (configuration == null) {
if (configuration == null)
{
throw new ArgumentNullException(nameof(configuration));
}
@ -135,8 +151,10 @@ namespace Microsoft.AspNetCore.Builder {
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddEphemeralSigningKey([NotNull] this OpenIddictBuilder builder) {
if (builder == null) {
public static OpenIddictBuilder AddEphemeralSigningKey([NotNull] this OpenIddictBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
@ -153,12 +171,15 @@ namespace Microsoft.AspNetCore.Builder {
/// <param name="algorithm">The algorithm associated with the signing key.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddEphemeralSigningKey(
[NotNull] this OpenIddictBuilder builder, [NotNull] string algorithm) {
if (builder == null) {
[NotNull] this OpenIddictBuilder builder, [NotNull] string algorithm)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (string.IsNullOrEmpty(algorithm)) {
if (string.IsNullOrEmpty(algorithm))
{
throw new ArgumentException("The algorithm cannot be null or empty.", nameof(algorithm));
}
@ -173,16 +194,20 @@ namespace Microsoft.AspNetCore.Builder {
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddSigningCertificate(
[NotNull] this OpenIddictBuilder builder,
[NotNull] X509Certificate2 certificate) {
if (builder == null) {
[NotNull] X509Certificate2 certificate)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (certificate == null) {
if (certificate == null)
{
throw new ArgumentNullException(nameof(certificate));
}
if (!certificate.HasPrivateKey) {
if (!certificate.HasPrivateKey)
{
throw new InvalidOperationException("The certificate doesn't contain the required private key.");
}
@ -200,20 +225,25 @@ namespace Microsoft.AspNetCore.Builder {
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddSigningCertificate(
[NotNull] this OpenIddictBuilder builder, [NotNull] Assembly assembly,
[NotNull] string resource, [NotNull] string password) {
if (builder == null) {
[NotNull] string resource, [NotNull] string password)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (assembly == null) {
if (assembly == null)
{
throw new ArgumentNullException(nameof(assembly));
}
if (string.IsNullOrEmpty(resource)) {
if (string.IsNullOrEmpty(resource))
{
throw new ArgumentNullException(nameof(resource));
}
if (string.IsNullOrEmpty(password)) {
if (string.IsNullOrEmpty(password))
{
throw new ArgumentNullException(nameof(password));
}
@ -230,16 +260,20 @@ namespace Microsoft.AspNetCore.Builder {
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddSigningCertificate(
[NotNull] this OpenIddictBuilder builder,
[NotNull] Stream stream, [NotNull] string password) {
if (builder == null) {
[NotNull] Stream stream, [NotNull] string password)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (stream == null) {
if (stream == null)
{
throw new ArgumentNullException(nameof(stream));
}
if (string.IsNullOrEmpty(password)) {
if (string.IsNullOrEmpty(password))
{
throw new ArgumentNullException(nameof(password));
}
@ -260,16 +294,20 @@ namespace Microsoft.AspNetCore.Builder {
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddSigningCertificate(
[NotNull] this OpenIddictBuilder builder, [NotNull] Stream stream,
[NotNull] string password, X509KeyStorageFlags flags) {
if (builder == null) {
[NotNull] string password, X509KeyStorageFlags flags)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (stream == null) {
if (stream == null)
{
throw new ArgumentNullException(nameof(stream));
}
if (string.IsNullOrEmpty(password)) {
if (string.IsNullOrEmpty(password))
{
throw new ArgumentNullException(nameof(password));
}
@ -284,12 +322,15 @@ namespace Microsoft.AspNetCore.Builder {
/// <param name="thumbprint">The thumbprint of the certificate used to identify it in the X.509 store.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddSigningCertificate(
[NotNull] this OpenIddictBuilder builder, [NotNull] string thumbprint) {
if (builder == null) {
[NotNull] this OpenIddictBuilder builder, [NotNull] string thumbprint)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (string.IsNullOrEmpty(thumbprint)) {
if (string.IsNullOrEmpty(thumbprint))
{
throw new ArgumentNullException(nameof(thumbprint));
}
@ -307,12 +348,15 @@ namespace Microsoft.AspNetCore.Builder {
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddSigningCertificate(
[NotNull] this OpenIddictBuilder builder,
[NotNull] string thumbprint, StoreName name, StoreLocation location) {
if (builder == null) {
[NotNull] string thumbprint, StoreName name, StoreLocation location)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (string.IsNullOrEmpty(thumbprint)) {
if (string.IsNullOrEmpty(thumbprint))
{
throw new ArgumentNullException(nameof(thumbprint));
}
@ -327,12 +371,15 @@ namespace Microsoft.AspNetCore.Builder {
/// <param name="key">The security key.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddSigningKey(
[NotNull] this OpenIddictBuilder builder, [NotNull] SecurityKey key) {
if (builder == null) {
[NotNull] this OpenIddictBuilder builder, [NotNull] SecurityKey key)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (key == null) {
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
@ -347,8 +394,10 @@ namespace Microsoft.AspNetCore.Builder {
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AllowAuthorizationCodeFlow([NotNull] this OpenIddictBuilder builder) {
if (builder == null) {
public static OpenIddictBuilder AllowAuthorizationCodeFlow([NotNull] this OpenIddictBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
@ -361,8 +410,10 @@ namespace Microsoft.AspNetCore.Builder {
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AllowClientCredentialsFlow([NotNull] this OpenIddictBuilder builder) {
if (builder == null) {
public static OpenIddictBuilder AllowClientCredentialsFlow([NotNull] this OpenIddictBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
@ -376,12 +427,15 @@ namespace Microsoft.AspNetCore.Builder {
/// <param name="type">The grant type associated with the flow.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AllowCustomFlow(
[NotNull] this OpenIddictBuilder builder, [NotNull] string type) {
if (builder == null) {
[NotNull] this OpenIddictBuilder builder, [NotNull] string type)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (string.IsNullOrEmpty(type)) {
if (string.IsNullOrEmpty(type))
{
throw new ArgumentException("The grant type cannot be null or empty.", nameof(type));
}
@ -396,8 +450,10 @@ namespace Microsoft.AspNetCore.Builder {
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AllowImplicitFlow([NotNull] this OpenIddictBuilder builder) {
if (builder == null) {
public static OpenIddictBuilder AllowImplicitFlow([NotNull] this OpenIddictBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
@ -410,8 +466,10 @@ namespace Microsoft.AspNetCore.Builder {
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AllowPasswordFlow([NotNull] this OpenIddictBuilder builder) {
if (builder == null) {
public static OpenIddictBuilder AllowPasswordFlow([NotNull] this OpenIddictBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
@ -424,8 +482,10 @@ namespace Microsoft.AspNetCore.Builder {
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AllowRefreshTokenFlow([NotNull] this OpenIddictBuilder builder) {
if (builder == null) {
public static OpenIddictBuilder AllowRefreshTokenFlow([NotNull] this OpenIddictBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
@ -437,8 +497,10 @@ namespace Microsoft.AspNetCore.Builder {
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder DisableConfigurationEndpoint([NotNull] this OpenIddictBuilder builder) {
if (builder == null) {
public static OpenIddictBuilder DisableConfigurationEndpoint([NotNull] this OpenIddictBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
@ -450,8 +512,10 @@ namespace Microsoft.AspNetCore.Builder {
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder DisableCryptographyEndpoint([NotNull] this OpenIddictBuilder builder) {
if (builder == null) {
public static OpenIddictBuilder DisableCryptographyEndpoint([NotNull] this OpenIddictBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
@ -463,8 +527,10 @@ namespace Microsoft.AspNetCore.Builder {
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder DisableHttpsRequirement([NotNull] this OpenIddictBuilder builder) {
if (builder == null) {
public static OpenIddictBuilder DisableHttpsRequirement([NotNull] this OpenIddictBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
@ -477,8 +543,10 @@ namespace Microsoft.AspNetCore.Builder {
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder DisableSlidingExpiration([NotNull] this OpenIddictBuilder builder) {
if (builder == null) {
public static OpenIddictBuilder DisableSlidingExpiration([NotNull] this OpenIddictBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
@ -492,8 +560,10 @@ namespace Microsoft.AspNetCore.Builder {
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder DisableTokenRevocation([NotNull] this OpenIddictBuilder builder) {
if (builder == null) {
public static OpenIddictBuilder DisableTokenRevocation([NotNull] this OpenIddictBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
@ -507,12 +577,15 @@ namespace Microsoft.AspNetCore.Builder {
/// <param name="path">The relative path of the authorization endpoint.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder EnableAuthorizationEndpoint(
[NotNull] this OpenIddictBuilder builder, PathString path) {
if (builder == null) {
[NotNull] this OpenIddictBuilder builder, PathString path)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (!path.HasValue) {
if (!path.HasValue)
{
throw new ArgumentException("The path cannot be empty.", nameof(path));
}
@ -526,12 +599,15 @@ namespace Microsoft.AspNetCore.Builder {
/// <param name="path">The relative path of the logout endpoint.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder EnableIntrospectionEndpoint(
[NotNull] this OpenIddictBuilder builder, PathString path) {
if (builder == null) {
[NotNull] this OpenIddictBuilder builder, PathString path)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (!path.HasValue) {
if (!path.HasValue)
{
throw new ArgumentException("The path cannot be empty.", nameof(path));
}
@ -545,12 +621,15 @@ namespace Microsoft.AspNetCore.Builder {
/// <param name="path">The relative path of the logout endpoint.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder EnableLogoutEndpoint(
[NotNull] this OpenIddictBuilder builder, PathString path) {
if (builder == null) {
[NotNull] this OpenIddictBuilder builder, PathString path)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (!path.HasValue) {
if (!path.HasValue)
{
throw new ArgumentException("The path cannot be empty.", nameof(path));
}
@ -566,8 +645,10 @@ namespace Microsoft.AspNetCore.Builder {
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder EnableRequestCaching([NotNull] this OpenIddictBuilder builder) {
if (builder == null) {
public static OpenIddictBuilder EnableRequestCaching([NotNull] this OpenIddictBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
@ -581,12 +662,15 @@ namespace Microsoft.AspNetCore.Builder {
/// <param name="path">The relative path of the revocation endpoint.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder EnableRevocationEndpoint(
[NotNull] this OpenIddictBuilder builder, PathString path) {
if (builder == null) {
[NotNull] this OpenIddictBuilder builder, PathString path)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (!path.HasValue) {
if (!path.HasValue)
{
throw new ArgumentException("The path cannot be empty.", nameof(path));
}
@ -600,12 +684,15 @@ namespace Microsoft.AspNetCore.Builder {
/// <param name="path">The relative path of the token endpoint.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder EnableTokenEndpoint(
[NotNull] this OpenIddictBuilder builder, PathString path) {
if (builder == null) {
[NotNull] this OpenIddictBuilder builder, PathString path)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (!path.HasValue) {
if (!path.HasValue)
{
throw new ArgumentException("The path cannot be empty.", nameof(path));
}
@ -619,12 +706,15 @@ namespace Microsoft.AspNetCore.Builder {
/// <param name="path">The relative path of the userinfo endpoint.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder EnableUserinfoEndpoint(
[NotNull] this OpenIddictBuilder builder, PathString path) {
if (builder == null) {
[NotNull] this OpenIddictBuilder builder, PathString path)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (!path.HasValue) {
if (!path.HasValue)
{
throw new ArgumentException("The path cannot be empty.", nameof(path));
}
@ -638,8 +728,10 @@ namespace Microsoft.AspNetCore.Builder {
/// the token and revocation endpoints, but specifying a client_id is required.
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
public static OpenIddictBuilder RequireClientIdentification([NotNull] this OpenIddictBuilder builder) {
if (builder == null) {
public static OpenIddictBuilder RequireClientIdentification([NotNull] this OpenIddictBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
@ -656,8 +748,10 @@ namespace Microsoft.AspNetCore.Builder {
/// <param name="lifetime">The access token lifetime.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder SetAccessTokenLifetime(
[NotNull] this OpenIddictBuilder builder, TimeSpan lifetime) {
if (builder == null) {
[NotNull] this OpenIddictBuilder builder, TimeSpan lifetime)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
@ -673,8 +767,10 @@ namespace Microsoft.AspNetCore.Builder {
/// <param name="lifetime">The authorization code lifetime.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder SetAuthorizationCodeLifetime(
[NotNull] this OpenIddictBuilder builder, TimeSpan lifetime) {
if (builder == null) {
[NotNull] this OpenIddictBuilder builder, TimeSpan lifetime)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
@ -689,8 +785,10 @@ namespace Microsoft.AspNetCore.Builder {
/// <param name="lifetime">The identity token lifetime.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder SetIdentityTokenLifetime(
[NotNull] this OpenIddictBuilder builder, TimeSpan lifetime) {
if (builder == null) {
[NotNull] this OpenIddictBuilder builder, TimeSpan lifetime)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
@ -707,8 +805,10 @@ namespace Microsoft.AspNetCore.Builder {
/// <param name="lifetime">The refresh token lifetime.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder SetRefreshTokenLifetime(
[NotNull] this OpenIddictBuilder builder, TimeSpan lifetime) {
if (builder == null) {
[NotNull] this OpenIddictBuilder builder, TimeSpan lifetime)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
@ -723,12 +823,15 @@ namespace Microsoft.AspNetCore.Builder {
/// <param name="address">The issuer address.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder SetIssuer(
[NotNull] this OpenIddictBuilder builder, [NotNull] Uri address) {
if (builder == null) {
[NotNull] this OpenIddictBuilder builder, [NotNull] Uri address)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (address == null) {
if (address == null)
{
throw new ArgumentNullException(nameof(address));
}
@ -743,12 +846,15 @@ namespace Microsoft.AspNetCore.Builder {
/// <param name="provider">The data protection provider used to create token protectors.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder UseDataProtectionProvider(
[NotNull] this OpenIddictBuilder builder, [NotNull] IDataProtectionProvider provider) {
if (builder == null) {
[NotNull] this OpenIddictBuilder builder, [NotNull] IDataProtectionProvider provider)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (provider == null) {
if (provider == null)
{
throw new ArgumentNullException(nameof(provider));
}
@ -760,8 +866,10 @@ namespace Microsoft.AspNetCore.Builder {
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder UseJsonWebTokens([NotNull] this OpenIddictBuilder builder) {
if (builder == null) {
public static OpenIddictBuilder UseJsonWebTokens([NotNull] this OpenIddictBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}

9
src/OpenIddict/OpenIddictOptions.cs

@ -9,12 +9,15 @@ using System.Collections.Generic;
using AspNet.Security.OpenIdConnect.Server;
using Microsoft.Extensions.Caching.Distributed;
namespace OpenIddict {
namespace OpenIddict
{
/// <summary>
/// Provides various settings needed to configure OpenIddict.
/// </summary>
public class OpenIddictOptions : OpenIdConnectServerOptions {
public OpenIddictOptions() {
public class OpenIddictOptions : OpenIdConnectServerOptions
{
public OpenIddictOptions()
{
Provider = null;
}

105
src/OpenIddict/OpenIddictProvider.Authentication.cs

@ -22,15 +22,19 @@ using Newtonsoft.Json.Bson;
using Newtonsoft.Json.Linq;
using OpenIddict.Core;
namespace OpenIddict {
namespace OpenIddict
{
public partial class OpenIddictProvider<TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
where TApplication : class where TAuthorization : class where TScope : class where TToken : class {
public override async Task ExtractAuthorizationRequest([NotNull] ExtractAuthorizationRequestContext context) {
where TApplication : class where TAuthorization : class where TScope : class where TToken : class
{
public override async Task ExtractAuthorizationRequest([NotNull] ExtractAuthorizationRequestContext context)
{
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<OpenIddictProvider<TApplication, TAuthorization, TScope, TToken>>>();
var options = context.HttpContext.RequestServices.GetRequiredService<IOptions<OpenIddictOptions>>();
// Reject requests using the unsupported request parameter.
if (!string.IsNullOrEmpty(context.Request.Request)) {
if (!string.IsNullOrEmpty(context.Request.Request))
{
logger.LogError("The authorization request was rejected because it contained " +
"an unsupported parameter: {Parameter}.", "request");
@ -42,7 +46,8 @@ namespace OpenIddict {
}
// Reject requests using the unsupported request_uri parameter.
if (!string.IsNullOrEmpty(context.Request.RequestUri)) {
if (!string.IsNullOrEmpty(context.Request.RequestUri))
{
logger.LogError("The authorization request was rejected because it contained " +
"an unsupported parameter: {Parameter}.", "request_uri");
@ -55,9 +60,11 @@ namespace OpenIddict {
// If a request_id parameter can be found in the authorization request,
// restore the complete authorization request from the distributed cache.
if (!string.IsNullOrEmpty(context.Request.RequestId)) {
if (!string.IsNullOrEmpty(context.Request.RequestId))
{
// Return an error if request caching support was not enabled.
if (!options.Value.EnableRequestCaching) {
if (!options.Value.EnableRequestCaching)
{
logger.LogError("The authorization request was rejected because " +
"request caching support was not enabled.");
@ -73,7 +80,8 @@ namespace OpenIddict {
var key = OpenIddictConstants.Environment.AuthorizationRequest + context.Request.RequestId;
var payload = await options.Value.Cache.GetAsync(key);
if (payload == null) {
if (payload == null)
{
logger.LogError("The authorization request was rejected because an unknown " +
"or invalid request_id parameter was specified.");
@ -85,10 +93,13 @@ namespace OpenIddict {
}
// Restore the authorization request parameters from the serialized payload.
using (var reader = new BsonReader(new MemoryStream(payload))) {
foreach (var parameter in JObject.Load(reader)) {
using (var reader = new BsonReader(new MemoryStream(payload)))
{
foreach (var parameter in JObject.Load(reader))
{
// Avoid overriding the current request parameters.
if (context.Request.HasParameter(parameter.Key)) {
if (context.Request.HasParameter(parameter.Key))
{
continue;
}
@ -98,7 +109,8 @@ namespace OpenIddict {
}
}
public override async Task ValidateAuthorizationRequest([NotNull] ValidateAuthorizationRequestContext context) {
public override async Task ValidateAuthorizationRequest([NotNull] ValidateAuthorizationRequestContext context)
{
var applications = context.HttpContext.RequestServices.GetRequiredService<OpenIddictApplicationManager<TApplication>>();
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<OpenIddictProvider<TApplication, TAuthorization, TScope, TToken>>>();
var options = context.HttpContext.RequestServices.GetRequiredService<IOptions<OpenIddictOptions>>();
@ -106,7 +118,8 @@ namespace OpenIddict {
// Note: the OpenID Connect server middleware supports authorization code, implicit, hybrid,
// none and custom flows but OpenIddict uses a stricter policy rejecting unknown flows.
if (!context.Request.IsAuthorizationCodeFlow() && !context.Request.IsHybridFlow() &&
!context.Request.IsImplicitFlow() && !context.Request.IsNoneFlow()) {
!context.Request.IsImplicitFlow() && !context.Request.IsNoneFlow())
{
logger.LogError("The authorization request was rejected because the '{ResponseType}' " +
"response type is not supported.", context.Request.ResponseType);
@ -119,7 +132,8 @@ namespace OpenIddict {
// Reject code flow authorization requests if the authorization code flow is not enabled.
if (context.Request.IsAuthorizationCodeFlow() &&
!options.Value.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.AuthorizationCode)) {
!options.Value.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.AuthorizationCode))
{
logger.LogError("The authorization request was rejected because " +
"the authorization code flow was not enabled.");
@ -131,7 +145,8 @@ namespace OpenIddict {
}
// Reject implicit flow authorization requests if the implicit flow is not enabled.
if (context.Request.IsImplicitFlow() && !options.Value.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.Implicit)) {
if (context.Request.IsImplicitFlow() && !options.Value.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.Implicit))
{
logger.LogError("The authorization request was rejected because the implicit flow was not enabled.");
context.Reject(
@ -143,7 +158,8 @@ namespace OpenIddict {
// Reject hybrid flow authorization requests if the authorization code or the implicit flows are not enabled.
if (context.Request.IsHybridFlow() && (!options.Value.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.AuthorizationCode) ||
!options.Value.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.Implicit))) {
!options.Value.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.Implicit)))
{
logger.LogError("The authorization request was rejected because the " +
"authorization code flow or the implicit flow was not enabled.");
@ -156,7 +172,8 @@ namespace OpenIddict {
// Reject authorization requests that specify scope=offline_access if the refresh token flow is not enabled.
if (context.Request.HasScope(OpenIdConnectConstants.Scopes.OfflineAccess) &&
!options.Value.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.RefreshToken)) {
!options.Value.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.RefreshToken))
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidRequest,
description: "The 'offline_access' scope is not allowed.");
@ -169,7 +186,8 @@ namespace OpenIddict {
// To ensure authorization requests are rejected early enough, an additional check is made by OpenIddict.
if (!string.IsNullOrEmpty(context.Request.ResponseMode) && !context.Request.IsFormPostResponseMode() &&
!context.Request.IsFragmentResponseMode() &&
!context.Request.IsQueryResponseMode()) {
!context.Request.IsQueryResponseMode())
{
logger.LogError("The authorization request was rejected because the '{ResponseMode}' " +
"response mode is not supported.", context.Request.ResponseMode);
@ -184,7 +202,8 @@ namespace OpenIddict {
// but this provider uses a stricter policy making it mandatory,
// as required by the OpenID Connect core specification.
// See http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest.
if (string.IsNullOrEmpty(context.RedirectUri)) {
if (string.IsNullOrEmpty(context.RedirectUri))
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidRequest,
description: "The required redirect_uri parameter was missing.");
@ -194,10 +213,12 @@ namespace OpenIddict {
// Note: the OpenID Connect server middleware always ensures a
// code_challenge_method can't be specified without code_challenge.
if (!string.IsNullOrEmpty(context.Request.CodeChallenge)) {
if (!string.IsNullOrEmpty(context.Request.CodeChallenge))
{
// Since the default challenge method (plain) is explicitly disallowed,
// reject the authorization request if the code_challenge_method is missing.
if (string.IsNullOrEmpty(context.Request.CodeChallengeMethod)) {
if (string.IsNullOrEmpty(context.Request.CodeChallengeMethod))
{
logger.LogError("The authorization request was rejected because the " +
"required 'code_challenge_method' parameter was missing.");
@ -210,7 +231,8 @@ namespace OpenIddict {
// Disallow the use of the unsecure code_challenge_method=plain method.
// See https://tools.ietf.org/html/rfc7636#section-7.2 for more information.
if (string.Equals(context.Request.CodeChallengeMethod, OpenIdConnectConstants.CodeChallengeMethods.Plain)) {
if (string.Equals(context.Request.CodeChallengeMethod, OpenIdConnectConstants.CodeChallengeMethods.Plain))
{
logger.LogError("The authorization request was rejected because the " +
"'code_challenge_method' parameter was set to 'plain'.");
@ -222,7 +244,8 @@ namespace OpenIddict {
}
// Reject authorization requests that contain response_type=token when a code_challenge is specified.
if (context.Request.HasResponseType(OpenIdConnectConstants.ResponseTypes.Token)) {
if (context.Request.HasResponseType(OpenIdConnectConstants.ResponseTypes.Token))
{
logger.LogError("The authorization request was rejected because the " +
"specified response type was not compatible with PKCE.");
@ -236,7 +259,8 @@ namespace OpenIddict {
// Retrieve the application details corresponding to the requested client_id.
var application = await applications.FindByClientIdAsync(context.ClientId, context.HttpContext.RequestAborted);
if (application == null) {
if (application == null)
{
logger.LogError("The authorization request was rejected because the client " +
"application was not found: '{ClientId}'.", context.ClientId);
@ -248,7 +272,8 @@ namespace OpenIddict {
}
// Ensure a redirect_uri was associated with the application.
if (!await applications.HasRedirectUriAsync(application, context.HttpContext.RequestAborted)) {
if (!await applications.HasRedirectUriAsync(application, context.HttpContext.RequestAborted))
{
logger.LogError("The authorization request was rejected because no redirect_uri " +
"was registered with the application '{ClientId}'.", context.ClientId);
@ -260,7 +285,8 @@ namespace OpenIddict {
}
// Ensure the redirect_uri is valid.
if (!await applications.ValidateRedirectUriAsync(application, context.RedirectUri, context.HttpContext.RequestAborted)) {
if (!await applications.ValidateRedirectUriAsync(application, context.RedirectUri, context.HttpContext.RequestAborted))
{
logger.LogError("The authorization request was rejected because the redirect_uri " +
"was invalid: '{RedirectUri}'.", context.RedirectUri);
@ -276,7 +302,8 @@ namespace OpenIddict {
// Note: when using the authorization code grant, ValidateTokenRequest is responsible of rejecting
// the token request if the client_id corresponds to an unauthenticated confidential client.
if (await applications.IsConfidentialAsync(application, context.HttpContext.RequestAborted) &&
context.Request.HasResponseType(OpenIdConnectConstants.ResponseTypes.Token)) {
context.Request.HasResponseType(OpenIdConnectConstants.ResponseTypes.Token))
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidRequest,
description: "Confidential clients are not allowed to retrieve " +
@ -288,20 +315,23 @@ namespace OpenIddict {
context.Validate();
}
public override async Task HandleAuthorizationRequest([NotNull] HandleAuthorizationRequestContext context) {
public override async Task HandleAuthorizationRequest([NotNull] HandleAuthorizationRequestContext context)
{
var options = context.HttpContext.RequestServices.GetRequiredService<IOptions<OpenIddictOptions>>();
// If no request_id parameter can be found in the current request, assume the OpenID Connect request
// was not serialized yet and store the entire payload in the distributed cache to make it easier
// to flow across requests and internal/external authentication/registration workflows.
if (options.Value.EnableRequestCaching && string.IsNullOrEmpty(context.Request.RequestId)) {
if (options.Value.EnableRequestCaching && string.IsNullOrEmpty(context.Request.RequestId))
{
// Generate a request identifier. Note: using a crypto-secure
// random number generator is not necessary in this case.
context.Request.RequestId = Guid.NewGuid().ToString();
// Store the serialized authorization request parameters in the distributed cache.
var stream = new MemoryStream();
using (var writer = new BsonWriter(stream)) {
using (var writer = new BsonWriter(stream))
{
writer.CloseOutput = false;
var serializer = JsonSerializer.CreateDefault();
@ -312,7 +342,8 @@ namespace OpenIddict {
// to avoid collisions with the other types of cached requests.
var key = OpenIddictConstants.Environment.AuthorizationRequest + context.Request.RequestId;
await options.Value.Cache.SetAsync(key, stream.ToArray(), new DistributedCacheEntryOptions {
await options.Value.Cache.SetAsync(key, stream.ToArray(), new DistributedCacheEntryOptions
{
AbsoluteExpiration = context.Options.SystemClock.UtcNow + TimeSpan.FromMinutes(30),
SlidingExpiration = TimeSpan.FromMinutes(10)
});
@ -335,11 +366,13 @@ namespace OpenIddict {
context.SkipToNextMiddleware();
}
public override async Task ApplyAuthorizationResponse([NotNull] ApplyAuthorizationResponseContext context) {
public override async Task ApplyAuthorizationResponse([NotNull] ApplyAuthorizationResponseContext context)
{
var options = context.HttpContext.RequestServices.GetRequiredService<IOptions<OpenIddictOptions>>();
// Remove the authorization request from the distributed cache.
if (options.Value.EnableRequestCaching && !string.IsNullOrEmpty(context.Request.RequestId)) {
if (options.Value.EnableRequestCaching && !string.IsNullOrEmpty(context.Request.RequestId))
{
// Note: the cache key is always prefixed with a specific marker
// to avoid collisions with the other types of cached requests.
var key = OpenIddictConstants.Environment.AuthorizationRequest + context.Request.RequestId;
@ -351,12 +384,14 @@ namespace OpenIddict {
}
if (!options.Value.ApplicationCanDisplayErrors && !string.IsNullOrEmpty(context.Response.Error) &&
string.IsNullOrEmpty(context.Response.RedirectUri)) {
string.IsNullOrEmpty(context.Response.RedirectUri))
{
// Determine if the status code pages middleware has been enabled for this request.
// If it was not registered or enabled, let the OpenID Connect server middleware render
// a default error page instead of delegating the rendering to the status code middleware.
var feature = context.HttpContext.Features.Get<IStatusCodePagesFeature>();
if (feature != null && feature.Enabled) {
if (feature != null && feature.Enabled)
{
// Replace the default status code to return a 400 response.
context.HttpContext.Response.StatusCode = 400;

13
src/OpenIddict/OpenIddictProvider.Discovery.cs

@ -9,16 +9,18 @@ using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Primitives;
using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
using OpenIddict.Core;
namespace OpenIddict {
namespace OpenIddict
{
public partial class OpenIddictProvider<TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
where TApplication : class where TAuthorization : class where TScope : class where TToken : class {
public override Task HandleConfigurationRequest([NotNull] HandleConfigurationRequestContext context) {
where TApplication : class where TAuthorization : class where TScope : class where TToken : class
{
public override Task HandleConfigurationRequest([NotNull] HandleConfigurationRequestContext context)
{
var options = context.HttpContext.RequestServices.GetRequiredService<IOptions<OpenIddictOptions>>();
// Note: though it's natively supported by the OpenID Connect server middleware,
@ -40,7 +42,8 @@ namespace OpenIddict {
context.Scopes.Add(OpenIddictConstants.Scopes.Roles);
// Only add the "offline_access" scope if the refresh token grant is enabled.
if (context.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.RefreshToken)) {
if (context.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.RefreshToken))
{
context.Scopes.Add(OpenIdConnectConstants.Scopes.OfflineAccess);
}

66
src/OpenIddict/OpenIddictProvider.Exchange.cs

@ -15,16 +15,20 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using OpenIddict.Core;
namespace OpenIddict {
namespace OpenIddict
{
public partial class OpenIddictProvider<TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
where TApplication : class where TAuthorization : class where TScope : class where TToken : class {
public override async Task ValidateTokenRequest([NotNull] ValidateTokenRequestContext context) {
where TApplication : class where TAuthorization : class where TScope : class where TToken : class
{
public override async Task ValidateTokenRequest([NotNull] ValidateTokenRequestContext context)
{
var applications = context.HttpContext.RequestServices.GetRequiredService<OpenIddictApplicationManager<TApplication>>();
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<OpenIddictProvider<TApplication, TAuthorization, TScope, TToken>>>();
var options = context.HttpContext.RequestServices.GetRequiredService<IOptions<OpenIddictOptions>>();
// Reject token requests that don't specify a supported grant type.
if (!options.Value.GrantTypes.Contains(context.Request.GrantType)) {
if (!options.Value.GrantTypes.Contains(context.Request.GrantType))
{
logger.LogError("The token request was rejected because the '{Grant}' " +
"grant is not supported.", context.Request.GrantType);
@ -37,7 +41,8 @@ namespace OpenIddict {
// Reject token requests that specify scope=offline_access if the refresh token flow is not enabled.
if (context.Request.HasScope(OpenIdConnectConstants.Scopes.OfflineAccess) &&
!options.Value.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.RefreshToken)) {
!options.Value.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.RefreshToken))
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidRequest,
description: "The 'offline_access' scope is not allowed.");
@ -51,7 +56,8 @@ namespace OpenIddict {
// that rejects grant_type=client_credentials requests containing the 'offline_access' scope.
// See https://tools.ietf.org/html/rfc6749#section-4.4.3 for more information.
if (context.Request.IsClientCredentialsGrantType() &&
context.Request.HasScope(OpenIdConnectConstants.Scopes.OfflineAccess)) {
context.Request.HasScope(OpenIdConnectConstants.Scopes.OfflineAccess))
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidRequest,
description: "The 'offline_access' scope is not allowed when using grant_type=client_credentials.");
@ -63,7 +69,8 @@ namespace OpenIddict {
// when validation is skipped but an early check is made here to avoid making unnecessary
// database roundtrips to retrieve the client application corresponding to the client_id.
if (context.Request.IsClientCredentialsGrantType() && (string.IsNullOrEmpty(context.Request.ClientId) ||
string.IsNullOrEmpty(context.Request.ClientSecret))) {
string.IsNullOrEmpty(context.Request.ClientSecret)))
{
logger.LogError("The token request was rejected because the client credentials were missing.");
context.Reject(
@ -85,9 +92,11 @@ namespace OpenIddict {
// Note: the OpenID Connect server middleware will automatically ensure that
// the calling application cannot use an authorization code or a refresh token
// if it's not the intended audience, even if client authentication was skipped.
if (string.IsNullOrEmpty(context.ClientId)) {
if (string.IsNullOrEmpty(context.ClientId))
{
// Reject the request if client identification is mandatory.
if (options.Value.RequireClientIdentification) {
if (options.Value.RequireClientIdentification)
{
logger.LogError("The token request was rejected becaused the " +
"mandatory client_id parameter was missing or empty.");
@ -108,7 +117,8 @@ namespace OpenIddict {
// Retrieve the application details corresponding to the requested client_id.
var application = await applications.FindByClientIdAsync(context.ClientId, context.HttpContext.RequestAborted);
if (application == null) {
if (application == null)
{
logger.LogError("The token request was rejected because the client " +
"application was not found: '{ClientId}'.", context.ClientId);
@ -119,9 +129,11 @@ namespace OpenIddict {
return;
}
if (await applications.IsPublicAsync(application, context.HttpContext.RequestAborted)) {
if (await applications.IsPublicAsync(application, context.HttpContext.RequestAborted))
{
// Note: public applications are not allowed to use the client credentials grant.
if (context.Request.IsClientCredentialsGrantType()) {
if (context.Request.IsClientCredentialsGrantType())
{
logger.LogError("The token request was rejected because the public client application '{ClientId}' " +
"was not allowed to use the client credentials grant.", context.Request.ClientId);
@ -133,7 +145,8 @@ namespace OpenIddict {
}
// Reject tokens requests containing a client_secret when the client is a public application.
if (!string.IsNullOrEmpty(context.ClientSecret)) {
if (!string.IsNullOrEmpty(context.ClientSecret))
{
logger.LogError("The token request was rejected because the public application '{ClientId}' " +
"was not allowed to send a client secret.", context.ClientId);
@ -156,7 +169,8 @@ namespace OpenIddict {
// Confidential applications MUST authenticate
// to protect them from impersonation attacks.
if (string.IsNullOrEmpty(context.ClientSecret)) {
if (string.IsNullOrEmpty(context.ClientSecret))
{
logger.LogError("The token request was rejected because the confidential application " +
"'{ClientId}' didn't specify a client secret.", context.ClientId);
@ -167,7 +181,8 @@ namespace OpenIddict {
return;
}
if (!await applications.ValidateClientSecretAsync(application, context.ClientSecret, context.HttpContext.RequestAborted)) {
if (!await applications.ValidateClientSecretAsync(application, context.ClientSecret, context.HttpContext.RequestAborted))
{
logger.LogError("The token request was rejected because the confidential application " +
"'{ClientId}' didn't specify valid client credentials.", context.ClientId);
@ -181,13 +196,15 @@ namespace OpenIddict {
context.Validate();
}
public override async Task HandleTokenRequest([NotNull] HandleTokenRequestContext context) {
public override async Task HandleTokenRequest([NotNull] HandleTokenRequestContext context)
{
var options = context.HttpContext.RequestServices.GetRequiredService<IOptions<OpenIddictOptions>>();
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<OpenIddictProvider<TApplication, TAuthorization, TScope, TToken>>>();
var tokens = context.HttpContext.RequestServices.GetRequiredService<OpenIddictTokenManager<TToken>>();
if (!options.Value.DisableTokenRevocation && (context.Request.IsAuthorizationCodeGrantType() ||
context.Request.IsRefreshTokenGrantType())) {
context.Request.IsRefreshTokenGrantType()))
{
Debug.Assert(context.Ticket != null, "The authentication ticket shouldn't be null.");
// Extract the token identifier from the authentication ticket.
@ -195,10 +212,12 @@ namespace OpenIddict {
Debug.Assert(!string.IsNullOrEmpty(identifier),
"The authentication ticket should contain a ticket identifier.");
if (context.Request.IsAuthorizationCodeGrantType()) {
if (context.Request.IsAuthorizationCodeGrantType())
{
// Retrieve the token from the database and ensure it is still valid.
var token = await tokens.FindByIdAsync(identifier, context.HttpContext.RequestAborted);
if (token == null) {
if (token == null)
{
logger.LogError("The token request was rejected because the authorization code was revoked.");
context.Reject(
@ -212,10 +231,12 @@ namespace OpenIddict {
await tokens.RevokeAsync(token, context.HttpContext.RequestAborted);
}
else if (context.Request.IsRefreshTokenGrantType()) {
else if (context.Request.IsRefreshTokenGrantType())
{
// Retrieve the token from the database and ensure it is still valid.
var token = await tokens.FindByIdAsync(identifier, context.HttpContext.RequestAborted);
if (token == null) {
if (token == null)
{
logger.LogError("The token request was rejected because the refresh token was revoked.");
context.Reject(
@ -228,7 +249,8 @@ namespace OpenIddict {
// When sliding expiration is enabled, immediately
// revoke the refresh token to prevent future reuse.
// See https://tools.ietf.org/html/rfc6749#section-6.
if (context.Options.UseSlidingExpiration) {
if (context.Options.UseSlidingExpiration)
{
await tokens.RevokeAsync(token, context.HttpContext.RequestAborted);
}
}

39
src/OpenIddict/OpenIddictProvider.Introspection.cs

@ -16,13 +16,17 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using OpenIddict.Core;
namespace OpenIddict {
namespace OpenIddict
{
public partial class OpenIddictProvider<TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
where TApplication : class where TAuthorization : class where TScope : class where TToken : class {
public override Task ExtractIntrospectionRequest([NotNull] ExtractIntrospectionRequestContext context) {
where TApplication : class where TAuthorization : class where TScope : class where TToken : class
{
public override Task ExtractIntrospectionRequest([NotNull] ExtractIntrospectionRequestContext context)
{
// Note: the OpenID Connect server middleware supports both GET and POST
// introspection requests but OpenIddict only accepts POST requests.
if (!string.Equals(context.HttpContext.Request.Method, "POST", StringComparison.OrdinalIgnoreCase)) {
if (!string.Equals(context.HttpContext.Request.Method, "POST", StringComparison.OrdinalIgnoreCase))
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidRequest,
description: "Introspection requests must use HTTP POST.");
@ -33,7 +37,8 @@ namespace OpenIddict {
return Task.FromResult(0);
}
public override async Task ValidateIntrospectionRequest([NotNull] ValidateIntrospectionRequestContext context) {
public override async Task ValidateIntrospectionRequest([NotNull] ValidateIntrospectionRequestContext context)
{
var applications = context.HttpContext.RequestServices.GetRequiredService<OpenIddictApplicationManager<TApplication>>();
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<OpenIddictProvider<TApplication, TAuthorization, TScope, TToken>>>();
@ -41,7 +46,8 @@ namespace OpenIddict {
// but OpenIddict uses a stricter policy preventing unauthenticated/public applications
// from using the introspection endpoint, as required by the specifications.
// See https://tools.ietf.org/html/rfc7662#section-2.1 for more information.
if (string.IsNullOrEmpty(context.ClientId) || string.IsNullOrEmpty(context.ClientSecret)) {
if (string.IsNullOrEmpty(context.ClientId) || string.IsNullOrEmpty(context.ClientSecret))
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidRequest,
description: "Clients must be authenticated to use the introspection endpoint.");
@ -51,7 +57,8 @@ namespace OpenIddict {
// Retrieve the application details corresponding to the requested client_id.
var application = await applications.FindByClientIdAsync(context.ClientId, context.HttpContext.RequestAborted);
if (application == null) {
if (application == null)
{
logger.LogError("The introspection request was rejected because the client " +
"application was not found: '{ClientId}'.", context.ClientId);
@ -63,7 +70,8 @@ namespace OpenIddict {
}
// Reject non-confidential applications.
if (!await applications.IsConfidentialAsync(application, context.HttpContext.RequestAborted)) {
if (!await applications.IsConfidentialAsync(application, context.HttpContext.RequestAborted))
{
logger.LogError("The introspection request was rejected because the public application " +
"'{ClientId}' was not allowed to use this endpoint.", context.ClientId);
@ -75,7 +83,8 @@ namespace OpenIddict {
}
// Validate the client credentials.
if (!await applications.ValidateClientSecretAsync(application, context.ClientSecret, context.HttpContext.RequestAborted)) {
if (!await applications.ValidateClientSecretAsync(application, context.ClientSecret, context.HttpContext.RequestAborted))
{
logger.LogError("The introspection request was rejected because the confidential application " +
"'{ClientId}' didn't specify valid client credentials.", context.ClientId);
@ -89,7 +98,8 @@ namespace OpenIddict {
context.Validate();
}
public override async Task HandleIntrospectionRequest([NotNull] HandleIntrospectionRequestContext context) {
public override async Task HandleIntrospectionRequest([NotNull] HandleIntrospectionRequestContext context)
{
var options = context.HttpContext.RequestServices.GetRequiredService<IOptions<OpenIddictOptions>>();
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<OpenIddictProvider<TApplication, TAuthorization, TScope, TToken>>>();
var tokens = context.HttpContext.RequestServices.GetRequiredService<OpenIddictTokenManager<TToken>>();
@ -100,7 +110,8 @@ namespace OpenIddict {
// Note: the OpenID Connect server middleware allows authorized presenters (e.g relying parties) to introspect access tokens
// but OpenIddict uses a stricter policy that only allows resource servers to use the introspection endpoint, unless the ticket
// doesn't have any audience: in this case, the caller is allowed to introspect the token even if it's not listed as a valid audience.
if (context.Ticket.IsAccessToken() && context.Ticket.HasAudience() && !context.Ticket.HasAudience(context.Request.ClientId)) {
if (context.Ticket.IsAccessToken() && context.Ticket.HasAudience() && !context.Ticket.HasAudience(context.Request.ClientId))
{
logger.LogWarning("The client application '{ClientId}' is not allowed to introspect the access " +
"token '{Identifier}' because it's not listed as a valid audience.",
context.Request.ClientId, context.Ticket.GetTicketId());
@ -111,11 +122,13 @@ namespace OpenIddict {
}
// When the received ticket is revocable, ensure it is still valid.
if (!options.Value.DisableTokenRevocation && (context.Ticket.IsAuthorizationCode() || context.Ticket.IsRefreshToken())) {
if (!options.Value.DisableTokenRevocation && (context.Ticket.IsAuthorizationCode() || context.Ticket.IsRefreshToken()))
{
// Retrieve the token from the database using the unique identifier stored in the authentication ticket:
// if the corresponding entry cannot be found, return Active = false to indicate that is is no longer valid.
var token = await tokens.FindByIdAsync(context.Ticket.GetTicketId(), context.HttpContext.RequestAborted);
if (token == null) {
if (token == null)
{
logger.LogInformation("The token {Identifier} was declared as inactive because " +
"it was revoked.", context.Ticket.GetTicketId());

42
src/OpenIddict/OpenIddictProvider.Revocation.cs

@ -15,10 +15,13 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using OpenIddict.Core;
namespace OpenIddict {
namespace OpenIddict
{
public partial class OpenIddictProvider<TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
where TApplication : class where TAuthorization : class where TScope : class where TToken : class {
public override async Task ValidateRevocationRequest([NotNull] ValidateRevocationRequestContext context) {
where TApplication : class where TAuthorization : class where TScope : class where TToken : class
{
public override async Task ValidateRevocationRequest([NotNull] ValidateRevocationRequestContext context)
{
var applications = context.HttpContext.RequestServices.GetRequiredService<OpenIddictApplicationManager<TApplication>>();
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<OpenIddictProvider<TApplication, TAuthorization, TScope, TToken>>>();
var options = context.HttpContext.RequestServices.GetRequiredService<IOptions<OpenIddictOptions>>();
@ -28,7 +31,8 @@ namespace OpenIddict {
// When token_type_hint is specified, reject the request if it doesn't correspond to a revocable token.
if (!string.IsNullOrEmpty(context.Request.TokenTypeHint) &&
!string.Equals(context.Request.TokenTypeHint, OpenIdConnectConstants.TokenTypeHints.AuthorizationCode) &&
!string.Equals(context.Request.TokenTypeHint, OpenIdConnectConstants.TokenTypeHints.RefreshToken)) {
!string.Equals(context.Request.TokenTypeHint, OpenIdConnectConstants.TokenTypeHints.RefreshToken))
{
context.Reject(
error: OpenIdConnectConstants.Errors.UnsupportedTokenType,
description: "Only authorization codes and refresh tokens can be revoked. When specifying a token_type_hint " +
@ -42,9 +46,11 @@ namespace OpenIddict {
// Note: the OpenID Connect server middleware will automatically ensure that
// the calling application cannot revoke a refresh token if it's not
// the intended audience, even if client authentication was skipped.
if (string.IsNullOrEmpty(context.ClientId)) {
if (string.IsNullOrEmpty(context.ClientId))
{
// Reject the request if client identification is mandatory.
if (options.Value.RequireClientIdentification) {
if (options.Value.RequireClientIdentification)
{
logger.LogError("The revocation request was rejected becaused the " +
"mandatory client_id parameter was missing or empty.");
@ -65,7 +71,8 @@ namespace OpenIddict {
// Retrieve the application details corresponding to the requested client_id.
var application = await applications.FindByClientIdAsync(context.ClientId, context.HttpContext.RequestAborted);
if (application == null) {
if (application == null)
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidClient,
description: "Application not found in the database: ensure that your client_id is correct.");
@ -74,9 +81,11 @@ namespace OpenIddict {
}
// Reject revocation requests containing a client_secret if the client application is not confidential.
if (await applications.IsPublicAsync(application, context.HttpContext.RequestAborted)) {
if (await applications.IsPublicAsync(application, context.HttpContext.RequestAborted))
{
// Reject tokens requests containing a client_secret when the client is a public application.
if (!string.IsNullOrEmpty(context.ClientSecret)) {
if (!string.IsNullOrEmpty(context.ClientSecret))
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidRequest,
description: "Public clients are not allowed to send a client_secret.");
@ -96,7 +105,8 @@ namespace OpenIddict {
// Confidential applications MUST authenticate
// to protect them from impersonation attacks.
if (string.IsNullOrEmpty(context.ClientSecret)) {
if (string.IsNullOrEmpty(context.ClientSecret))
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidClient,
description: "Missing credentials: ensure that you specified a client_secret.");
@ -104,7 +114,8 @@ namespace OpenIddict {
return;
}
if (!await applications.ValidateClientSecretAsync(application, context.ClientSecret, context.HttpContext.RequestAborted)) {
if (!await applications.ValidateClientSecretAsync(application, context.ClientSecret, context.HttpContext.RequestAborted))
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidClient,
description: "Invalid credentials: ensure that you specified a correct client_secret.");
@ -115,7 +126,8 @@ namespace OpenIddict {
context.Validate();
}
public override async Task HandleRevocationRequest([NotNull] HandleRevocationRequestContext context) {
public override async Task HandleRevocationRequest([NotNull] HandleRevocationRequestContext context)
{
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<OpenIddictProvider<TApplication, TAuthorization, TScope, TToken>>>();
var tokens = context.HttpContext.RequestServices.GetRequiredService<OpenIddictTokenManager<TToken>>();
@ -123,7 +135,8 @@ namespace OpenIddict {
// If the received token is not an authorization code or a refresh token,
// return an error to indicate that the token cannot be revoked.
if (!context.Ticket.IsAuthorizationCode() && !context.Ticket.IsRefreshToken()) {
if (!context.Ticket.IsAuthorizationCode() && !context.Ticket.IsRefreshToken())
{
logger.LogError("The revocation request was rejected because the token was not revocable.");
context.Reject(
@ -140,7 +153,8 @@ namespace OpenIddict {
// Retrieve the token from the database. If the token cannot be found,
// assume it is invalid and consider the revocation as successful.
var token = await tokens.FindByIdAsync(identifier, context.HttpContext.RequestAborted);
if (token == null) {
if (token == null)
{
logger.LogInformation("The token '{Identifier}' was already revoked.", identifier);
context.Revoked = true;

51
src/OpenIddict/OpenIddictProvider.Serialization.cs

@ -16,35 +16,42 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using OpenIddict.Core;
namespace OpenIddict {
namespace OpenIddict
{
public partial class OpenIddictProvider<TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
where TApplication : class where TAuthorization : class where TScope : class where TToken : class {
public override async Task SerializeAuthorizationCode([NotNull] SerializeAuthorizationCodeContext context) {
where TApplication : class where TAuthorization : class where TScope : class where TToken : class
{
public override async Task SerializeAuthorizationCode([NotNull] SerializeAuthorizationCodeContext context)
{
var applications = context.HttpContext.RequestServices.GetRequiredService<OpenIddictApplicationManager<TApplication>>();
var options = context.HttpContext.RequestServices.GetRequiredService<IOptions<OpenIddictOptions>>();
var tokens = context.HttpContext.RequestServices.GetRequiredService<OpenIddictTokenManager<TToken>>();
Debug.Assert(!string.IsNullOrEmpty(context.Request.ClientId), "The client identifier shouldn't be null or empty.");
if (!options.Value.DisableTokenRevocation) {
if (!options.Value.DisableTokenRevocation)
{
// Resolve the subject from the authentication ticket. If it cannot be found, throw an exception.
var subject = context.Ticket.Principal.GetClaim(OpenIdConnectConstants.Claims.Subject) ??
context.Ticket.Principal.GetClaim(ClaimTypes.NameIdentifier) ??
context.Ticket.Principal.GetClaim(ClaimTypes.Upn);
if (string.IsNullOrEmpty(subject)) {
if (string.IsNullOrEmpty(subject))
{
throw new InvalidOperationException("The subject associated with the authentication ticket cannot be retrieved.");
}
// If a null value was returned by CreateAsync, return immediately.
var token = await tokens.CreateAsync(OpenIdConnectConstants.TokenTypeHints.AuthorizationCode, subject, context.HttpContext.RequestAborted);
if (token == null) {
if (token == null)
{
return;
}
// Throw an exception if the token identifier can't be resolved.
var identifier = await tokens.GetIdAsync(token, context.HttpContext.RequestAborted);
if (string.IsNullOrEmpty(identifier)) {
if (string.IsNullOrEmpty(identifier))
{
throw new InvalidOperationException("The unique key associated with an authorization code cannot be null or empty.");
}
@ -54,7 +61,8 @@ namespace OpenIddict {
context.Ticket.SetTicketId(identifier);
var application = await applications.FindByClientIdAsync(context.Request.ClientId, context.HttpContext.RequestAborted);
if (application == null) {
if (application == null)
{
throw new InvalidOperationException("The client application cannot be retrieved from the database.");
}
@ -62,36 +70,42 @@ namespace OpenIddict {
// If an authorization identifier was specified, bind it to the token.
var authorization = context.Ticket.GetProperty(OpenIddictConstants.Properties.AuthorizationId);
if (!string.IsNullOrEmpty(authorization)) {
if (!string.IsNullOrEmpty(authorization))
{
await tokens.SetAuthorizationAsync(token, authorization, context.HttpContext.RequestAborted);
}
}
}
public override async Task SerializeRefreshToken([NotNull] SerializeRefreshTokenContext context) {
public override async Task SerializeRefreshToken([NotNull] SerializeRefreshTokenContext context)
{
var applications = context.HttpContext.RequestServices.GetRequiredService<OpenIddictApplicationManager<TApplication>>();
var options = context.HttpContext.RequestServices.GetRequiredService<IOptions<OpenIddictOptions>>();
var tokens = context.HttpContext.RequestServices.GetRequiredService<OpenIddictTokenManager<TToken>>();
if (!options.Value.DisableTokenRevocation) {
if (!options.Value.DisableTokenRevocation)
{
// Resolve the subject from the authentication ticket. If it cannot be found, throw an exception.
var subject = context.Ticket.Principal.GetClaim(OpenIdConnectConstants.Claims.Subject) ??
context.Ticket.Principal.GetClaim(ClaimTypes.NameIdentifier) ??
context.Ticket.Principal.GetClaim(ClaimTypes.Upn);
if (string.IsNullOrEmpty(subject)) {
if (string.IsNullOrEmpty(subject))
{
throw new InvalidOperationException("The subject associated with the authentication ticket cannot be retrieved.");
}
// If a null value was returned by CreateAsync, return immediately.
var token = await tokens.CreateAsync(OpenIdConnectConstants.TokenTypeHints.RefreshToken, subject, context.HttpContext.RequestAborted);
if (token == null) {
if (token == null)
{
return;
}
// Throw an exception if the token identifier can't be resolved.
var identifier = await tokens.GetIdAsync(token, context.HttpContext.RequestAborted);
if (string.IsNullOrEmpty(identifier)) {
if (string.IsNullOrEmpty(identifier))
{
throw new InvalidOperationException("The unique key associated with a refresh token cannot be null or empty.");
}
@ -101,9 +115,11 @@ namespace OpenIddict {
context.Ticket.SetTicketId(identifier);
// If the client application is known, associate it with the token.
if (!string.IsNullOrEmpty(context.Request.ClientId)) {
if (!string.IsNullOrEmpty(context.Request.ClientId))
{
var application = await applications.FindByClientIdAsync(context.Request.ClientId, context.HttpContext.RequestAborted);
if (application == null) {
if (application == null)
{
throw new InvalidOperationException("The client application cannot be retrieved from the database.");
}
@ -112,7 +128,8 @@ namespace OpenIddict {
// If an authorization identifier was specified, bind it to the token.
var authorization = context.Ticket.GetProperty(OpenIddictConstants.Properties.AuthorizationId);
if (!string.IsNullOrEmpty(authorization)) {
if (!string.IsNullOrEmpty(authorization))
{
await tokens.SetAuthorizationAsync(token, authorization, context.HttpContext.RequestAborted);
}
}

60
src/OpenIddict/OpenIddictProvider.Session.cs

@ -21,18 +21,23 @@ using Newtonsoft.Json.Bson;
using Newtonsoft.Json.Linq;
using OpenIddict.Core;
namespace OpenIddict {
namespace OpenIddict
{
public partial class OpenIddictProvider<TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
where TApplication : class where TAuthorization : class where TScope : class where TToken : class {
public override async Task ExtractLogoutRequest([NotNull] ExtractLogoutRequestContext context) {
where TApplication : class where TAuthorization : class where TScope : class where TToken : class
{
public override async Task ExtractLogoutRequest([NotNull] ExtractLogoutRequestContext context)
{
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<OpenIddictProvider<TApplication, TAuthorization, TScope, TToken>>>();
var options = context.HttpContext.RequestServices.GetRequiredService<IOptions<OpenIddictOptions>>();
// If a request_id parameter can be found in the logout request,
// restore the complete logout request from the distributed cache.
if (!string.IsNullOrEmpty(context.Request.RequestId)) {
if (!string.IsNullOrEmpty(context.Request.RequestId))
{
// Return an error if request caching support was not enabled.
if (!options.Value.EnableRequestCaching) {
if (!options.Value.EnableRequestCaching)
{
logger.LogError("The logout request was rejected because " +
"request caching support was not enabled.");
@ -48,7 +53,8 @@ namespace OpenIddict {
var key = OpenIddictConstants.Environment.LogoutRequest + context.Request.RequestId;
var payload = await options.Value.Cache.GetAsync(key);
if (payload == null) {
if (payload == null)
{
logger.LogError("The logout request was rejected because an unknown " +
"or invalid request_id parameter was specified.");
@ -60,10 +66,13 @@ namespace OpenIddict {
}
// Restore the logout request parameters from the serialized payload.
using (var reader = new BsonReader(new MemoryStream(payload))) {
foreach (var parameter in JObject.Load(reader)) {
using (var reader = new BsonReader(new MemoryStream(payload)))
{
foreach (var parameter in JObject.Load(reader))
{
// Avoid overriding the current request parameters.
if (context.Request.HasParameter(parameter.Key)) {
if (context.Request.HasParameter(parameter.Key))
{
continue;
}
@ -73,13 +82,15 @@ namespace OpenIddict {
}
}
public override async Task ValidateLogoutRequest([NotNull] ValidateLogoutRequestContext context) {
public override async Task ValidateLogoutRequest([NotNull] ValidateLogoutRequestContext context)
{
var applications = context.HttpContext.RequestServices.GetRequiredService<OpenIddictApplicationManager<TApplication>>();
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<OpenIddictProvider<TApplication, TAuthorization, TScope, TToken>>>();
// Skip validation if the optional post_logout_redirect_uri
// parameter was missing from the logout request.
if (string.IsNullOrEmpty(context.PostLogoutRedirectUri)) {
if (string.IsNullOrEmpty(context.PostLogoutRedirectUri))
{
logger.LogInformation("The logout request validation process was skipped because " +
"the post_logout_redirect_uri parameter was missing.");
@ -89,7 +100,8 @@ namespace OpenIddict {
}
var application = await applications.FindByLogoutRedirectUri(context.PostLogoutRedirectUri, context.HttpContext.RequestAborted);
if (application == null) {
if (application == null)
{
logger.LogError("The logout request was rejected because the client application corresponding " +
"to the specified post_logout_redirect_uri was not found in the database: " +
"'{PostLogoutRedirectUri}'.", context.PostLogoutRedirectUri);
@ -104,20 +116,23 @@ namespace OpenIddict {
context.Validate();
}
public override async Task HandleLogoutRequest([NotNull] HandleLogoutRequestContext context) {
public override async Task HandleLogoutRequest([NotNull] HandleLogoutRequestContext context)
{
var options = context.HttpContext.RequestServices.GetRequiredService<IOptions<OpenIddictOptions>>();
// If no request_id parameter can be found in the current request, assume the OpenID Connect
// request was not serialized yet and store the entire payload in the distributed cache
// to make it easier to flow across requests and internal/external logout workflows.
if (options.Value.EnableRequestCaching && string.IsNullOrEmpty(context.Request.RequestId)) {
if (options.Value.EnableRequestCaching && string.IsNullOrEmpty(context.Request.RequestId))
{
// Generate a request identifier. Note: using a crypto-secure
// random number generator is not necessary in this case.
context.Request.RequestId = Guid.NewGuid().ToString();
// Store the serialized logout request parameters in the distributed cache.
var stream = new MemoryStream();
using (var writer = new BsonWriter(stream)) {
using (var writer = new BsonWriter(stream))
{
writer.CloseOutput = false;
var serializer = JsonSerializer.CreateDefault();
@ -128,7 +143,8 @@ namespace OpenIddict {
// to avoid collisions with the other types of cached requests.
var key = OpenIddictConstants.Environment.LogoutRequest + context.Request.RequestId;
await options.Value.Cache.SetAsync(key, stream.ToArray(), new DistributedCacheEntryOptions {
await options.Value.Cache.SetAsync(key, stream.ToArray(), new DistributedCacheEntryOptions
{
AbsoluteExpiration = context.Options.SystemClock.UtcNow + TimeSpan.FromMinutes(30),
SlidingExpiration = TimeSpan.FromMinutes(10)
});
@ -149,11 +165,13 @@ namespace OpenIddict {
}
}
public override async Task ApplyLogoutResponse([NotNull] ApplyLogoutResponseContext context) {
public override async Task ApplyLogoutResponse([NotNull] ApplyLogoutResponseContext context)
{
var options = context.HttpContext.RequestServices.GetRequiredService<IOptions<OpenIddictOptions>>();
// Remove the logout request from the distributed cache.
if (options.Value.EnableRequestCaching && !string.IsNullOrEmpty(context.Request.RequestId)) {
if (options.Value.EnableRequestCaching && !string.IsNullOrEmpty(context.Request.RequestId))
{
// Note: the cache key is always prefixed with a specific marker
// to avoid collisions with the other types of cached requests.
var key = OpenIddictConstants.Environment.LogoutRequest + context.Request.RequestId;
@ -165,12 +183,14 @@ namespace OpenIddict {
}
if (!options.Value.ApplicationCanDisplayErrors && !string.IsNullOrEmpty(context.Response.Error) &&
string.IsNullOrEmpty(context.Response.PostLogoutRedirectUri)) {
string.IsNullOrEmpty(context.Response.PostLogoutRedirectUri))
{
// Determine if the status code pages middleware has been enabled for this request.
// If it was not registered or enabled, let the OpenID Connect server middleware render
// a default error page instead of delegating the rendering to the status code middleware.
var feature = context.HttpContext.Features.Get<IStatusCodePagesFeature>();
if (feature != null && feature.Enabled) {
if (feature != null && feature.Enabled)
{
// Replace the default status code by a 400 response.
context.HttpContext.Response.StatusCode = 400;

9
src/OpenIddict/OpenIddictProvider.Userinfo.cs

@ -8,10 +8,13 @@ using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations;
namespace OpenIddict {
namespace OpenIddict
{
public partial class OpenIddictProvider<TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
where TApplication : class where TAuthorization : class where TScope : class where TToken : class {
public override Task ExtractUserinfoRequest([NotNull] ExtractUserinfoRequestContext context) {
where TApplication : class where TAuthorization : class where TScope : class where TToken : class
{
public override Task ExtractUserinfoRequest([NotNull] ExtractUserinfoRequestContext context)
{
// Note: when enabling the userinfo endpoint, OpenIddict users are intended
// to handle the userinfo requests in their own code (e.g in a MVC controller).
// To avoid validating the access token twice, the default logic enforced by

6
src/OpenIddict/OpenIddictProvider.cs

@ -7,10 +7,12 @@
using System.ComponentModel;
using AspNet.Security.OpenIdConnect.Server;
namespace OpenIddict {
namespace OpenIddict
{
[EditorBrowsable(EditorBrowsableState.Never)]
public partial class OpenIddictProvider<TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
where TApplication : class where TAuthorization : class where TScope : class where TToken : class {
where TApplication : class where TAuthorization : class where TScope : class where TToken : class
{
// Note: this class is split into specialized partial classes.
}
}

5
test/OpenIddict.Core.Tests/OpenIddict.Core.Tests.xproj

@ -14,5 +14,8 @@
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
</Project>

54
test/OpenIddict.Core.Tests/OpenIddictBuilderTests.cs

@ -4,10 +4,13 @@ using Microsoft.Extensions.Logging;
using Moq;
using Xunit;
namespace OpenIddict.Core.Tests {
public class OpenIddictBuilderTests {
namespace OpenIddict.Core.Tests
{
public class OpenIddictBuilderTests
{
[Fact]
public void AddApplicationManager_ThrowsAnExceptionForInvalidManager() {
public void AddApplicationManager_ThrowsAnExceptionForInvalidManager()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -22,7 +25,8 @@ namespace OpenIddict.Core.Tests {
}
[Fact]
public void AddApplicationManager_OverridesDefaultManager() {
public void AddApplicationManager_OverridesDefaultManager()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -45,7 +49,8 @@ namespace OpenIddict.Core.Tests {
}
[Fact]
public void AddApplicationStore_ThrowsAnExceptionForInvalidStore() {
public void AddApplicationStore_ThrowsAnExceptionForInvalidStore()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -60,7 +65,8 @@ namespace OpenIddict.Core.Tests {
}
[Fact]
public void AddApplicationStore_OverridesDefaultManager() {
public void AddApplicationStore_OverridesDefaultManager()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -81,7 +87,8 @@ namespace OpenIddict.Core.Tests {
}
[Fact]
public void AddAuthorizationManager_ThrowsAnExceptionForInvalidManager() {
public void AddAuthorizationManager_ThrowsAnExceptionForInvalidManager()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -96,7 +103,8 @@ namespace OpenIddict.Core.Tests {
}
[Fact]
public void AddAuthorizationManager_OverridesDefaultManager() {
public void AddAuthorizationManager_OverridesDefaultManager()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -119,7 +127,8 @@ namespace OpenIddict.Core.Tests {
}
[Fact]
public void AddAuthorizationStore_ThrowsAnExceptionForInvalidStore() {
public void AddAuthorizationStore_ThrowsAnExceptionForInvalidStore()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -134,7 +143,8 @@ namespace OpenIddict.Core.Tests {
}
[Fact]
public void AddAuthorizationStore_OverridesDefaultManager() {
public void AddAuthorizationStore_OverridesDefaultManager()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -155,7 +165,8 @@ namespace OpenIddict.Core.Tests {
}
[Fact]
public void AddScopeManager_ThrowsAnExceptionForInvalidManager() {
public void AddScopeManager_ThrowsAnExceptionForInvalidManager()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -170,7 +181,8 @@ namespace OpenIddict.Core.Tests {
}
[Fact]
public void AddScopeManager_OverridesDefaultManager() {
public void AddScopeManager_OverridesDefaultManager()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -193,7 +205,8 @@ namespace OpenIddict.Core.Tests {
}
[Fact]
public void AddScopeStore_ThrowsAnExceptionForInvalidStore() {
public void AddScopeStore_ThrowsAnExceptionForInvalidStore()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -208,7 +221,8 @@ namespace OpenIddict.Core.Tests {
}
[Fact]
public void AddScopeStore_OverridesDefaultManager() {
public void AddScopeStore_OverridesDefaultManager()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -229,7 +243,8 @@ namespace OpenIddict.Core.Tests {
}
[Fact]
public void AddTokenManager_ThrowsAnExceptionForInvalidManager() {
public void AddTokenManager_ThrowsAnExceptionForInvalidManager()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -244,7 +259,8 @@ namespace OpenIddict.Core.Tests {
}
[Fact]
public void AddTokenManager_OverridesDefaultManager() {
public void AddTokenManager_OverridesDefaultManager()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -267,7 +283,8 @@ namespace OpenIddict.Core.Tests {
}
[Fact]
public void AddTokenStore_ThrowsAnExceptionForInvalidStore() {
public void AddTokenStore_ThrowsAnExceptionForInvalidStore()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -282,7 +299,8 @@ namespace OpenIddict.Core.Tests {
}
[Fact]
public void AddTokenStore_OverridesDefaultManager() {
public void AddTokenStore_OverridesDefaultManager()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();

15
test/OpenIddict.Core.Tests/OpenIddictExtensionsTests.cs

@ -3,14 +3,17 @@ using Microsoft.Extensions.DependencyInjection;
using OpenIddict.Models;
using Xunit;
namespace OpenIddict.Core.Tests {
public class OpenIddictExtensionsTests {
namespace OpenIddict.Core.Tests
{
public class OpenIddictExtensionsTests
{
[Theory]
[InlineData(typeof(OpenIddictApplicationManager<OpenIddictApplication>))]
[InlineData(typeof(OpenIddictAuthorizationManager<OpenIddictAuthorization>))]
[InlineData(typeof(OpenIddictScopeManager<OpenIddictScope>))]
[InlineData(typeof(OpenIddictTokenManager<OpenIddictToken>))]
public void AddOpenIddict_KeyTypeDefaultsToString(Type type) {
public void AddOpenIddict_KeyTypeDefaultsToString(Type type)
{
// Arrange
var services = new ServiceCollection();
@ -26,7 +29,8 @@ namespace OpenIddict.Core.Tests {
[InlineData(typeof(OpenIddictAuthorizationManager<OpenIddictAuthorization<Guid>>))]
[InlineData(typeof(OpenIddictScopeManager<OpenIddictScope<Guid>>))]
[InlineData(typeof(OpenIddictTokenManager<OpenIddictToken<Guid>>))]
public void AddOpenIddict_KeyTypeCanBeOverriden(Type type) {
public void AddOpenIddict_KeyTypeCanBeOverriden(Type type)
{
// Arrange
var services = new ServiceCollection();
@ -42,7 +46,8 @@ namespace OpenIddict.Core.Tests {
[InlineData(typeof(OpenIddictAuthorizationManager<object>))]
[InlineData(typeof(OpenIddictScopeManager<object>))]
[InlineData(typeof(OpenIddictTokenManager<object>))]
public void AddOpenIddict_DefaultEntitiesCanBeReplaced(Type type) {
public void AddOpenIddict_DefaultEntitiesCanBeReplaced(Type type)
{
// Arrange
var services = new ServiceCollection();

5
test/OpenIddict.EntityFrameworkCore.Tests/OpenIddict.EntityFrameworkCore.Tests.xproj

@ -4,7 +4,6 @@
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>7831f17a-df0b-42ec-841b-065a9b5bd786</ProjectGuid>
@ -13,9 +12,11 @@
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

45
test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictExtensionsTests.cs

@ -4,16 +4,20 @@ using Microsoft.Extensions.DependencyInjection;
using OpenIddict.Models;
using Xunit;
namespace OpenIddict.EntityFrameworkCore.Tests {
public class OpenIddictExtensionsTests {
namespace OpenIddict.EntityFrameworkCore.Tests
{
public class OpenIddictExtensionsTests
{
[Fact]
public void AddEntityFrameworkCoreStores_ThrowsAnExceptionForInvalidApplicationEntity() {
public void AddEntityFrameworkCoreStores_ThrowsAnExceptionForInvalidApplicationEntity()
{
// Arrange
var builder = new OpenIddictBuilder(new ServiceCollection());
builder.ApplicationType = typeof(object);
// Act and assert
var exception = Assert.Throws<InvalidOperationException>(delegate {
var exception = Assert.Throws<InvalidOperationException>(delegate
{
builder.AddEntityFrameworkCoreStores<DbContext>();
});
@ -22,13 +26,15 @@ namespace OpenIddict.EntityFrameworkCore.Tests {
}
[Fact]
public void AddEntityFrameworkCoreStores_ThrowsAnExceptionForInvalidAuthorizationEntity() {
public void AddEntityFrameworkCoreStores_ThrowsAnExceptionForInvalidAuthorizationEntity()
{
// Arrange
var builder = new OpenIddictBuilder(new ServiceCollection());
builder.AuthorizationType = typeof(object);
// Act and assert
var exception = Assert.Throws<InvalidOperationException>(delegate {
var exception = Assert.Throws<InvalidOperationException>(delegate
{
builder.AddEntityFrameworkCoreStores<DbContext>();
});
@ -37,13 +43,15 @@ namespace OpenIddict.EntityFrameworkCore.Tests {
}
[Fact]
public void AddEntityFrameworkCoreStores_ThrowsAnExceptionForInvalidScopeEntity() {
public void AddEntityFrameworkCoreStores_ThrowsAnExceptionForInvalidScopeEntity()
{
// Arrange
var builder = new OpenIddictBuilder(new ServiceCollection());
builder.ScopeType = typeof(object);
// Act and assert
var exception = Assert.Throws<InvalidOperationException>(delegate {
var exception = Assert.Throws<InvalidOperationException>(delegate
{
builder.AddEntityFrameworkCoreStores<DbContext>();
});
@ -52,13 +60,15 @@ namespace OpenIddict.EntityFrameworkCore.Tests {
}
[Fact]
public void AddEntityFrameworkCoreStores_ThrowsAnExceptionForInvalidTokenEntity() {
public void AddEntityFrameworkCoreStores_ThrowsAnExceptionForInvalidTokenEntity()
{
// Arrange
var builder = new OpenIddictBuilder(new ServiceCollection());
builder.TokenType = typeof(object);
// Act and assert
var exception = Assert.Throws<InvalidOperationException>(delegate {
var exception = Assert.Throws<InvalidOperationException>(delegate
{
builder.AddEntityFrameworkCoreStores<DbContext>();
});
@ -71,7 +81,8 @@ namespace OpenIddict.EntityFrameworkCore.Tests {
[InlineData(typeof(OpenIddictAuthorizationStore<OpenIddictAuthorization, OpenIddictApplication, OpenIddictToken, DbContext, string>))]
[InlineData(typeof(OpenIddictScopeStore<OpenIddictScope, DbContext, string>))]
[InlineData(typeof(OpenIddictTokenStore<OpenIddictToken, OpenIddictApplication, OpenIddictAuthorization, DbContext, string>))]
public void AddEntityFrameworkCoreStores_RegistersEntityFrameworkStores(Type type) {
public void AddEntityFrameworkCoreStores_RegistersEntityFrameworkStores(Type type)
{
// Arrange
var services = new ServiceCollection();
var builder = new OpenIddictBuilder(services);
@ -88,11 +99,13 @@ namespace OpenIddict.EntityFrameworkCore.Tests {
[InlineData(typeof(OpenIddictAuthorizationStore<OpenIddictAuthorization<Guid>, OpenIddictApplication<Guid>, OpenIddictToken<Guid>, DbContext, Guid>))]
[InlineData(typeof(OpenIddictScopeStore<OpenIddictScope<Guid>, DbContext, Guid>))]
[InlineData(typeof(OpenIddictTokenStore<OpenIddictToken<Guid>, OpenIddictApplication<Guid>, OpenIddictAuthorization<Guid>, DbContext, Guid>))]
public void AddEntityFrameworkCoreStores_KeyTypeIsInferredFromEntities(Type type) {
public void AddEntityFrameworkCoreStores_KeyTypeIsInferredFromEntities(Type type)
{
// Arrange
var services = new ServiceCollection();
var builder = new OpenIddictBuilder(services) {
var builder = new OpenIddictBuilder(services)
{
ApplicationType = typeof(OpenIddictApplication<Guid>),
AuthorizationType = typeof(OpenIddictAuthorization<Guid>),
ScopeType = typeof(OpenIddictScope<Guid>),
@ -111,11 +124,13 @@ namespace OpenIddict.EntityFrameworkCore.Tests {
[InlineData(typeof(OpenIddictAuthorizationStore<CustomAuthorization, CustomApplication, CustomToken, DbContext, long>))]
[InlineData(typeof(OpenIddictScopeStore<CustomScope, DbContext, long>))]
[InlineData(typeof(OpenIddictTokenStore<CustomToken, CustomApplication, CustomAuthorization, DbContext, long>))]
public void AddEntityFrameworkCoreStores_DefaultEntitiesCanBeReplaced(Type type) {
public void AddEntityFrameworkCoreStores_DefaultEntitiesCanBeReplaced(Type type)
{
// Arrange
var services = new ServiceCollection();
var builder = new OpenIddictBuilder(services) {
var builder = new OpenIddictBuilder(services)
{
ApplicationType = typeof(CustomApplication),
AuthorizationType = typeof(CustomAuthorization),
ScopeType = typeof(CustomScope),

7
test/OpenIddict.Mvc.Tests/OpenIddict.Mvc.Tests.xproj

@ -4,7 +4,6 @@
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>8b4b0ccc-711b-4f9d-9de6-dd32bdd3bcca</ProjectGuid>
@ -13,9 +12,11 @@
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
</Project>

9
test/OpenIddict.Mvc.Tests/OpenIddictExtensionsTests.cs

@ -3,10 +3,13 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Xunit;
namespace OpenIddict.Mvc.Tests {
public class OpenIddictExtensionsTests {
namespace OpenIddict.Mvc.Tests
{
public class OpenIddictExtensionsTests
{
[Fact]
public void AddMvcBinders_RegistersModelBinderProvider() {
public void AddMvcBinders_RegistersModelBinderProvider()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();

66
test/OpenIddict.Mvc.Tests/OpenIddictModelBinderTests.cs

@ -12,23 +12,28 @@ using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Moq;
using Xunit;
namespace OpenIddict.Mvc.Tests {
public class OpenIddictModelBinderTests {
namespace OpenIddict.Mvc.Tests
{
public class OpenIddictModelBinderTests
{
[Theory]
[InlineData(typeof(object))]
[InlineData(typeof(IList<int>))]
[InlineData(typeof(int[]))]
public async Task BindModelAsync_ThrowsAnExceptionForUnsupportedTypes(Type type) {
public async Task BindModelAsync_ThrowsAnExceptionForUnsupportedTypes(Type type)
{
// Arrange
var binder = new OpenIddictModelBinder();
var provider = new EmptyModelMetadataProvider();
var context = new DefaultModelBindingContext {
var context = new DefaultModelBindingContext
{
ModelMetadata = provider.GetMetadataForType(type)
};
// Act and assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(delegate {
var exception = await Assert.ThrowsAsync<InvalidOperationException>(delegate
{
return binder.BindModelAsync(context);
});
@ -36,13 +41,16 @@ namespace OpenIddict.Mvc.Tests {
}
[Fact]
public async Task BindModelAsync_ThrowsAnExceptionWhenRequestCannotBeFound() {
public async Task BindModelAsync_ThrowsAnExceptionWhenRequestCannotBeFound()
{
// Arrange
var binder = new OpenIddictModelBinder();
var provider = new EmptyModelMetadataProvider();
var context = new DefaultModelBindingContext {
ActionContext = new ActionContext() {
var context = new DefaultModelBindingContext
{
ActionContext = new ActionContext()
{
HttpContext = new DefaultHttpContext(),
},
@ -50,7 +58,8 @@ namespace OpenIddict.Mvc.Tests {
};
// Act and assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(delegate {
var exception = await Assert.ThrowsAsync<InvalidOperationException>(delegate
{
return binder.BindModelAsync(context);
});
@ -61,13 +70,16 @@ namespace OpenIddict.Mvc.Tests {
}
[Fact]
public async Task BindModelAsync_ReturnsNullWhenResponseCannotBeFound() {
public async Task BindModelAsync_ReturnsNullWhenResponseCannotBeFound()
{
// Arrange
var binder = new OpenIddictModelBinder();
var provider = new EmptyModelMetadataProvider();
var context = new DefaultModelBindingContext {
ActionContext = new ActionContext() {
var context = new DefaultModelBindingContext
{
ActionContext = new ActionContext()
{
HttpContext = new DefaultHttpContext(),
},
@ -85,7 +97,8 @@ namespace OpenIddict.Mvc.Tests {
}
[Fact]
public async Task BindModelAsync_ReturnsAmbientRequest() {
public async Task BindModelAsync_ReturnsAmbientRequest()
{
// Arrange
var binder = new OpenIddictModelBinder();
var provider = new EmptyModelMetadataProvider();
@ -93,12 +106,15 @@ namespace OpenIddict.Mvc.Tests {
var request = new OpenIdConnectRequest();
var features = new FeatureCollection();
features.Set(new OpenIdConnectServerFeature {
features.Set(new OpenIdConnectServerFeature
{
Request = request
});
var context = new DefaultModelBindingContext {
ActionContext = new ActionContext() {
var context = new DefaultModelBindingContext
{
ActionContext = new ActionContext()
{
HttpContext = new DefaultHttpContext(features),
},
@ -117,7 +133,8 @@ namespace OpenIddict.Mvc.Tests {
}
[Fact]
public async Task BindModelAsync_ReturnsAmbientResponse() {
public async Task BindModelAsync_ReturnsAmbientResponse()
{
// Arrange
var binder = new OpenIddictModelBinder();
var provider = new EmptyModelMetadataProvider();
@ -125,12 +142,15 @@ namespace OpenIddict.Mvc.Tests {
var response = new OpenIdConnectResponse();
var features = new FeatureCollection();
features.Set(new OpenIdConnectServerFeature {
features.Set(new OpenIdConnectServerFeature
{
Response = response
});
var context = new DefaultModelBindingContext {
ActionContext = new ActionContext() {
var context = new DefaultModelBindingContext
{
ActionContext = new ActionContext()
{
HttpContext = new DefaultHttpContext(features),
},
@ -152,7 +172,8 @@ namespace OpenIddict.Mvc.Tests {
[InlineData(typeof(object))]
[InlineData(typeof(IList<int>))]
[InlineData(typeof(int[]))]
public void GetBinder_ReturnsNullForUnsupportedTypes(Type type) {
public void GetBinder_ReturnsNullForUnsupportedTypes(Type type)
{
// Arrange
var provider = new OpenIddictModelBinder();
@ -172,7 +193,8 @@ namespace OpenIddict.Mvc.Tests {
[Theory]
[InlineData(typeof(OpenIdConnectRequest))]
[InlineData(typeof(OpenIdConnectResponse))]
public void GetBinder_ReturnsNonNullForSupportedTypes(Type type) {
public void GetBinder_ReturnsNonNullForSupportedTypes(Type type)
{
// Arrange
var binder = new OpenIddictModelBinder();

7
test/OpenIddict.Tests/OpenIddict.Tests.xproj

@ -4,7 +4,6 @@
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>3e2fbdb3-dc82-4e97-8ebc-cc8b279110ff</ProjectGuid>
@ -13,9 +12,11 @@
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
</Project>

120
test/OpenIddict.Tests/OpenIddictExtensionsTests.cs

@ -12,10 +12,13 @@ using Microsoft.IdentityModel.Tokens;
using Moq;
using Xunit;
namespace OpenIddict.Tests {
public class OpenIddictExtensionsTests {
namespace OpenIddict.Tests
{
public class OpenIddictExtensionsTests
{
[Fact]
public void UseOpenIddict_ThrowsAnExceptionWhenServicesAreNotRegistered() {
public void UseOpenIddict_ThrowsAnExceptionWhenServicesAreNotRegistered()
{
// Arrange
var services = new ServiceCollection();
var builder = new ApplicationBuilder(services.BuildServiceProvider());
@ -28,7 +31,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void UseOpenIddict_ThrowsAnExceptionWhenNoDistributedCacheIsRegisteredIfRequestCachingIsEnabled() {
public void UseOpenIddict_ThrowsAnExceptionWhenNoDistributedCacheIsRegisteredIfRequestCachingIsEnabled()
{
// Arrange
var services = new ServiceCollection();
@ -45,7 +49,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void UseOpenIddict_ThrowsAnExceptionWhenNoFlowIsEnabled() {
public void UseOpenIddict_ThrowsAnExceptionWhenNoFlowIsEnabled()
{
// Arrange
var services = new ServiceCollection();
services.AddOpenIddict();
@ -61,7 +66,8 @@ namespace OpenIddict.Tests {
[Theory]
[InlineData(OpenIdConnectConstants.GrantTypes.AuthorizationCode)]
[InlineData(OpenIdConnectConstants.GrantTypes.Implicit)]
public void UseOpenIddict_ThrowsAnExceptionWhenAuthorizationEndpointIsDisabled(string flow) {
public void UseOpenIddict_ThrowsAnExceptionWhenAuthorizationEndpointIsDisabled(string flow)
{
// Arrange
var services = new ServiceCollection();
@ -83,7 +89,8 @@ namespace OpenIddict.Tests {
[InlineData(OpenIdConnectConstants.GrantTypes.ClientCredentials)]
[InlineData(OpenIdConnectConstants.GrantTypes.Password)]
[InlineData(OpenIdConnectConstants.GrantTypes.RefreshToken)]
public void UseOpenIddict_ThrowsAnExceptionWhenTokenEndpointIsDisabled(string flow) {
public void UseOpenIddict_ThrowsAnExceptionWhenTokenEndpointIsDisabled(string flow)
{
// Arrange
var services = new ServiceCollection();
@ -102,7 +109,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void UseOpenIddict_ThrowsAnExceptionWhenTokenRevocationIsDisabled() {
public void UseOpenIddict_ThrowsAnExceptionWhenTokenRevocationIsDisabled()
{
// Arrange
var services = new ServiceCollection();
@ -121,7 +129,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void UseOpenIddict_ThrowsAnExceptionWhenNoSigningKeyIsRegisteredIfTheImplicitFlowIsEnabled() {
public void UseOpenIddict_ThrowsAnExceptionWhenNoSigningKeyIsRegisteredIfTheImplicitFlowIsEnabled()
{
// Arrange
var services = new ServiceCollection();
@ -140,7 +149,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void Configure_OptionsAreCorrectlyAmended() {
public void Configure_OptionsAreCorrectlyAmended()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -158,7 +168,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void UseOpenIddict_OpenIdConnectServerMiddlewareIsRegistered() {
public void UseOpenIddict_OpenIdConnectServerMiddlewareIsRegistered()
{
// Arrange
var services = new ServiceCollection();
@ -182,7 +193,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void AddEphemeralSigningKey_SigningKeyIsCorrectlyAdded() {
public void AddEphemeralSigningKey_SigningKeyIsCorrectlyAdded()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -208,7 +220,8 @@ namespace OpenIddict.Tests {
[InlineData(SecurityAlgorithms.EcdsaSha384Signature)]
[InlineData(SecurityAlgorithms.EcdsaSha512Signature)]
#endif
public void AddEphemeralSigningKey_SigningCredentialsUseSpecifiedAlgorithm(string algorithm) {
public void AddEphemeralSigningKey_SigningCredentialsUseSpecifiedAlgorithm(string algorithm)
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -234,7 +247,8 @@ namespace OpenIddict.Tests {
[InlineData(SecurityAlgorithms.EcdsaSha384Signature)]
[InlineData(SecurityAlgorithms.EcdsaSha512Signature)]
#endif
public void AddSigningKey_SigningKeyIsCorrectlyAdded(string algorithm) {
public void AddSigningKey_SigningKeyIsCorrectlyAdded(string algorithm)
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -257,7 +271,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void AddSigningCertificate_SigningKeyIsCorrectlyAdded() {
public void AddSigningCertificate_SigningKeyIsCorrectlyAdded()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -278,7 +293,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void AllowAuthorizationCodeFlow_CodeFlowIsAddedToGrantTypes() {
public void AllowAuthorizationCodeFlow_CodeFlowIsAddedToGrantTypes()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -296,7 +312,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void AllowClientCredentialsFlow_ClientCredentialsFlowIsAddedToGrantTypes() {
public void AllowClientCredentialsFlow_ClientCredentialsFlowIsAddedToGrantTypes()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -314,7 +331,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void AllowCustomFlow_CustomFlowIsAddedToGrantTypes() {
public void AllowCustomFlow_CustomFlowIsAddedToGrantTypes()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -332,7 +350,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void AllowImplicitFlow_ImplicitFlowIsAddedToGrantTypes() {
public void AllowImplicitFlow_ImplicitFlowIsAddedToGrantTypes()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -350,7 +369,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void AllowPasswordFlow_PasswordFlowIsAddedToGrantTypes() {
public void AllowPasswordFlow_PasswordFlowIsAddedToGrantTypes()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -368,7 +388,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void AllowRefreshTokenFlow_RefreshTokenFlowIsAddedToGrantTypes() {
public void AllowRefreshTokenFlow_RefreshTokenFlowIsAddedToGrantTypes()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -386,7 +407,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void DisableConfigurationEndpoint_ConfigurationEndpointIsDisabled() {
public void DisableConfigurationEndpoint_ConfigurationEndpointIsDisabled()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -404,7 +426,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void DisableCryptographyEndpoint_CryptographyEndpointIsDisabled() {
public void DisableCryptographyEndpoint_CryptographyEndpointIsDisabled()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -422,7 +445,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void DisableSlidingExpiration_SlidingExpirationIsDisabled() {
public void DisableSlidingExpiration_SlidingExpirationIsDisabled()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -440,7 +464,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void DisableTokenRevocation_TokenRevocationIsDisabled() {
public void DisableTokenRevocation_TokenRevocationIsDisabled()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -458,7 +483,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void EnableAuthorizationEndpoint_AuthorizationEndpointIsEnabled() {
public void EnableAuthorizationEndpoint_AuthorizationEndpointIsEnabled()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -476,7 +502,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void EnableIntrospectionEndpoint_IntrospectionEndpointIsEnabled() {
public void EnableIntrospectionEndpoint_IntrospectionEndpointIsEnabled()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -494,7 +521,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void EnableLogoutEndpoint_LogoutEndpointIsEnabled() {
public void EnableLogoutEndpoint_LogoutEndpointIsEnabled()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -512,7 +540,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void EnableRequestCaching_RequestCachingIsEnabled() {
public void EnableRequestCaching_RequestCachingIsEnabled()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -530,7 +559,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void EnableRevocationEndpoint_RevocationEndpointIsEnabled() {
public void EnableRevocationEndpoint_RevocationEndpointIsEnabled()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -548,7 +578,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void EnableTokenEndpoint_TokenEndpointIsEnabled() {
public void EnableTokenEndpoint_TokenEndpointIsEnabled()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -566,7 +597,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void EnableUserinfoEndpoint_UserinfoEndpointIsEnabled() {
public void EnableUserinfoEndpoint_UserinfoEndpointIsEnabled()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -584,7 +616,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void RequireClientIdentification_ClientIdentificationIsEnforced() {
public void RequireClientIdentification_ClientIdentificationIsEnforced()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -602,7 +635,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void SetAccessTokenLifetime_DefaultAccessTokenLifetimeIsReplaced() {
public void SetAccessTokenLifetime_DefaultAccessTokenLifetimeIsReplaced()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -620,7 +654,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void SetAuthorizationCodeLifetime_DefaultAuthorizationCodeLifetimeIsReplaced() {
public void SetAuthorizationCodeLifetime_DefaultAuthorizationCodeLifetimeIsReplaced()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -638,7 +673,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void SetIdentityTokenLifetime_DefaultIdentityTokenLifetimeIsReplaced() {
public void SetIdentityTokenLifetime_DefaultIdentityTokenLifetimeIsReplaced()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -656,7 +692,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void SetRefreshTokenLifetime_DefaultRefreshTokenLifetimeIsReplaced() {
public void SetRefreshTokenLifetime_DefaultRefreshTokenLifetimeIsReplaced()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -674,7 +711,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void SetIssuer_AddressIsReplaced() {
public void SetIssuer_AddressIsReplaced()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -692,7 +730,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void UseDataProtectionProvider_DefaultProviderIsReplaced() {
public void UseDataProtectionProvider_DefaultProviderIsReplaced()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();
@ -710,7 +749,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public void UseJsonWebTokens_AccessTokenHandlerIsCorrectlySet() {
public void UseJsonWebTokens_AccessTokenHandlerIsCorrectlySet()
{
// Arrange
var services = new ServiceCollection();
services.AddOptions();

192
test/OpenIddict.Tests/OpenIddictProviderTests.Authentication.cs

@ -14,17 +14,21 @@ using OpenIddict.Core;
using OpenIddict.Models;
using Xunit;
namespace OpenIddict.Tests {
public partial class OpenIddictProviderTests {
namespace OpenIddict.Tests
{
public partial class OpenIddictProviderTests
{
[Fact]
public async Task ExtractAuthorizationRequest_UnsupportedRequestParameterIsRejected() {
public async Task ExtractAuthorizationRequest_UnsupportedRequestParameterIsRejected()
{
// Arrange
var server = CreateAuthorizationServer();
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
RedirectUri = "http://www.fabrikam.com/path",
Request = "eyJhbGciOiJub25lIn0.eyJpc3MiOiJodHRwOi8vd3d3LmZhYnJpa2FtLmNvbSIsImF1ZCI6Imh0" +
@ -40,14 +44,16 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ExtractAuthorizationRequest_UnsupportedRequestUriParameterIsRejected() {
public async Task ExtractAuthorizationRequest_UnsupportedRequestUriParameterIsRejected()
{
// Arrange
var server = CreateAuthorizationServer();
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
RedirectUri = "http://www.fabrikam.com/path",
RequestUri = "http://www.fabrikam.com/request/GkurKxf5T0Y-mnPFCHqWOMiZi4VS138cQO_V7PZHAdM",
@ -61,14 +67,16 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ExtractAuthorizationRequest_RequestIdParameterIsRejectedWhenRequestCachingIsDisabled() {
public async Task ExtractAuthorizationRequest_RequestIdParameterIsRejectedWhenRequestCachingIsDisabled()
{
// Arrange
var server = CreateAuthorizationServer();
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest
{
RequestId = "EFAF3596-F868-497F-96BB-AA2AD1F8B7E7"
});
@ -78,9 +86,11 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ExtractAuthorizationRequest_InvalidRequestIdParameterIsRejected() {
public async Task ExtractAuthorizationRequest_InvalidRequestIdParameterIsRejected()
{
// Arrange
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddDistributedMemoryCache();
builder.EnableRequestCaching();
@ -89,7 +99,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest
{
RequestId = "EFAF3596-F868-497F-96BB-AA2AD1F8B7E7"
});
@ -99,14 +110,16 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ValidateAuthorizationRequest_UnknownResponseTypeParameterIsRejected() {
public async Task ValidateAuthorizationRequest_UnknownResponseTypeParameterIsRejected()
{
// Arrange
var server = CreateAuthorizationServer();
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
RedirectUri = "http://www.fabrikam.com/path",
ResponseType = "unknown_response_type"
@ -128,16 +141,19 @@ namespace OpenIddict.Tests {
[InlineData(OpenIdConnectConstants.GrantTypes.Implicit, "id_token")]
[InlineData(OpenIdConnectConstants.GrantTypes.Implicit, "id_token token")]
[InlineData(OpenIdConnectConstants.GrantTypes.Implicit, "token")]
public async Task ValidateAuthorizationRequest_RequestIsRejectedWhenCorrespondingFlowIsDisabled(string flow, string type) {
public async Task ValidateAuthorizationRequest_RequestIsRejectedWhenCorrespondingFlowIsDisabled(string flow, string type)
{
// Arrange
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Configure(options => options.GrantTypes.Remove(flow));
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
Nonce = "n-0S6_WzA2Mj",
RedirectUri = "http://www.fabrikam.com/path",
@ -151,16 +167,19 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ValidateAuthorizationRequest_RequestWithOfflineAccessScopeIsRejectedWhenRefreshTokenFlowIsDisabled() {
public async Task ValidateAuthorizationRequest_RequestWithOfflineAccessScopeIsRejectedWhenRefreshTokenFlowIsDisabled()
{
// Arrange
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Configure(options => options.GrantTypes.Remove(OpenIdConnectConstants.GrantTypes.RefreshToken));
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
RedirectUri = "http://www.fabrikam.com/path",
ResponseType = OpenIdConnectConstants.ResponseTypes.Code,
@ -173,14 +192,16 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ValidateAuthorizationRequest_UnknownResponseModeParameterIsRejected() {
public async Task ValidateAuthorizationRequest_UnknownResponseModeParameterIsRejected()
{
// Arrange
var server = CreateAuthorizationServer();
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
RedirectUri = "http://www.fabrikam.com/path",
ResponseMode = "unknown_response_mode",
@ -193,14 +214,16 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ValidateAuthorizationRequest_RequestIsRejectedWhenRedirectUriIsMissing() {
public async Task ValidateAuthorizationRequest_RequestIsRejectedWhenRedirectUriIsMissing()
{
// Arrange
var server = CreateAuthorizationServer();
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
RedirectUri = null,
ResponseType = OpenIdConnectConstants.ResponseTypes.Code
@ -212,14 +235,16 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ValidateAuthorizationRequest_RequestIsRejectedWhenCodeChallengeMethodIsMissing() {
public async Task ValidateAuthorizationRequest_RequestIsRejectedWhenCodeChallengeMethodIsMissing()
{
// Arrange
var server = CreateAuthorizationServer();
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
CodeChallenge = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM",
CodeChallengeMethod = null,
@ -233,14 +258,16 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ValidateAuthorizationRequest_RequestIsRejectedWhenCodeChallengeMethodIsPlain() {
public async Task ValidateAuthorizationRequest_RequestIsRejectedWhenCodeChallengeMethodIsPlain()
{
// Arrange
var server = CreateAuthorizationServer();
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
CodeChallenge = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM",
CodeChallengeMethod = OpenIdConnectConstants.CodeChallengeMethods.Plain,
@ -256,14 +283,16 @@ namespace OpenIddict.Tests {
[Theory]
[InlineData("code id_token token")]
[InlineData("code token")]
public async Task ValidateAuthorizationRequest_CodeChallengeRequestWithForbiddenResponseTypeIsRejected(string type) {
public async Task ValidateAuthorizationRequest_CodeChallengeRequestWithForbiddenResponseTypeIsRejected(string type)
{
// Arrange
var server = CreateAuthorizationServer();
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
CodeChallenge = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM",
CodeChallengeMethod = OpenIdConnectConstants.CodeChallengeMethods.Sha256,
@ -279,21 +308,25 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ValidateAuthorizationRequest_RequestIsRejectedWhenClientCannotBeFound() {
public async Task ValidateAuthorizationRequest_RequestIsRejectedWhenClientCannotBeFound()
{
// Arrange
var manager = CreateApplicationManager(instance => {
var manager = CreateApplicationManager(instance =>
{
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(null);
});
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(manager);
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
RedirectUri = "http://www.fabrikam.com/path",
ResponseType = OpenIdConnectConstants.ResponseTypes.Code
@ -307,11 +340,13 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ValidateAuthorizationRequest_RequestIsRejectedWhenClientHasNoRedirectUri() {
public async Task ValidateAuthorizationRequest_RequestIsRejectedWhenClientHasNoRedirectUri()
{
// Arrange
var application = new OpenIddictApplication();
var manager = CreateApplicationManager(instance => {
var manager = CreateApplicationManager(instance =>
{
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
@ -319,14 +354,16 @@ namespace OpenIddict.Tests {
.ReturnsAsync(false);
});
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(manager);
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
RedirectUri = "http://www.fabrikam.com/path",
ResponseType = OpenIdConnectConstants.ResponseTypes.Code
@ -341,11 +378,13 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ValidateAuthorizationRequest_RequestIsRejectedWhenRedirectUriIsInvalid() {
public async Task ValidateAuthorizationRequest_RequestIsRejectedWhenRedirectUriIsInvalid()
{
// Arrange
var application = new OpenIddictApplication();
var manager = CreateApplicationManager(instance => {
var manager = CreateApplicationManager(instance =>
{
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
@ -356,14 +395,16 @@ namespace OpenIddict.Tests {
.ReturnsAsync(false);
});
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(manager);
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
RedirectUri = "http://www.fabrikam.com/path",
ResponseType = OpenIdConnectConstants.ResponseTypes.Code
@ -383,11 +424,13 @@ namespace OpenIddict.Tests {
[InlineData("code token")]
[InlineData("id_token token")]
[InlineData("token")]
public async Task ValidateAuthorizationRequest_ImplicitOrHybridRequestIsRejectedWhenClientIsConfidential(string type) {
public async Task ValidateAuthorizationRequest_ImplicitOrHybridRequestIsRejectedWhenClientIsConfidential(string type)
{
// Arrange
var application = new OpenIddictApplication();
var manager = CreateApplicationManager(instance => {
var manager = CreateApplicationManager(instance =>
{
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
@ -401,14 +444,16 @@ namespace OpenIddict.Tests {
.ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential);
});
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(manager);
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
Nonce = "n-0S6_WzA2Mj",
RedirectUri = "http://www.fabrikam.com/path",
@ -428,12 +473,15 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task HandleAuthorizationRequest_RequestIsPersistedInDistributedCache() {
public async Task HandleAuthorizationRequest_RequestIsPersistedInDistributedCache()
{
// Arrange
var cache = new Mock<IDistributedCache>();
var server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(CreateApplicationManager(instance =>
{
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
@ -457,7 +505,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
RedirectUri = "http://www.fabrikam.com/path",
ResponseType = OpenIdConnectConstants.ResponseTypes.Token
@ -483,10 +532,13 @@ namespace OpenIddict.Tests {
[InlineData("id_token")]
[InlineData("id_token token")]
[InlineData("token")]
public async Task HandleAuthorizationRequest_RequestsAreNotHandledLocally(string type) {
public async Task HandleAuthorizationRequest_RequestsAreNotHandledLocally(string type)
{
// Arrange
var server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(CreateApplicationManager(instance =>
{
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
@ -502,7 +554,8 @@ namespace OpenIddict.Tests {
.ReturnsAsync(OpenIddictConstants.ClientTypes.Public);
}));
builder.Services.AddSingleton(CreateTokenManager(instance => {
builder.Services.AddSingleton(CreateTokenManager(instance =>
{
var token = new OpenIddictToken();
instance.Setup(mock => mock.CreateAsync(OpenIdConnectConstants.TokenTypeHints.AuthorizationCode, "Bob le Magnifique", It.IsAny<CancellationToken>()))
@ -516,7 +569,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
Nonce = "n-0S6_WzA2Mj",
RedirectUri = "http://www.fabrikam.com/path",
@ -531,16 +585,19 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ApplyAuthorizationResponse_RequestIsRemovedFromDistributedCache() {
public async Task ApplyAuthorizationResponse_RequestIsRemovedFromDistributedCache()
{
// Arrange
var request = new OpenIdConnectRequest {
var request = new OpenIdConnectRequest
{
ClientId = "Fabrikam",
RedirectUri = "http://www.fabrikam.com/path",
ResponseType = OpenIdConnectConstants.ResponseTypes.Token
};
var stream = new MemoryStream();
using (var writer = new BsonWriter(stream)) {
using (var writer = new BsonWriter(stream))
{
writer.CloseOutput = false;
var serializer = JsonSerializer.CreateDefault();
@ -553,8 +610,10 @@ namespace OpenIddict.Tests {
"b2ee7815-5579-4ff7-86b0-ba671b939d96"))
.ReturnsAsync(stream.ToArray());
var server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(CreateApplicationManager(instance =>
{
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
@ -578,7 +637,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest
{
RequestId = "b2ee7815-5579-4ff7-86b0-ba671b939d96"
});
@ -591,10 +651,13 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ApplyAuthorizationResponse_ErroredRequestIsNotHandledLocallyWhenStatusCodeMiddlewareIsEnabled() {
public async Task ApplyAuthorizationResponse_ErroredRequestIsNotHandledLocallyWhenStatusCodeMiddlewareIsEnabled()
{
// Arrange
var server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(CreateApplicationManager(instance =>
{
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
@ -616,7 +679,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync("/authorize-status-code-middleware", new OpenIdConnectRequest {
var response = await client.PostAsync("/authorize-status-code-middleware", new OpenIdConnectRequest
{
ClientId = null,
RedirectUri = null,
ResponseType = null

36
test/OpenIddict.Tests/OpenIddictProviderTests.Discovery.cs

@ -10,10 +10,13 @@ using Newtonsoft.Json.Linq;
using OpenIddict.Core;
using Xunit;
namespace OpenIddict.Tests {
public partial class OpenIddictProviderTests {
namespace OpenIddict.Tests
{
public partial class OpenIddictProviderTests
{
[Fact]
public async Task HandleConfigurationRequest_PlainCodeChallengeMethodIsNotReturned() {
public async Task HandleConfigurationRequest_PlainCodeChallengeMethodIsNotReturned()
{
// Arrange
var server = CreateAuthorizationServer();
@ -34,10 +37,13 @@ namespace OpenIddict.Tests {
[InlineData(OpenIdConnectConstants.GrantTypes.Implicit)]
[InlineData(OpenIdConnectConstants.GrantTypes.Password)]
[InlineData(OpenIdConnectConstants.GrantTypes.RefreshToken)]
public async Task HandleConfigurationRequest_EnabledFlowsAreReturned(string flow) {
public async Task HandleConfigurationRequest_EnabledFlowsAreReturned(string flow)
{
// Arrange
var server = CreateAuthorizationServer(builder => {
builder.Configure(options => {
var server = CreateAuthorizationServer(builder =>
{
builder.Configure(options =>
{
options.GrantTypes.Clear();
options.GrantTypes.Add(flow);
});
@ -59,7 +65,8 @@ namespace OpenIddict.Tests {
[InlineData(OpenIdConnectConstants.Scopes.Email)]
[InlineData(OpenIdConnectConstants.Scopes.Phone)]
[InlineData(OpenIddictConstants.Scopes.Roles)]
public async Task HandleConfigurationRequest_StandardScopesAreExposed(string scope) {
public async Task HandleConfigurationRequest_StandardScopesAreExposed(string scope)
{
// Arrange
var server = CreateAuthorizationServer();
@ -73,7 +80,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task HandleConfigurationRequest_OfflineAccessScopeIsReturnedWhenRefreshTokenFlowIsEnabled() {
public async Task HandleConfigurationRequest_OfflineAccessScopeIsReturnedWhenRefreshTokenFlowIsEnabled()
{
// Arrange
var server = CreateAuthorizationServer();
@ -88,10 +96,13 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task HandleConfigurationRequest_OfflineAccessScopeIsReturnedWhenRefreshTokenFlowIsDisabled() {
public async Task HandleConfigurationRequest_OfflineAccessScopeIsReturnedWhenRefreshTokenFlowIsDisabled()
{
// Arrange
var server = CreateAuthorizationServer(builder => {
builder.Configure(options => {
var server = CreateAuthorizationServer(builder =>
{
builder.Configure(options =>
{
// Note: at least one flow must be enabled.
options.GrantTypes.Clear();
options.GrantTypes.Add(OpenIdConnectConstants.GrantTypes.AuthorizationCode);
@ -109,7 +120,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task HandleConfigurationRequest_ExternalProvidersAreCorrectlyReturned() {
public async Task HandleConfigurationRequest_ExternalProvidersAreCorrectlyReturned()
{
// Arrange
var server = CreateAuthorizationServer();

204
test/OpenIddict.Tests/OpenIddictProviderTests.Exchange.cs

@ -15,23 +15,28 @@ using OpenIddict.Core;
using OpenIddict.Models;
using Xunit;
namespace OpenIddict.Tests {
public partial class OpenIddictProviderTests {
namespace OpenIddict.Tests
{
public partial class OpenIddictProviderTests
{
[Theory]
[InlineData(OpenIdConnectConstants.GrantTypes.AuthorizationCode)]
[InlineData(OpenIdConnectConstants.GrantTypes.ClientCredentials)]
[InlineData(OpenIdConnectConstants.GrantTypes.Password)]
[InlineData(OpenIdConnectConstants.GrantTypes.RefreshToken)]
public async Task ValidateTokenRequest_RequestIsRejectedWhenFlowIsNotEnabled(string flow) {
public async Task ValidateTokenRequest_RequestIsRejectedWhenFlowIsNotEnabled(string flow)
{
// Arrange
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Configure(options => options.GrantTypes.Remove(flow));
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest
{
Code = "SplxlOBeZQQYbYS6WxSbIA",
GrantType = flow,
Username = "johndoe",
@ -45,16 +50,19 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ValidateTokenRequest_RequestWithOfflineAccessScopeIsRejectedWhenRefreshTokenFlowIsDisabled() {
public async Task ValidateTokenRequest_RequestWithOfflineAccessScopeIsRejectedWhenRefreshTokenFlowIsDisabled()
{
// Arrange
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Configure(options => options.GrantTypes.Remove(OpenIdConnectConstants.GrantTypes.RefreshToken));
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest
{
GrantType = OpenIdConnectConstants.GrantTypes.Password,
Username = "johndoe",
Password = "A3ddj3w",
@ -67,14 +75,16 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ValidateTokenRequest_ClientCredentialsRequestWithOfflineAccessScopeIsRejected() {
public async Task ValidateTokenRequest_ClientCredentialsRequestWithOfflineAccessScopeIsRejected()
{
// Arrange
var server = CreateAuthorizationServer();
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest
{
GrantType = OpenIdConnectConstants.GrantTypes.ClientCredentials,
Scope = OpenIdConnectConstants.Scopes.OfflineAccess
});
@ -87,14 +97,16 @@ namespace OpenIddict.Tests {
[Theory]
[InlineData("client_id", "")]
[InlineData("", "client_secret")]
public async Task ValidateTokenRequest_ClientCredentialsRequestIsRejectedWhenCredentialsAreMissing(string identifier, string secret) {
public async Task ValidateTokenRequest_ClientCredentialsRequestIsRejectedWhenCredentialsAreMissing(string identifier, string secret)
{
// Arrange
var server = CreateAuthorizationServer();
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest
{
ClientId = identifier,
ClientSecret = secret,
GrantType = OpenIdConnectConstants.GrantTypes.ClientCredentials
@ -106,14 +118,16 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ValidateTokenRequest_RequestWithoutClientIdIsRejectedWhenClientIdentificationIsRequired() {
public async Task ValidateTokenRequest_RequestWithoutClientIdIsRejectedWhenClientIdentificationIsRequired()
{
// Arrange
var server = CreateAuthorizationServer(builder => builder.RequireClientIdentification());
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest
{
ClientId = null,
GrantType = OpenIdConnectConstants.GrantTypes.Password,
Username = "johndoe",
@ -126,21 +140,25 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ValidateTokenRequest_RequestIsRejectedWhenClientCannotBeFound() {
public async Task ValidateTokenRequest_RequestIsRejectedWhenClientCannotBeFound()
{
// Arrange
var manager = CreateApplicationManager(instance => {
var manager = CreateApplicationManager(instance =>
{
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(null);
});
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(manager);
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
GrantType = OpenIdConnectConstants.GrantTypes.Password,
Username = "johndoe",
@ -155,11 +173,13 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ValidateTokenRequest_ClientCredentialsRequestFromPublicClientIsRejected() {
public async Task ValidateTokenRequest_ClientCredentialsRequestFromPublicClientIsRejected()
{
// Arrange
var application = new OpenIddictApplication();
var manager = CreateApplicationManager(instance => {
var manager = CreateApplicationManager(instance =>
{
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
@ -167,14 +187,16 @@ namespace OpenIddict.Tests {
.ReturnsAsync(OpenIddictConstants.ClientTypes.Public);
});
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(manager);
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
GrantType = OpenIdConnectConstants.GrantTypes.ClientCredentials
@ -189,11 +211,13 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ValidateTokenRequest_ClientSecretCannotBeUsedByPublicClients() {
public async Task ValidateTokenRequest_ClientSecretCannotBeUsedByPublicClients()
{
// Arrange
var application = new OpenIddictApplication();
var manager = CreateApplicationManager(instance => {
var manager = CreateApplicationManager(instance =>
{
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
@ -201,14 +225,16 @@ namespace OpenIddict.Tests {
.ReturnsAsync(OpenIddictConstants.ClientTypes.Public);
});
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(manager);
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
GrantType = OpenIdConnectConstants.GrantTypes.Password,
@ -225,11 +251,13 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ValidateTokenRequest_ClientSecretIsRequiredForConfidentialClients() {
public async Task ValidateTokenRequest_ClientSecretIsRequiredForConfidentialClients()
{
// Arrange
var application = new OpenIddictApplication();
var manager = CreateApplicationManager(instance => {
var manager = CreateApplicationManager(instance =>
{
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
@ -237,14 +265,16 @@ namespace OpenIddict.Tests {
.ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential);
});
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(manager);
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
ClientSecret = null,
GrantType = OpenIdConnectConstants.GrantTypes.Password,
@ -261,11 +291,13 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ValidateTokenRequest_RequestIsRejectedWhenClientCredentialsAreInvalid() {
public async Task ValidateTokenRequest_RequestIsRejectedWhenClientCredentialsAreInvalid()
{
// Arrange
var application = new OpenIddictApplication();
var manager = CreateApplicationManager(instance => {
var manager = CreateApplicationManager(instance =>
{
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
@ -276,14 +308,16 @@ namespace OpenIddict.Tests {
.ReturnsAsync(false);
});
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(manager);
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
GrantType = OpenIdConnectConstants.GrantTypes.Password,
@ -301,7 +335,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task HandleTokenRequest_AuthorizationCodeRevocationIsIgnoredWhenTokenRevocationIsDisabled() {
public async Task HandleTokenRequest_AuthorizationCodeRevocationIsIgnoredWhenTokenRevocationIsDisabled()
{
// Arrange
var ticket = new AuthenticationTicket(
new ClaimsPrincipal(),
@ -317,8 +352,10 @@ namespace OpenIddict.Tests {
format.Setup(mock => mock.Unprotect("SplxlOBeZQQYbYS6WxSbIA"))
.Returns(ticket);
var server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(CreateApplicationManager(instance =>
{
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
@ -337,7 +374,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
Code = "SplxlOBeZQQYbYS6WxSbIA",
GrantType = OpenIdConnectConstants.GrantTypes.AuthorizationCode
@ -348,7 +386,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task HandleTokenRequest_RefreshTokenRevocationIsIgnoredWhenTokenRevocationIsDisabled() {
public async Task HandleTokenRequest_RefreshTokenRevocationIsIgnoredWhenTokenRevocationIsDisabled()
{
// Arrange
var ticket = new AuthenticationTicket(
new ClaimsPrincipal(),
@ -363,8 +402,10 @@ namespace OpenIddict.Tests {
format.Setup(mock => mock.Unprotect("8xLOxBtZp8"))
.Returns(ticket);
var server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(CreateApplicationManager(instance =>
{
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
@ -383,7 +424,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest
{
GrantType = OpenIdConnectConstants.GrantTypes.RefreshToken,
RefreshToken = "8xLOxBtZp8"
});
@ -393,7 +435,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task HandleTokenRequest_RequestIsRejectedWhenAuthorizationCodeIsExpired() {
public async Task HandleTokenRequest_RequestIsRejectedWhenAuthorizationCodeIsExpired()
{
// Arrange
var ticket = new AuthenticationTicket(
new ClaimsPrincipal(),
@ -409,13 +452,16 @@ namespace OpenIddict.Tests {
format.Setup(mock => mock.Unprotect("SplxlOBeZQQYbYS6WxSbIA"))
.Returns(ticket);
var manager = CreateTokenManager(instance => {
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()))
.ReturnsAsync(null);
});
var server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(CreateApplicationManager(instance =>
{
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
@ -433,7 +479,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
Code = "SplxlOBeZQQYbYS6WxSbIA",
GrantType = OpenIdConnectConstants.GrantTypes.AuthorizationCode
@ -447,7 +494,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task HandleTokenRequest_RequestIsRejectedWhenRefreshTokenIsExpired() {
public async Task HandleTokenRequest_RequestIsRejectedWhenRefreshTokenIsExpired()
{
// Arrange
var ticket = new AuthenticationTicket(
new ClaimsPrincipal(),
@ -462,13 +510,16 @@ namespace OpenIddict.Tests {
format.Setup(mock => mock.Unprotect("8xLOxBtZp8"))
.Returns(ticket);
var manager = CreateTokenManager(instance => {
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()))
.ReturnsAsync(null);
});
var server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(CreateApplicationManager(instance =>
{
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
@ -486,7 +537,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest
{
GrantType = OpenIdConnectConstants.GrantTypes.RefreshToken,
RefreshToken = "8xLOxBtZp8"
});
@ -499,7 +551,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task HandleTokenRequest_AuthorizationCodeIsAutomaticallyRevoked() {
public async Task HandleTokenRequest_AuthorizationCodeIsAutomaticallyRevoked()
{
// Arrange
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
identity.AddClaim(ClaimTypes.NameIdentifier, "Bob le Bricoleur");
@ -520,13 +573,16 @@ namespace OpenIddict.Tests {
var token = new OpenIddictToken();
var manager = CreateTokenManager(instance => {
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
});
var server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(CreateApplicationManager(instance =>
{
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
@ -544,7 +600,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
Code = "SplxlOBeZQQYbYS6WxSbIA",
GrantType = OpenIdConnectConstants.GrantTypes.AuthorizationCode
@ -556,7 +613,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task HandleTokenRequest_RefreshTokenIsAutomaticallyRevokedWhenSlidingExpirationIsEnabled() {
public async Task HandleTokenRequest_RefreshTokenIsAutomaticallyRevokedWhenSlidingExpirationIsEnabled()
{
// Arrange
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
identity.AddClaim(ClaimTypes.NameIdentifier, "Bob le Bricoleur");
@ -576,13 +634,16 @@ namespace OpenIddict.Tests {
var token = new OpenIddictToken();
var manager = CreateTokenManager(instance => {
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
});
var server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(CreateApplicationManager(instance =>
{
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
@ -600,7 +661,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest
{
GrantType = OpenIdConnectConstants.GrantTypes.RefreshToken,
RefreshToken = "8xLOxBtZp8"
});
@ -616,7 +678,8 @@ namespace OpenIddict.Tests {
[InlineData(OpenIdConnectConstants.GrantTypes.Password)]
[InlineData(OpenIdConnectConstants.GrantTypes.RefreshToken)]
[InlineData("urn:ietf:params:oauth:grant-type:custom_grant")]
public async Task HandleTokenRequest_RequestsAreNotHandledLocally(string flow) {
public async Task HandleTokenRequest_RequestsAreNotHandledLocally(string flow)
{
// Arrange
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
identity.AddClaim(ClaimTypes.NameIdentifier, "Bob le Bricoleur");
@ -628,7 +691,8 @@ namespace OpenIddict.Tests {
ticket.SetTicketId("60FFF7EA-F98E-437B-937E-5073CC313103");
switch (flow) {
switch (flow)
{
case OpenIdConnectConstants.GrantTypes.AuthorizationCode:
ticket.SetUsage(OpenIdConnectConstants.Usages.AuthorizationCode);
ticket.SetPresenters("Fabrikam");
@ -646,13 +710,16 @@ namespace OpenIddict.Tests {
var token = new OpenIddictToken();
var manager = CreateTokenManager(instance => {
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
});
var server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(CreateApplicationManager(instance =>
{
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
@ -676,7 +743,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
Code = "8xLOxBtZp8",

120
test/OpenIddict.Tests/OpenIddictProviderTests.Introspection.cs

@ -16,17 +16,21 @@ using OpenIddict.Core;
using OpenIddict.Models;
using Xunit;
namespace OpenIddict.Tests {
public partial class OpenIddictProviderTests {
namespace OpenIddict.Tests
{
public partial class OpenIddictProviderTests
{
[Fact]
public async Task ExtractIntrospectionRequest_GetRequestsAreRejected() {
public async Task ExtractIntrospectionRequest_GetRequestsAreRejected()
{
// Arrange
var server = CreateAuthorizationServer();
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.GetAsync(IntrospectionEndpoint, new OpenIdConnectRequest {
var response = await client.GetAsync(IntrospectionEndpoint, new OpenIdConnectRequest
{
Token = "2YotnFZFEjr1zCsicMWpAA"
});
@ -38,14 +42,16 @@ namespace OpenIddict.Tests {
[Theory]
[InlineData("client_id", "")]
[InlineData("", "client_secret")]
public async Task ValidateIntrospectionRequest_ClientCredentialsRequestIsRejectedWhenCredentialsAreMissing(string identifier, string secret) {
public async Task ValidateIntrospectionRequest_ClientCredentialsRequestIsRejectedWhenCredentialsAreMissing(string identifier, string secret)
{
// Arrange
var server = CreateAuthorizationServer();
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest
{
ClientId = identifier,
ClientSecret = secret,
Token = "2YotnFZFEjr1zCsicMWpAA"
@ -57,21 +63,25 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ValidateIntrospectionRequest_RequestIsRejectedWhenClientCannotBeFound() {
public async Task ValidateIntrospectionRequest_RequestIsRejectedWhenClientCannotBeFound()
{
// Arrange
var manager = CreateApplicationManager(instance => {
var manager = CreateApplicationManager(instance =>
{
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(null);
});
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(manager);
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
Token = "2YotnFZFEjr1zCsicMWpAA"
@ -85,11 +95,13 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ValidateIntrospectionRequest_RequestsSentByPublicClientsAreRejected() {
public async Task ValidateIntrospectionRequest_RequestsSentByPublicClientsAreRejected()
{
// Arrange
var application = new OpenIddictApplication();
var manager = CreateApplicationManager(instance => {
var manager = CreateApplicationManager(instance =>
{
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
@ -97,14 +109,16 @@ namespace OpenIddict.Tests {
.ReturnsAsync(OpenIddictConstants.ClientTypes.Public);
});
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(manager);
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
Token = "2YotnFZFEjr1zCsicMWpAA"
@ -119,11 +133,13 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ValidateIntrospectionRequest_RequestIsRejectedWhenClientCredentialsAreInvalid() {
public async Task ValidateIntrospectionRequest_RequestIsRejectedWhenClientCredentialsAreInvalid()
{
// Arrange
var application = new OpenIddictApplication();
var manager = CreateApplicationManager(instance => {
var manager = CreateApplicationManager(instance =>
{
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
@ -134,14 +150,16 @@ namespace OpenIddict.Tests {
.ReturnsAsync(false);
});
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(manager);
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
Token = "2YotnFZFEjr1zCsicMWpAA"
@ -157,7 +175,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task HandleIntrospectionRequest_RequestIsRejectedWhenClientIsNotAValidAudience() {
public async Task HandleIntrospectionRequest_RequestIsRejectedWhenClientIsNotAValidAudience()
{
// Arrange
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
identity.AddClaim(ClaimTypes.NameIdentifier, "Bob le Bricoleur");
@ -176,8 +195,10 @@ namespace OpenIddict.Tests {
format.Setup(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"))
.Returns(ticket);
var server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(CreateApplicationManager(instance =>
{
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
@ -196,7 +217,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
Token = "2YotnFZFEjr1zCsicMWpAA"
@ -208,7 +230,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task HandleIntrospectionRequest_AuthorizationCodeRevocationIsIgnoredWhenTokenRevocationIsDisabled() {
public async Task HandleIntrospectionRequest_AuthorizationCodeRevocationIsIgnoredWhenTokenRevocationIsDisabled()
{
// Arrange
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
identity.AddClaim(ClaimTypes.NameIdentifier, "Bob le Bricoleur");
@ -226,8 +249,10 @@ namespace OpenIddict.Tests {
format.Setup(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"))
.Returns(ticket);
var server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(CreateApplicationManager(instance =>
{
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
@ -249,7 +274,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
Token = "2YotnFZFEjr1zCsicMWpAA"
@ -260,7 +286,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task HandleIntrospectionRequest_RefreshTokenRevocationIsIgnoredWhenTokenRevocationIsDisabled() {
public async Task HandleIntrospectionRequest_RefreshTokenRevocationIsIgnoredWhenTokenRevocationIsDisabled()
{
// Arrange
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
identity.AddClaim(ClaimTypes.NameIdentifier, "Bob le Bricoleur");
@ -278,8 +305,10 @@ namespace OpenIddict.Tests {
format.Setup(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"))
.Returns(ticket);
var server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(CreateApplicationManager(instance =>
{
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
@ -301,7 +330,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
Token = "2YotnFZFEjr1zCsicMWpAA"
@ -312,7 +342,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task HandleIntrospectionRequest_RequestIsRejectedWhenAuthorizationCodeIsRevoked() {
public async Task HandleIntrospectionRequest_RequestIsRejectedWhenAuthorizationCodeIsRevoked()
{
// Arrange
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
identity.AddClaim(ClaimTypes.NameIdentifier, "Bob le Bricoleur");
@ -330,13 +361,16 @@ namespace OpenIddict.Tests {
format.Setup(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"))
.Returns(ticket);
var manager = CreateTokenManager(instance => {
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()))
.ReturnsAsync(null);
});
var server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(CreateApplicationManager(instance =>
{
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
@ -357,7 +391,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
Token = "2YotnFZFEjr1zCsicMWpAA"
@ -371,7 +406,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task HandleIntrospectionRequest_RequestIsRejectedWhenRefreshTokenIsRevoked() {
public async Task HandleIntrospectionRequest_RequestIsRejectedWhenRefreshTokenIsRevoked()
{
// Arrange
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
identity.AddClaim(ClaimTypes.NameIdentifier, "Bob le Bricoleur");
@ -389,13 +425,16 @@ namespace OpenIddict.Tests {
format.Setup(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"))
.Returns(ticket);
var manager = CreateTokenManager(instance => {
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()))
.ReturnsAsync(null);
});
var server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(CreateApplicationManager(instance =>
{
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
@ -416,7 +455,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
Token = "2YotnFZFEjr1zCsicMWpAA"

108
test/OpenIddict.Tests/OpenIddictProviderTests.Revocation.cs

@ -18,19 +18,23 @@ using OpenIddict.Core;
using OpenIddict.Models;
using Xunit;
namespace OpenIddict.Tests {
public partial class OpenIddictProviderTests {
namespace OpenIddict.Tests
{
public partial class OpenIddictProviderTests
{
[Theory]
[InlineData(OpenIdConnectConstants.TokenTypeHints.AccessToken)]
[InlineData(OpenIdConnectConstants.TokenTypeHints.IdToken)]
public async Task ValidateRevocationRequest_UnknownTokenTokenHintIsRejected(string hint) {
public async Task ValidateRevocationRequest_UnknownTokenTokenHintIsRejected(string hint)
{
// Arrange
var server = CreateAuthorizationServer();
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest
{
Token = "SlAV32hkKG",
TokenTypeHint = hint
});
@ -42,14 +46,16 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ValidateRevocationRequest_RequestWithoutClientIdIsRejectedWhenClientIdentificationIsRequired() {
public async Task ValidateRevocationRequest_RequestWithoutClientIdIsRejectedWhenClientIdentificationIsRequired()
{
// Arrange
var server = CreateAuthorizationServer(builder => builder.RequireClientIdentification());
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest
{
Token = "SlAV32hkKG",
TokenTypeHint = OpenIdConnectConstants.TokenTypeHints.RefreshToken
});
@ -60,21 +66,25 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ValidateRevocationRequest_RequestIsRejectedWhenClientCannotBeFound() {
public async Task ValidateRevocationRequest_RequestIsRejectedWhenClientCannotBeFound()
{
// Arrange
var manager = CreateApplicationManager(instance => {
var manager = CreateApplicationManager(instance =>
{
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(null);
});
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(manager);
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
Token = "SlAV32hkKG",
TokenTypeHint = OpenIdConnectConstants.TokenTypeHints.RefreshToken
@ -88,11 +98,13 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ValidateRevocationRequest_ClientSecretCannotBeUsedByPublicClients() {
public async Task ValidateRevocationRequest_ClientSecretCannotBeUsedByPublicClients()
{
// Arrange
var application = new OpenIddictApplication();
var manager = CreateApplicationManager(instance => {
var manager = CreateApplicationManager(instance =>
{
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
@ -100,14 +112,16 @@ namespace OpenIddict.Tests {
.ReturnsAsync(OpenIddictConstants.ClientTypes.Public);
});
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(manager);
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
Token = "SlAV32hkKG",
@ -123,11 +137,13 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ValidateRevocationRequest_ClientSecretIsRequiredForConfidentialClients() {
public async Task ValidateRevocationRequest_ClientSecretIsRequiredForConfidentialClients()
{
// Arrange
var application = new OpenIddictApplication();
var manager = CreateApplicationManager(instance => {
var manager = CreateApplicationManager(instance =>
{
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
@ -135,14 +151,16 @@ namespace OpenIddict.Tests {
.ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential);
});
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(manager);
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
ClientSecret = null,
Token = "SlAV32hkKG",
@ -158,11 +176,13 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ValidateRevocationRequest_RequestIsRejectedWhenClientCredentialsAreInvalid() {
public async Task ValidateRevocationRequest_RequestIsRejectedWhenClientCredentialsAreInvalid()
{
// Arrange
var application = new OpenIddictApplication();
var manager = CreateApplicationManager(instance => {
var manager = CreateApplicationManager(instance =>
{
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
@ -173,14 +193,16 @@ namespace OpenIddict.Tests {
.ReturnsAsync(false);
});
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(manager);
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
Token = "SlAV32hkKG",
@ -197,7 +219,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task HandleRevocationRequest_RequestIsRejectedWhenTokenIsAnAccessToken() {
public async Task HandleRevocationRequest_RequestIsRejectedWhenTokenIsAnAccessToken()
{
// Arrange
var ticket = new AuthenticationTicket(
new ClaimsPrincipal(),
@ -212,14 +235,16 @@ namespace OpenIddict.Tests {
format.Setup(mock => mock.Unprotect("SlAV32hkKG"))
.Returns(ticket);
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Configure(options => options.AccessTokenFormat = format.Object);
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest
{
Token = "SlAV32hkKG"
});
@ -231,7 +256,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task HandleRevocationRequest_RequestIsNotRejectedWhenTokenIsAnIdentityToken() {
public async Task HandleRevocationRequest_RequestIsNotRejectedWhenTokenIsAnIdentityToken()
{
// Arrange
var token = Mock.Of<SecurityToken>(mock =>
mock.ValidFrom == DateTime.UtcNow.AddDays(-1) &&
@ -249,14 +275,16 @@ namespace OpenIddict.Tests {
.Setup(mock => mock.ValidateToken("SlAV32hkKG", It.IsAny<TokenValidationParameters>(), out token))
.Returns(new ClaimsPrincipal(identity));
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Configure(options => options.IdentityTokenHandler = handler.Object);
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest
{
Token = "SlAV32hkKG"
});
@ -272,7 +300,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task HandleRevocationRequest_TokenIsNotRevokedWhenItIsAlreadyInvalid() {
public async Task HandleRevocationRequest_TokenIsNotRevokedWhenItIsAlreadyInvalid()
{
// Arrange
var ticket = new AuthenticationTicket(
new ClaimsPrincipal(),
@ -287,12 +316,14 @@ namespace OpenIddict.Tests {
format.Setup(mock => mock.Unprotect("SlAV32hkKG"))
.Returns(ticket);
var manager = CreateTokenManager(instance => {
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()))
.ReturnsAsync(null);
});
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(manager);
builder.Configure(options => options.AccessTokenFormat = format.Object);
@ -301,7 +332,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest
{
Token = "SlAV32hkKG"
});
@ -313,7 +345,8 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task HandleRevocationRequest_TokenIsSuccessfullyRevoked() {
public async Task HandleRevocationRequest_TokenIsSuccessfullyRevoked()
{
// Arrange
var ticket = new AuthenticationTicket(
new ClaimsPrincipal(),
@ -330,12 +363,14 @@ namespace OpenIddict.Tests {
var token = new OpenIddictToken();
var manager = CreateTokenManager(instance => {
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
});
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(manager);
builder.Configure(options => options.AccessTokenFormat = format.Object);
@ -344,7 +379,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(RevocationEndpoint, new OpenIdConnectRequest
{
Token = "SlAV32hkKG"
});

117
test/OpenIddict.Tests/OpenIddictProviderTests.Serialization.cs

@ -10,15 +10,20 @@ using OpenIddict.Core;
using OpenIddict.Models;
using Xunit;
namespace OpenIddict.Tests {
public partial class OpenIddictProviderTests {
namespace OpenIddict.Tests
{
public partial class OpenIddictProviderTests
{
[Fact]
public async Task SerializeAuthorizationCode_AuthorizationCodeIsNotPersistedWhenRevocationIsDisabled() {
public async Task SerializeAuthorizationCode_AuthorizationCodeIsNotPersistedWhenRevocationIsDisabled()
{
// Arrange
var manager = CreateTokenManager();
var server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(CreateApplicationManager(instance =>
{
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
@ -44,7 +49,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
RedirectUri = "http://www.fabrikam.com/path",
ResponseType = OpenIdConnectConstants.ResponseTypes.Code
@ -57,11 +63,13 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task SerializeAuthorizationCode_AuthorizationCodeIsCorrectlyPersisted() {
public async Task SerializeAuthorizationCode_AuthorizationCodeIsCorrectlyPersisted()
{
// Arrange
var token = new OpenIddictToken();
var manager = CreateTokenManager(instance => {
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.CreateAsync(OpenIdConnectConstants.TokenTypeHints.AuthorizationCode, "Bob le Magnifique", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
@ -69,8 +77,10 @@ namespace OpenIddict.Tests {
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
});
var server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(CreateApplicationManager(instance =>
{
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
@ -92,7 +102,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
RedirectUri = "http://www.fabrikam.com/path",
ResponseType = OpenIdConnectConstants.ResponseTypes.Code
@ -106,11 +117,13 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task SerializeAuthorizationCode_ClientApplicationIsAutomaticallyAttached() {
public async Task SerializeAuthorizationCode_ClientApplicationIsAutomaticallyAttached()
{
// Arrange
var token = new OpenIddictToken();
var manager = CreateTokenManager(instance => {
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.CreateAsync(OpenIdConnectConstants.TokenTypeHints.AuthorizationCode, "Bob le Magnifique", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
@ -121,8 +134,10 @@ namespace OpenIddict.Tests {
.Returns(Task.FromResult(0));
});
var server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(CreateApplicationManager(instance =>
{
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
@ -147,7 +162,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
RedirectUri = "http://www.fabrikam.com/path",
ResponseType = OpenIdConnectConstants.ResponseTypes.Code
@ -160,11 +176,13 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task SerializeAuthorizationCode_AuthorizationIsAutomaticallyAttached() {
public async Task SerializeAuthorizationCode_AuthorizationIsAutomaticallyAttached()
{
// Arrange
var token = new OpenIddictToken();
var manager = CreateTokenManager(instance => {
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.CreateAsync(OpenIdConnectConstants.TokenTypeHints.AuthorizationCode, "Bob le Magnifique", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
@ -178,8 +196,10 @@ namespace OpenIddict.Tests {
.Returns(Task.FromResult(0));
});
var server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(CreateApplicationManager(instance =>
{
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
@ -198,7 +218,8 @@ namespace OpenIddict.Tests {
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
}));
builder.Services.AddSingleton(CreateAuthorizationManager(instance => {
builder.Services.AddSingleton(CreateAuthorizationManager(instance =>
{
instance.Setup(mock => mock.FindByIdAsync("1AF06AB2-A0FC-4E3D-86AF-E04DA8C7BE70", It.IsAny<CancellationToken>()))
.ReturnsAsync(new OpenIddictAuthorization());
}));
@ -209,7 +230,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
RedirectUri = "http://www.fabrikam.com/path",
ResponseType = OpenIdConnectConstants.ResponseTypes.Code,
@ -222,11 +244,13 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task SerializeRefreshToken_RefreshTokenIsNotPersistedWhenRevocationIsDisabled() {
public async Task SerializeRefreshToken_RefreshTokenIsNotPersistedWhenRevocationIsDisabled()
{
// Arrange
var manager = CreateTokenManager();
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(manager);
builder.Configure(options => options.RevocationEndpointPath = PathString.Empty);
@ -237,7 +261,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest
{
GrantType = OpenIdConnectConstants.GrantTypes.Password,
Username = "johndoe",
Password = "A3ddj3w",
@ -251,11 +276,13 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task SerializeRefreshToken_RefreshTokenIsCorrectlyPersisted() {
public async Task SerializeRefreshToken_RefreshTokenIsCorrectlyPersisted()
{
// Arrange
var token = new OpenIddictToken();
var manager = CreateTokenManager(instance => {
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.CreateAsync(OpenIdConnectConstants.TokenTypeHints.RefreshToken, "Bob le Magnifique", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
@ -263,14 +290,16 @@ namespace OpenIddict.Tests {
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
});
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(manager);
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest
{
GrantType = OpenIdConnectConstants.GrantTypes.Password,
Username = "johndoe",
Password = "A3ddj3w",
@ -285,11 +314,13 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task SerializeRefreshToken_ClientApplicationIsAutomaticallyAttached() {
public async Task SerializeRefreshToken_ClientApplicationIsAutomaticallyAttached()
{
// Arrange
var token = new OpenIddictToken();
var manager = CreateTokenManager(instance => {
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.CreateAsync(OpenIdConnectConstants.TokenTypeHints.RefreshToken, "Bob le Magnifique", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
@ -300,8 +331,10 @@ namespace OpenIddict.Tests {
.Returns(Task.FromResult(0));
});
var server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(CreateApplicationManager(instance =>
{
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
@ -320,7 +353,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
GrantType = OpenIdConnectConstants.GrantTypes.Password,
Username = "johndoe",
@ -335,11 +369,13 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task SerializeRefreshToken_AuthorizationIsAutomaticallyAttached() {
public async Task SerializeRefreshToken_AuthorizationIsAutomaticallyAttached()
{
// Arrange
var token = new OpenIddictToken();
var manager = CreateTokenManager(instance => {
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.CreateAsync(OpenIdConnectConstants.TokenTypeHints.RefreshToken, "Bob le Magnifique", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
@ -350,8 +386,10 @@ namespace OpenIddict.Tests {
.Returns(Task.FromResult(0));
});
var server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateAuthorizationManager(instance => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(CreateAuthorizationManager(instance =>
{
instance.Setup(mock => mock.FindByIdAsync("1AF06AB2-A0FC-4E3D-86AF-E04DA8C7BE70", It.IsAny<CancellationToken>()))
.ReturnsAsync(new OpenIddictAuthorization());
}));
@ -362,7 +400,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest
{
GrantType = OpenIdConnectConstants.GrantTypes.Password,
Username = "johndoe",
Password = "A3ddj3w",

69
test/OpenIddict.Tests/OpenIddictProviderTests.Session.cs

@ -11,17 +11,21 @@ using OpenIddict.Core;
using OpenIddict.Models;
using Xunit;
namespace OpenIddict.Tests {
public partial class OpenIddictProviderTests {
namespace OpenIddict.Tests
{
public partial class OpenIddictProviderTests
{
[Fact]
public async Task ExtractLogoutRequest_RequestIdParameterIsRejectedWhenRequestCachingIsDisabled() {
public async Task ExtractLogoutRequest_RequestIdParameterIsRejectedWhenRequestCachingIsDisabled()
{
// Arrange
var server = CreateAuthorizationServer();
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(LogoutEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(LogoutEndpoint, new OpenIdConnectRequest
{
RequestId = "EFAF3596-F868-497F-96BB-AA2AD1F8B7E7"
});
@ -31,9 +35,11 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ExtractLogoutRequest_InvalidRequestIdParameterIsRejected() {
public async Task ExtractLogoutRequest_InvalidRequestIdParameterIsRejected()
{
// Arrange
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddDistributedMemoryCache();
builder.EnableRequestCaching();
@ -42,7 +48,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(LogoutEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(LogoutEndpoint, new OpenIdConnectRequest
{
RequestId = "EFAF3596-F868-497F-96BB-AA2AD1F8B7E7"
});
@ -52,21 +59,25 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ValidateLogoutRequest_RequestIsRejectedWhenRedirectUriIsInvalid() {
public async Task ValidateLogoutRequest_RequestIsRejectedWhenRedirectUriIsInvalid()
{
// Arrange
var manager = CreateApplicationManager(instance => {
var manager = CreateApplicationManager(instance =>
{
instance.Setup(mock => mock.FindByLogoutRedirectUri("http://www.fabrikam.com/path", It.IsAny<CancellationToken>()))
.ReturnsAsync(null);
});
var server = CreateAuthorizationServer(builder => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(manager);
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(LogoutEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(LogoutEndpoint, new OpenIdConnectRequest
{
PostLogoutRedirectUri = "http://www.fabrikam.com/path"
});
@ -78,12 +89,15 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task HandleLogoutRequest_RequestIsPersistedInDistributedCache() {
public async Task HandleLogoutRequest_RequestIsPersistedInDistributedCache()
{
// Arrange
var cache = new Mock<IDistributedCache>();
var server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(CreateApplicationManager(instance =>
{
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByLogoutRedirectUri("http://www.fabrikam.com/path", It.IsAny<CancellationToken>()))
@ -98,7 +112,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(LogoutEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(LogoutEndpoint, new OpenIdConnectRequest
{
PostLogoutRedirectUri = "http://www.fabrikam.com/path"
});
@ -115,10 +130,13 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task HandleLogoutRequest_RequestsAreNotHandledLocally() {
public async Task HandleLogoutRequest_RequestsAreNotHandledLocally()
{
// Arrange
var server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(CreateApplicationManager(instance =>
{
var application = new OpenIddictApplication();
instance.Setup(mock => mock.FindByLogoutRedirectUri("http://www.fabrikam.com/path", It.IsAny<CancellationToken>()))
@ -129,7 +147,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(LogoutEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(LogoutEndpoint, new OpenIdConnectRequest
{
PostLogoutRedirectUri = "http://www.fabrikam.com/path",
State = "af0ifjsldkj"
});
@ -139,10 +158,13 @@ namespace OpenIddict.Tests {
}
[Fact]
public async Task ApplyLogoutResponse_ErroredRequestIsNotHandledLocallyWhenStatusCodeMiddlewareIsEnabled() {
public async Task ApplyLogoutResponse_ErroredRequestIsNotHandledLocallyWhenStatusCodeMiddlewareIsEnabled()
{
// Arrange
var server = CreateAuthorizationServer(builder => {
builder.Services.AddSingleton(CreateApplicationManager(instance => {
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(CreateApplicationManager(instance =>
{
instance.Setup(mock => mock.FindByLogoutRedirectUri("http://www.fabrikam.com/path", It.IsAny<CancellationToken>()))
.ReturnsAsync(null);
}));
@ -153,7 +175,8 @@ namespace OpenIddict.Tests {
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync("/logout-status-code-middleware", new OpenIdConnectRequest {
var response = await client.PostAsync("/logout-status-code-middleware", new OpenIdConnectRequest
{
PostLogoutRedirectUri = "http://www.fabrikam.com/path"
});

12
test/OpenIddict.Tests/OpenIddictProviderTests.Userinfo.cs

@ -3,16 +3,20 @@ using AspNet.Security.OpenIdConnect.Client;
using AspNet.Security.OpenIdConnect.Primitives;
using Xunit;
namespace OpenIddict.Tests {
public partial class OpenIddictProviderTests {
namespace OpenIddict.Tests
{
public partial class OpenIddictProviderTests
{
[Fact]
public async Task ExtractUserinfoRequest_RequestIsHandledByUserCode() {
public async Task ExtractUserinfoRequest_RequestIsHandledByUserCode()
{
// Arrange
var server = CreateAuthorizationServer();
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(UserinfoEndpoint, new OpenIdConnectRequest {
var response = await client.PostAsync(UserinfoEndpoint, new OpenIdConnectRequest
{
AccessToken = "SlAV32hkKG"
});

57
test/OpenIddict.Tests/OpenIddictProviderTests.cs

@ -22,8 +22,10 @@ using Newtonsoft.Json;
using OpenIddict.Core;
using OpenIddict.Models;
namespace OpenIddict.Tests {
public partial class OpenIddictProviderTests {
namespace OpenIddict.Tests
{
public partial class OpenIddictProviderTests
{
public const string AuthorizationEndpoint = "/connect/authorize";
public const string ConfigurationEndpoint = "/.well-known/openid-configuration";
public const string IntrospectionEndpoint = "/connect/introspect";
@ -32,14 +34,16 @@ namespace OpenIddict.Tests {
public const string TokenEndpoint = "/connect/token";
public const string UserinfoEndpoint = "/connect/userinfo";
private static TestServer CreateAuthorizationServer(Action<OpenIddictBuilder> configuration = null) {
private static TestServer CreateAuthorizationServer(Action<OpenIddictBuilder> configuration = null)
{
var builder = new WebHostBuilder();
builder.UseEnvironment("Testing");
builder.ConfigureLogging(options => options.AddDebug());
builder.ConfigureServices(services => {
builder.ConfigureServices(services =>
{
services.AddAuthentication();
services.AddOptions();
@ -82,18 +86,23 @@ namespace OpenIddict.Tests {
configuration?.Invoke(instance);
});
builder.Configure(app => {
app.UseStatusCodePages(context => {
builder.Configure(app =>
{
app.UseStatusCodePages(context =>
{
context.HttpContext.Response.Headers[HeaderNames.ContentType] = "application/json";
return context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(new {
return context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(new
{
error_custom = OpenIdConnectConstants.Errors.InvalidRequest
}));
});
app.Use(next => context => {
app.Use(next => context =>
{
if (context.Request.Path != "/authorize-status-code-middleware" &&
context.Request.Path != "/logout-status-code-middleware") {
context.Request.Path != "/logout-status-code-middleware")
{
var feature = context.Features.Get<IStatusCodePagesFeature>();
feature.Enabled = false;
}
@ -105,13 +114,15 @@ namespace OpenIddict.Tests {
// Note: the following client_id/client_secret are fake and are only
// used to test the metadata returned by the discovery endpoint.
app.UseFacebookAuthentication(new FacebookOptions {
app.UseFacebookAuthentication(new FacebookOptions
{
ClientId = "16018790-E88E-4553-8036-BB342579FF19",
ClientSecret = "3D6499AF-5607-489B-815A-F3ACF1617296",
SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme
});
app.UseGoogleAuthentication(new GoogleOptions {
app.UseGoogleAuthentication(new GoogleOptions
{
ClientId = "BAF437A5-87FA-4D06-8EFD-F9BA96CCEDC4",
ClientSecret = "27DF07D3-6B03-4EE0-95CD-3AC16782216B",
SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme
@ -119,9 +130,11 @@ namespace OpenIddict.Tests {
app.UseOpenIddict();
app.Run(context => {
app.Run(context =>
{
var request = context.GetOpenIdConnectRequest();
if (request.IsAuthorizationRequest() || request.IsTokenRequest()) {
if (request.IsAuthorizationRequest() || request.IsTokenRequest())
{
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
identity.AddClaim(ClaimTypes.NameIdentifier, "Bob le Magnifique");
@ -137,14 +150,17 @@ namespace OpenIddict.Tests {
return context.Authentication.SignInAsync(ticket.AuthenticationScheme, ticket.Principal, ticket.Properties);
}
else if (request.IsLogoutRequest()) {
else if (request.IsLogoutRequest())
{
return context.Authentication.SignOutAsync(OpenIdConnectServerDefaults.AuthenticationScheme);
}
else if (request.IsUserinfoRequest()) {
else if (request.IsUserinfoRequest())
{
context.Response.Headers[HeaderNames.ContentType] = "application/json";
return context.Response.WriteAsync(JsonConvert.SerializeObject(new {
return context.Response.WriteAsync(JsonConvert.SerializeObject(new
{
access_token = request.AccessToken,
sub = "Bob le Bricoleur"
}));
@ -157,7 +173,8 @@ namespace OpenIddict.Tests {
return new TestServer(builder);
}
private static OpenIddictApplicationManager<OpenIddictApplication> CreateApplicationManager(Action<Mock<OpenIddictApplicationManager<OpenIddictApplication>>> setup = null) {
private static OpenIddictApplicationManager<OpenIddictApplication> CreateApplicationManager(Action<Mock<OpenIddictApplicationManager<OpenIddictApplication>>> setup = null)
{
var manager = new Mock<OpenIddictApplicationManager<OpenIddictApplication>>(
Mock.Of<IOpenIddictApplicationStore<OpenIddictApplication>>(),
Mock.Of<ILogger<OpenIddictApplicationManager<OpenIddictApplication>>>());
@ -167,7 +184,8 @@ namespace OpenIddict.Tests {
return manager.Object;
}
private static OpenIddictAuthorizationManager<OpenIddictAuthorization> CreateAuthorizationManager(Action<Mock<OpenIddictAuthorizationManager<OpenIddictAuthorization>>> setup = null) {
private static OpenIddictAuthorizationManager<OpenIddictAuthorization> CreateAuthorizationManager(Action<Mock<OpenIddictAuthorizationManager<OpenIddictAuthorization>>> setup = null)
{
var manager = new Mock<OpenIddictAuthorizationManager<OpenIddictAuthorization>>(
Mock.Of<IOpenIddictAuthorizationStore<OpenIddictAuthorization>>(),
Mock.Of<ILogger<OpenIddictAuthorizationManager<OpenIddictAuthorization>>>());
@ -177,7 +195,8 @@ namespace OpenIddict.Tests {
return manager.Object;
}
private static OpenIddictTokenManager<OpenIddictToken> CreateTokenManager(Action<Mock<OpenIddictTokenManager<OpenIddictToken>>> setup = null) {
private static OpenIddictTokenManager<OpenIddictToken> CreateTokenManager(Action<Mock<OpenIddictTokenManager<OpenIddictToken>>> setup = null)
{
var manager = new Mock<OpenIddictTokenManager<OpenIddictToken>>(
Mock.Of<IOpenIddictTokenStore<OpenIddictToken>>(),
Mock.Of<ILogger<OpenIddictTokenManager<OpenIddictToken>>>());

Loading…
Cancel
Save