Browse Source

Replace OpenIddictApplicationManager.IsConfidentialAsync()/IsPublicAsync()/IsHybridAsync() by HasClientTypeAsync()

pull/927/head
Kévin Chalet 6 years ago
parent
commit
fb92acbdaf
  1. 19
      samples/Mvc.Server/Controllers/AuthorizationController.cs
  2. 38
      src/OpenIddict.Abstractions/Managers/IOpenIddictApplicationManager.cs
  3. 9
      src/OpenIddict.Abstractions/Managers/IOpenIddictAuthorizationManager.cs
  4. 100
      src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs
  5. 26
      src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
  6. 3
      src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs
  7. 4
      src/OpenIddict.Server/OpenIddictServerHandlers.Device.cs
  8. 4
      src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs
  9. 6
      src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs
  10. 4
      src/OpenIddict.Server/OpenIddictServerHandlers.Revocation.cs
  11. 22
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs
  12. 129
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs
  13. 118
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs
  14. 61
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Revocation.cs
  15. 19
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.cs

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

@ -249,17 +249,16 @@ namespace Mvc.Server
// Note: the same check is already made in the other action but is repeated
// here to ensure a malicious user can't abuse this POST-only endpoint and
// force it to return a valid response without the external authorization.
switch (await _applicationManager.GetConsentTypeAsync(application))
if (!authorizations.Any() && await _applicationManager.HasConsentTypeAsync(application, ConsentTypes.External))
{
case ConsentTypes.External when !authorizations.Any():
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
"The logged in user is not allowed to access this client application."
}));
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
"The logged in user is not allowed to access this client application."
}));
}
var principal = await _signInManager.CreateUserPrincipalAsync(user);

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

@ -254,46 +254,40 @@ namespace OpenIddict.Abstractions
ValueTask<ImmutableArray<string>> GetRequirementsAsync([NotNull] object application, CancellationToken cancellationToken = default);
/// <summary>
/// Determines whether the specified permission has been granted to the application.
/// Determines whether a given application has the specified client type.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="permission">The permission.</param>
/// <param name="type">The expected client type.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the application has been granted the specified permission, <c>false</c> otherwise.</returns>
ValueTask<bool> HasPermissionAsync([NotNull] object application, [NotNull] string permission, CancellationToken cancellationToken = default);
/// <returns><c>true</c> if the application has the specified client type, <c>false</c> otherwise.</returns>
ValueTask<bool> HasClientTypeAsync([NotNull] object application, [NotNull] string type, CancellationToken cancellationToken = default);
/// <summary>
/// Determines whether the specified requirement has been enforced for the specified application.
/// Determines whether a given application has the specified consent type.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="requirement">The requirement.</param>
/// <param name="type">The expected consent type.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the requirement has been enforced for the specified application, <c>false</c> otherwise.</returns>
ValueTask<bool> HasRequirementAsync([NotNull] object application, [NotNull] string requirement, CancellationToken cancellationToken = default);
/// <returns><c>true</c> if the application has the specified consent type, <c>false</c> otherwise.</returns>
ValueTask<bool> HasConsentTypeAsync([NotNull] object application, [NotNull] string type, CancellationToken cancellationToken = default);
/// <summary>
/// Determines whether an application is a confidential client.
/// </summary>
/// <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>
ValueTask<bool> IsConfidentialAsync([NotNull] object application, CancellationToken cancellationToken = default);
/// <summary>
/// Determines whether an application is a hybrid client.
/// Determines whether the specified permission has been granted to the application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="permission">The permission.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the application is a hybrid client, <c>false</c> otherwise.</returns>
ValueTask<bool> IsHybridAsync([NotNull] object application, CancellationToken cancellationToken = default);
/// <returns><c>true</c> if the application has been granted the specified permission, <c>false</c> otherwise.</returns>
ValueTask<bool> HasPermissionAsync([NotNull] object application, [NotNull] string permission, CancellationToken cancellationToken = default);
/// <summary>
/// Determines whether an application is a public client.
/// Determines whether the specified requirement has been enforced for the specified application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="requirement">The requirement.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the application is a public client, <c>false</c> otherwise.</returns>
ValueTask<bool> IsPublicAsync([NotNull] object application, CancellationToken cancellationToken = default);
/// <returns><c>true</c> if the requirement has been enforced for the specified application, <c>false</c> otherwise.</returns>
ValueTask<bool> HasRequirementAsync([NotNull] object application, [NotNull] string requirement, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.

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

@ -278,6 +278,15 @@ namespace OpenIddict.Abstractions
/// <returns><c>true</c> if the authorization has the specified status, <c>false</c> otherwise.</returns>
ValueTask<bool> HasStatusAsync([NotNull] object authorization, [NotNull] string status, CancellationToken cancellationToken = default);
/// <summary>
/// Determines whether a given authorization has the specified type.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="type">The expected type.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the authorization has the specified type, <c>false</c> otherwise.</returns>
ValueTask<bool> HasTypeAsync([NotNull] object authorization, [NotNull] string type, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>

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

@ -605,116 +605,95 @@ namespace OpenIddict.Core
}
/// <summary>
/// Determines whether the specified permission has been granted to the application.
/// Determines whether a given application has the specified client type.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="permission">The permission.</param>
/// <param name="type">The expected client type.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the application has been granted the specified permission, <c>false</c> otherwise.</returns>
public virtual async ValueTask<bool> HasPermissionAsync(
[NotNull] TApplication application, [NotNull] string permission, CancellationToken cancellationToken = default)
/// <returns><c>true</c> if the application has the specified client type, <c>false</c> otherwise.</returns>
public virtual async ValueTask<bool> HasClientTypeAsync(
[NotNull] TApplication application, [NotNull] string type, CancellationToken cancellationToken = default)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
if (string.IsNullOrEmpty(permission))
{
throw new ArgumentException("The permission name cannot be null or empty.", nameof(permission));
}
return (await GetPermissionsAsync(application, cancellationToken)).Contains(permission, StringComparer.Ordinal);
}
/// <summary>
/// Determines whether the specified requirement has been enforced for the specified application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="requirement">The requirement.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the requirement has been enforced for the specified application, <c>false</c> otherwise.</returns>
public virtual async ValueTask<bool> HasRequirementAsync(
[NotNull] TApplication application, [NotNull] string requirement, CancellationToken cancellationToken = default)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
if (string.IsNullOrEmpty(requirement))
if (string.IsNullOrEmpty(type))
{
throw new ArgumentException("The requirement name cannot be null or empty.", nameof(requirement));
throw new ArgumentException("The client type cannot be null or empty.", nameof(type));
}
return (await GetRequirementsAsync(application, cancellationToken)).Contains(requirement, StringComparer.Ordinal);
return string.Equals(await GetClientTypeAsync(application, cancellationToken), type, StringComparison.OrdinalIgnoreCase);
}
/// <summary>
/// Determines whether an application is a confidential client.
/// Determines whether a given application has the specified consent type.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="type">The expected consent type.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the application is a confidential client, <c>false</c> otherwise.</returns>
public async ValueTask<bool> IsConfidentialAsync([NotNull] TApplication application, CancellationToken cancellationToken = default)
/// <returns><c>true</c> if the application has the specified consent type, <c>false</c> otherwise.</returns>
public virtual async ValueTask<bool> HasConsentTypeAsync(
[NotNull] TApplication application, [NotNull] string type, CancellationToken cancellationToken = default)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
var type = await GetClientTypeAsync(application, cancellationToken);
if (string.IsNullOrEmpty(type))
{
return false;
throw new ArgumentException("The consent type cannot be null or empty.", nameof(type));
}
return string.Equals(type, ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase);
return string.Equals(await GetConsentTypeAsync(application, cancellationToken), type, StringComparison.OrdinalIgnoreCase);
}
/// <summary>
/// Determines whether an application is a hybrid client.
/// Determines whether the specified permission has been granted to the application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="permission">The permission.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the application is a hybrid client, <c>false</c> otherwise.</returns>
public async ValueTask<bool> IsHybridAsync([NotNull] TApplication application, CancellationToken cancellationToken = default)
/// <returns><c>true</c> if the application has been granted the specified permission, <c>false</c> otherwise.</returns>
public virtual async ValueTask<bool> HasPermissionAsync(
[NotNull] TApplication application, [NotNull] string permission, CancellationToken cancellationToken = default)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
var type = await GetClientTypeAsync(application, cancellationToken);
if (string.IsNullOrEmpty(type))
if (string.IsNullOrEmpty(permission))
{
return false;
throw new ArgumentException("The permission name cannot be null or empty.", nameof(permission));
}
return string.Equals(type, ClientTypes.Hybrid, StringComparison.OrdinalIgnoreCase);
return (await GetPermissionsAsync(application, cancellationToken)).Contains(permission, StringComparer.Ordinal);
}
/// <summary>
/// Determines whether an application is a public client.
/// Determines whether the specified requirement has been enforced for the specified application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="requirement">The requirement.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the application is a public client, <c>false</c> otherwise.</returns>
public async ValueTask<bool> IsPublicAsync([NotNull] TApplication application, CancellationToken cancellationToken = default)
/// <returns><c>true</c> if the requirement has been enforced for the specified application, <c>false</c> otherwise.</returns>
public virtual async ValueTask<bool> HasRequirementAsync(
[NotNull] TApplication application, [NotNull] string requirement, CancellationToken cancellationToken = default)
{
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(requirement))
{
return true;
throw new ArgumentException("The requirement name cannot be null or empty.", nameof(requirement));
}
return string.Equals(type, ClientTypes.Public, StringComparison.OrdinalIgnoreCase);
return (await GetRequirementsAsync(application, cancellationToken)).Contains(requirement, StringComparer.Ordinal);
}
/// <summary>
@ -1104,7 +1083,7 @@ namespace OpenIddict.Core
throw new ArgumentException("The secret cannot be null or empty.", nameof(secret));
}
if (await IsPublicAsync(application, cancellationToken))
if (await HasClientTypeAsync(application, ClientTypes.Public, cancellationToken))
{
Logger.LogWarning("Client authentication cannot be enforced for public applications.");
@ -1463,21 +1442,18 @@ namespace OpenIddict.Core
ValueTask<ImmutableArray<string>> IOpenIddictApplicationManager.GetRequirementsAsync(object application, CancellationToken cancellationToken)
=> GetRequirementsAsync((TApplication) application, cancellationToken);
ValueTask<bool> IOpenIddictApplicationManager.HasClientTypeAsync(object application, string type, CancellationToken cancellationToken)
=> HasClientTypeAsync((TApplication) application, type, cancellationToken);
ValueTask<bool> IOpenIddictApplicationManager.HasConsentTypeAsync(object application, string type, CancellationToken cancellationToken)
=> HasConsentTypeAsync((TApplication) application, type, cancellationToken);
ValueTask<bool> IOpenIddictApplicationManager.HasPermissionAsync(object application, string permission, CancellationToken cancellationToken)
=> HasPermissionAsync((TApplication) application, permission, cancellationToken);
ValueTask<bool> IOpenIddictApplicationManager.HasRequirementAsync(object application, string requirement, CancellationToken cancellationToken)
=> HasRequirementAsync((TApplication) application, requirement, cancellationToken);
ValueTask<bool> IOpenIddictApplicationManager.IsConfidentialAsync(object application, CancellationToken cancellationToken)
=> IsConfidentialAsync((TApplication) application, cancellationToken);
ValueTask<bool> IOpenIddictApplicationManager.IsHybridAsync(object application, CancellationToken cancellationToken)
=> IsHybridAsync((TApplication) application, cancellationToken);
ValueTask<bool> IOpenIddictApplicationManager.IsPublicAsync(object application, CancellationToken cancellationToken)
=> IsPublicAsync((TApplication) application, cancellationToken);
IAsyncEnumerable<object> IOpenIddictApplicationManager.ListAsync(int? count, int? offset, CancellationToken cancellationToken)
=> ListAsync(count, offset, cancellationToken).OfType<object>();

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

@ -724,6 +724,29 @@ namespace OpenIddict.Core
return string.Equals(await Store.GetStatusAsync(authorization, cancellationToken), status, StringComparison.OrdinalIgnoreCase);
}
/// <summary>
/// Determines whether a given authorization has the specified type.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="type">The expected type.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns><c>true</c> if the authorization has the specified type, <c>false</c> otherwise.</returns>
public virtual async ValueTask<bool> HasTypeAsync(
[NotNull] TAuthorization authorization, [NotNull] string type, CancellationToken cancellationToken = default)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
if (string.IsNullOrEmpty(type))
{
throw new ArgumentException("The type cannot be null or empty.", nameof(type));
}
return string.Equals(await Store.GetTypeAsync(authorization, cancellationToken), type, StringComparison.OrdinalIgnoreCase);
}
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
@ -1097,6 +1120,9 @@ namespace OpenIddict.Core
ValueTask<bool> IOpenIddictAuthorizationManager.HasStatusAsync(object authorization, string status, CancellationToken cancellationToken)
=> HasStatusAsync((TAuthorization) authorization, status, cancellationToken);
ValueTask<bool> IOpenIddictAuthorizationManager.HasTypeAsync(object authorization, string type, CancellationToken cancellationToken)
=> HasTypeAsync((TAuthorization) authorization, type, cancellationToken);
IAsyncEnumerable<object> IOpenIddictAuthorizationManager.ListAsync(int? count, int? offset, CancellationToken cancellationToken)
=> ListAsync(count, offset, cancellationToken).OfType<object>();

3
src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs

@ -1122,7 +1122,8 @@ namespace OpenIddict.Server
// from the authorization endpoint are rejected if the client_id corresponds to a confidential application.
// Note: when using the authorization code grant, the ValidateClientSecret handler is responsible of rejecting
// the token request if the client_id corresponds to an unauthenticated confidential client.
if (context.Request.HasResponseType(ResponseTypes.Token) && await _applicationManager.IsConfidentialAsync(application))
if (context.Request.HasResponseType(ResponseTypes.Token) &&
await _applicationManager.HasClientTypeAsync(application, ClientTypes.Confidential))
{
context.Logger.LogError("The authorization request was rejected because the confidential application '{ClientId}' " +
"was not allowed to retrieve an access token from the authorization endpoint.", context.ClientId);

4
src/OpenIddict.Server/OpenIddictServerHandlers.Device.cs

@ -601,7 +601,7 @@ namespace OpenIddict.Server
throw new InvalidOperationException("The client application details cannot be found in the database.");
}
if (await _applicationManager.IsPublicAsync(application))
if (await _applicationManager.HasClientTypeAsync(application, ClientTypes.Public))
{
// Reject device requests containing a client_secret when the client is a public application.
if (!string.IsNullOrEmpty(context.ClientSecret))
@ -685,7 +685,7 @@ namespace OpenIddict.Server
}
// If the application is not a public client, validate the client secret.
if (!await _applicationManager.IsPublicAsync(application) &&
if (!await _applicationManager.HasClientTypeAsync(application, ClientTypes.Public) &&
!await _applicationManager.ValidateClientSecretAsync(application, context.ClientSecret))
{
context.Logger.LogError("The device request was rejected because the confidential or hybrid application " +

4
src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs

@ -909,7 +909,7 @@ namespace OpenIddict.Server
throw new InvalidOperationException("The client application details cannot be found in the database.");
}
if (await _applicationManager.IsPublicAsync(application))
if (await _applicationManager.HasClientTypeAsync(application, ClientTypes.Public))
{
// Public applications are not allowed to use the client credentials grant.
if (context.Request.IsClientCredentialsGrantType())
@ -1006,7 +1006,7 @@ namespace OpenIddict.Server
}
// If the application is not a public client, validate the client secret.
if (!await _applicationManager.IsPublicAsync(application) &&
if (!await _applicationManager.HasClientTypeAsync(application, ClientTypes.Public) &&
!await _applicationManager.ValidateClientSecretAsync(application, context.ClientSecret))
{
context.Logger.LogError("The token request was rejected because the confidential or hybrid application " +

6
src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs

@ -581,7 +581,7 @@ namespace OpenIddict.Server
throw new InvalidOperationException("The client application details cannot be found in the database.");
}
if (await _applicationManager.IsPublicAsync(application))
if (await _applicationManager.HasClientTypeAsync(application, ClientTypes.Public))
{
// Reject introspection requests containing a client_secret when the client is a public application.
if (!string.IsNullOrEmpty(context.ClientSecret))
@ -665,7 +665,7 @@ namespace OpenIddict.Server
}
// If the application is not a public client, validate the client secret.
if (!await _applicationManager.IsPublicAsync(application) &&
if (!await _applicationManager.HasClientTypeAsync(application, ClientTypes.Public) &&
!await _applicationManager.ValidateClientSecretAsync(application, context.ClientSecret))
{
context.Logger.LogError("The introspection request was rejected because the confidential or hybrid application " +
@ -1126,7 +1126,7 @@ namespace OpenIddict.Server
}
// Public clients are not allowed to access sensitive claims as authentication cannot be enforced.
if (await _applicationManager.IsPublicAsync(application))
if (await _applicationManager.HasClientTypeAsync(application, ClientTypes.Public))
{
return;
}

4
src/OpenIddict.Server/OpenIddictServerHandlers.Revocation.cs

@ -527,7 +527,7 @@ namespace OpenIddict.Server
throw new InvalidOperationException("The client application details cannot be found in the database.");
}
if (await _applicationManager.IsPublicAsync(application))
if (await _applicationManager.HasClientTypeAsync(application, ClientTypes.Public))
{
// Reject revocation requests containing a client_secret when the client is a public application.
if (!string.IsNullOrEmpty(context.ClientSecret))
@ -611,7 +611,7 @@ namespace OpenIddict.Server
}
// If the application is not a public client, validate the client secret.
if (!await _applicationManager.IsPublicAsync(application) &&
if (!await _applicationManager.HasClientTypeAsync(application, ClientTypes.Public) &&
!await _applicationManager.ValidateClientSecretAsync(application, context.ClientSecret))
{
context.Logger.LogError("The revocation request was rejected because the confidential or hybrid application " +

22
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs

@ -555,8 +555,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
}));
options.Services.AddSingleton(CreateScopeManager(mock =>
@ -600,8 +600,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
}));
options.Services.AddSingleton(CreateApplicationManager(mock =>
@ -614,8 +614,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
}));
options.AddEventHandler<HandleAuthorizationRequestContext>(builder =>
@ -665,8 +665,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
}));
options.Services.AddSingleton(CreateScopeManager(mock =>
@ -1002,8 +1002,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Confidential, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
});
var client = CreateClient(options =>
@ -1026,7 +1026,7 @@ namespace OpenIddict.Server.FunctionalTests
Assert.Equal("The specified 'response_type' parameter is not valid for this client application.", response.ErrorDescription);
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(manager => manager.HasClientTypeAsync(application, ClientTypes.Confidential, It.IsAny<CancellationToken>()), Times.Once());
}
[Fact]

129
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs

@ -1259,8 +1259,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
});
var client = CreateClient(options =>
@ -1281,7 +1281,7 @@ namespace OpenIddict.Server.FunctionalTests
Assert.Equal("The specified 'grant_type' parameter is not valid for this client application.", response.ErrorDescription);
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()), Times.Once());
}
[Fact]
@ -1295,8 +1295,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
});
var client = CreateClient(options =>
@ -1319,49 +1319,11 @@ namespace OpenIddict.Server.FunctionalTests
Assert.Equal("The 'client_secret' parameter is not valid for this client application.", response.ErrorDescription);
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()), Times.Once());
}
[Fact]
public async Task ValidateTokenRequest_ClientSecretIsRequiredForConfidentialClients()
{
// Arrange
var application = new OpenIddictApplication();
var manager = CreateApplicationManager(mock =>
{
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
});
var client = CreateClient(options =>
{
options.Services.AddSingleton(manager);
});
// Act
var response = await client.PostAsync("/connect/token", new OpenIddictRequest
{
ClientId = "Fabrikam",
ClientSecret = null,
GrantType = GrantTypes.Password,
Username = "johndoe",
Password = "A3ddj3w"
});
// Assert
Assert.Equal(Errors.InvalidClient, response.Error);
Assert.Equal("The 'client_secret' parameter required for this client application is missing.", response.ErrorDescription);
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()), Times.Once());
}
[Fact]
public async Task ValidateTokenRequest_ClientSecretIsRequiredForHybridClients()
public async Task ValidateTokenRequest_ClientSecretIsRequiredForNonPublicClients()
{
// Arrange
var application = new OpenIddictApplication();
@ -1371,8 +1333,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Hybrid);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(false);
});
var client = CreateClient(options =>
@ -1395,7 +1357,7 @@ namespace OpenIddict.Server.FunctionalTests
Assert.Equal("The 'client_secret' parameter required for this client application is missing.", response.ErrorDescription);
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()), Times.Once());
}
[Fact]
@ -1409,8 +1371,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(false);
mock.Setup(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()))
.ReturnsAsync(false);
@ -1436,7 +1398,7 @@ namespace OpenIddict.Server.FunctionalTests
Assert.Equal("The specified client credentials are invalid.", response.ErrorDescription);
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()), Times.Once());
}
@ -1451,6 +1413,9 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.HasPermissionAsync(application,
Permissions.Endpoints.Token, It.IsAny<CancellationToken>()))
.ReturnsAsync(false);
@ -1492,6 +1457,9 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.HasPermissionAsync(application,
Permissions.GrantTypes.Password, It.IsAny<CancellationToken>()))
.ReturnsAsync(false);
@ -1533,6 +1501,9 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.HasPermissionAsync(application,
Permissions.GrantTypes.Password, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
@ -1578,8 +1549,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.HasPermissionAsync(application,
Permissions.Prefixes.Scope + Scopes.Profile, It.IsAny<CancellationToken>()))
@ -1633,8 +1604,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.HasRequirementAsync(application,
Requirements.Features.ProofKeyForCodeExchange, It.IsAny<CancellationToken>()))
@ -1675,8 +1646,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.HasRequirementAsync(application,
Requirements.Features.ProofKeyForCodeExchange, It.IsAny<CancellationToken>()))
@ -1739,8 +1710,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.HasRequirementAsync(application,
Requirements.Features.ProofKeyForCodeExchange, It.IsAny<CancellationToken>()))
@ -1936,8 +1907,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
}));
options.SetRevocationEndpointUris(Array.Empty<Uri>());
@ -2053,8 +2024,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
}));
options.Services.AddSingleton(manager);
@ -2188,8 +2159,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
}));
options.Services.AddSingleton(manager);
@ -2327,8 +2298,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
}));
options.Services.AddSingleton(CreateTokenManager(mock =>
@ -2522,8 +2493,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
}));
options.Services.AddSingleton(manager);
@ -2693,8 +2664,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
}));
options.Services.AddSingleton(manager);
@ -2836,8 +2807,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
}));
options.Services.AddSingleton(CreateTokenManager(mock =>
@ -3011,8 +2982,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
}));
options.Services.AddSingleton(CreateTokenManager(mock =>
@ -3107,8 +3078,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
}));
options.Services.AddSingleton(CreateTokenManager(mock =>
@ -3379,8 +3350,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Confidential, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()))
.ReturnsAsync(true);

118
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs

@ -469,8 +469,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.HasPermissionAsync(application,
Permissions.Endpoints.Introspection, It.IsAny<CancellationToken>()))
@ -500,6 +500,78 @@ namespace OpenIddict.Server.FunctionalTests
Permissions.Endpoints.Introspection, It.IsAny<CancellationToken>()), Times.Once());
}
[Fact]
public async Task ValidateIntrospectionRequest_ClientSecretCannotBeUsedByPublicClients()
{
// Arrange
var application = new OpenIddictApplication();
var manager = CreateApplicationManager(mock =>
{
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
});
var client = CreateClient(builder =>
{
builder.Services.AddSingleton(manager);
});
// Act
var response = await client.PostAsync("/connect/introspect", new OpenIddictRequest
{
ClientId = "Fabrikam",
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
Token = "2YotnFZFEjr1zCsicMWpAA"
});
// Assert
Assert.Equal(Errors.InvalidClient, response.Error);
Assert.Equal("The 'client_secret' parameter is not valid for this client application.", response.ErrorDescription);
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()), Times.Once());
}
[Fact]
public async Task ValidateIntrospectionRequest_ClientSecretIsRequiredForNonPublicClients()
{
// Arrange
var application = new OpenIddictApplication();
var manager = CreateApplicationManager(mock =>
{
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(false);
});
var client = CreateClient(builder =>
{
builder.Services.AddSingleton(manager);
});
// Act
var response = await client.PostAsync("/connect/introspect", new OpenIddictRequest
{
ClientId = "Fabrikam",
ClientSecret = null,
Token = "2YotnFZFEjr1zCsicMWpAA"
});
// Assert
Assert.Equal(Errors.InvalidClient, response.Error);
Assert.Equal("The 'client_secret' parameter required for this client application is missing.", response.ErrorDescription);
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()), Times.Once());
}
[Fact]
public async Task ValidateIntrospectionRequest_RequestIsRejectedWhenClientCredentialsAreInvalid()
{
@ -511,8 +583,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(false);
mock.Setup(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()))
.ReturnsAsync(false);
@ -534,7 +606,7 @@ namespace OpenIddict.Server.FunctionalTests
// Assert
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()), Times.Once());
}
@ -825,8 +897,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Confidential, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
@ -883,8 +955,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Confidential, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
@ -938,8 +1010,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Confidential, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
@ -1003,8 +1075,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Confidential, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
@ -1100,8 +1172,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Confidential, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
@ -1165,8 +1237,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Confidential, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
@ -1256,8 +1328,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Confidential, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
@ -1354,8 +1426,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Confidential, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
@ -1465,8 +1537,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Confidential, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()))
.ReturnsAsync(true);

61
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Revocation.cs

@ -409,8 +409,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.HasPermissionAsync(application,
Permissions.Endpoints.Revocation, It.IsAny<CancellationToken>()))
@ -452,8 +452,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
});
var client = CreateClient(builder =>
@ -475,11 +475,11 @@ namespace OpenIddict.Server.FunctionalTests
Assert.Equal("The 'client_secret' parameter is not valid for this client application.", response.ErrorDescription);
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()), Times.Once());
}
[Fact]
public async Task ValidateRevocationRequest_ClientSecretIsRequiredForConfidentialClients()
public async Task ValidateRevocationRequest_ClientSecretIsRequiredForNonPublicClients()
{
// Arrange
var application = new OpenIddictApplication();
@ -489,45 +489,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
});
var client = CreateClient(builder =>
{
builder.Services.AddSingleton(manager);
});
// Act
var response = await client.PostAsync("/connect/revoke", new OpenIddictRequest
{
ClientId = "Fabrikam",
ClientSecret = null,
Token = "SlAV32hkKG",
TokenTypeHint = TokenTypeHints.RefreshToken
});
// Assert
Assert.Equal(Errors.InvalidClient, response.Error);
Assert.Equal("The 'client_secret' parameter required for this client application is missing.", response.ErrorDescription);
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()), Times.Once());
}
[Fact]
public async Task ValidateRevocationRequest_ClientSecretIsRequiredForHybridClients()
{
// Arrange
var application = new OpenIddictApplication();
var manager = CreateApplicationManager(mock =>
{
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Hybrid);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(false);
});
var client = CreateClient(builder =>
@ -549,7 +512,7 @@ namespace OpenIddict.Server.FunctionalTests
Assert.Equal("The 'client_secret' parameter required for this client application is missing.", response.ErrorDescription);
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()), Times.Once());
}
[Fact]
@ -563,8 +526,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(false);
mock.Setup(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()))
.ReturnsAsync(false);
@ -589,7 +552,7 @@ namespace OpenIddict.Server.FunctionalTests
Assert.Equal("The specified client credentials are invalid.", response.ErrorDescription);
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()), Times.Once());
}

19
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.cs

@ -2679,8 +2679,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
}));
options.Services.AddSingleton(manager);
@ -2749,8 +2749,11 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
}));
options.Services.AddSingleton(manager);
@ -3465,8 +3468,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.GetIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
@ -3536,8 +3539,8 @@ namespace OpenIddict.Server.FunctionalTests
mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.GetIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");

Loading…
Cancel
Save