Browse Source

Enable scope validation and reject unidentified token/revocation requests by default

pull/620/head
Kévin Chalet 8 years ago
parent
commit
8f9c6f109d
  1. 17
      samples/Mvc.Server/Startup.cs
  2. 2
      src/OpenIddict.Server/Internal/OpenIddictServerProvider.Authentication.cs
  3. 4
      src/OpenIddict.Server/Internal/OpenIddictServerProvider.Exchange.cs
  4. 2
      src/OpenIddict.Server/Internal/OpenIddictServerProvider.Revocation.cs
  5. 26
      src/OpenIddict.Server/OpenIddictServerBuilder.cs
  6. 18
      src/OpenIddict.Server/OpenIddictServerOptions.cs
  7. 4
      test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Authentication.cs
  8. 14
      test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Exchange.cs
  9. 5
      test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Revocation.cs
  10. 3
      test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.cs
  11. 12
      test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs

17
samples/Mvc.Server/Startup.cs

@ -101,9 +101,6 @@ namespace Mvc.Server
OpenIdConnectConstants.Scopes.Profile, OpenIdConnectConstants.Scopes.Profile,
OpenIddictConstants.Scopes.Roles); OpenIddictConstants.Scopes.Roles);
// Make the "client_id" parameter mandatory when sending a token request.
options.RequireClientIdentification();
// When request caching is enabled, authorization and logout requests // When request caching is enabled, authorization and logout requests
// are stored in the distributed cache by OpenIddict and the user agent // are stored in the distributed cache by OpenIddict and the user agent
// is redirected to the same page with a single parameter (request_id). // is redirected to the same page with a single parameter (request_id).
@ -111,10 +108,6 @@ namespace Mvc.Server
// an external authentication provider like Google, Facebook or Twitter. // an external authentication provider like Google, Facebook or Twitter.
options.EnableRequestCaching(); options.EnableRequestCaching();
// Enable scope validation, so that authorization and token requests
// that specify unregistered scopes are automatically rejected.
options.EnableScopeValidation();
// During development, you can disable the HTTPS requirement. // During development, you can disable the HTTPS requirement.
options.DisableHttpsRequirement(); options.DisableHttpsRequirement();
@ -123,6 +116,16 @@ namespace Mvc.Server
// //
// options.UseJsonWebTokens(); // options.UseJsonWebTokens();
// options.AddEphemeralSigningKey(); // options.AddEphemeralSigningKey();
// Note: if you don't want to specify a client_id when sending
// a token or revocation request, uncomment the following line:
//
// options.AcceptAnonymousClients();
// Note: if you want to process authorization and token requests
// that specify non-registered scopes, uncomment the following line:
//
// options.DisableScopeValidation();
}) })
// Register the OpenIddict validation handler. // Register the OpenIddict validation handler.

2
src/OpenIddict.Server/Internal/OpenIddictServerProvider.Authentication.cs

@ -180,7 +180,7 @@ namespace OpenIddict.Server
} }
// Validates scopes, unless scope validation was explicitly disabled. // Validates scopes, unless scope validation was explicitly disabled.
if (options.EnableScopeValidation) if (!options.DisableScopeValidation)
{ {
var scopes = new HashSet<string>(context.Request.GetScopes(), StringComparer.Ordinal); var scopes = new HashSet<string>(context.Request.GetScopes(), StringComparer.Ordinal);
scopes.ExceptWith(options.Scopes); scopes.ExceptWith(options.Scopes);

4
src/OpenIddict.Server/Internal/OpenIddictServerProvider.Exchange.cs

@ -78,7 +78,7 @@ namespace OpenIddict.Server
} }
// Validates scopes, unless scope validation was explicitly disabled. // Validates scopes, unless scope validation was explicitly disabled.
if (options.EnableScopeValidation) if (!options.DisableScopeValidation)
{ {
var scopes = new HashSet<string>(context.Request.GetScopes(), StringComparer.Ordinal); var scopes = new HashSet<string>(context.Request.GetScopes(), StringComparer.Ordinal);
scopes.ExceptWith(options.Scopes); scopes.ExceptWith(options.Scopes);
@ -127,7 +127,7 @@ namespace OpenIddict.Server
if (string.IsNullOrEmpty(context.ClientId)) if (string.IsNullOrEmpty(context.ClientId))
{ {
// Reject the request if client identification is mandatory. // Reject the request if client identification is mandatory.
if (options.RequireClientIdentification) if (!options.AcceptAnonymousClients)
{ {
_logger.LogError("The token request was rejected becaused the " + _logger.LogError("The token request was rejected becaused the " +
"mandatory client_id parameter was missing or empty."); "mandatory client_id parameter was missing or empty.");

2
src/OpenIddict.Server/Internal/OpenIddictServerProvider.Revocation.cs

@ -55,7 +55,7 @@ namespace OpenIddict.Server
if (string.IsNullOrEmpty(context.ClientId)) if (string.IsNullOrEmpty(context.ClientId))
{ {
// Reject the request if client identification is mandatory. // Reject the request if client identification is mandatory.
if (options.RequireClientIdentification) if (!options.AcceptAnonymousClients)
{ {
_logger.LogError("The revocation request was rejected becaused the " + _logger.LogError("The revocation request was rejected becaused the " +
"mandatory client_id parameter was missing or empty."); "mandatory client_id parameter was missing or empty.");

26
src/OpenIddict.Server/OpenIddictServerBuilder.cs

@ -66,6 +66,14 @@ namespace Microsoft.Extensions.DependencyInjection
return this; return this;
} }
/// <summary>
/// Makes client identification optional so that token and revocation
/// requests that don't specify a client_id are not automatically rejected.
/// </summary>
/// <returns>The <see cref="OpenIddictServerBuilder"/>.</returns>
public OpenIddictServerBuilder AcceptAnonymousClients()
=> Configure(options => options.AcceptAnonymousClients = true);
/// <summary> /// <summary>
/// Registers (and generates if necessary) a user-specific development /// Registers (and generates if necessary) a user-specific development
/// certificate used to sign the JWT tokens issued by OpenIddict. /// certificate used to sign the JWT tokens issued by OpenIddict.
@ -462,12 +470,12 @@ namespace Microsoft.Extensions.DependencyInjection
} }
/// <summary> /// <summary>
/// Rejects authorization and token requests that specify scopes that have not been /// Allows processing authorization and token requests that specify scopes that have not
/// registered using <see cref="RegisterScopes(string[])"/> or the scope manager. /// been registered using <see cref="RegisterScopes(string[])"/> or the scope manager.
/// </summary> /// </summary>
/// <returns>The <see cref="OpenIddictServerBuilder"/>.</returns> /// <returns>The <see cref="OpenIddictServerBuilder"/>.</returns>
public OpenIddictServerBuilder EnableScopeValidation() public OpenIddictServerBuilder DisableScopeValidation()
=> Configure(options => options.EnableScopeValidation = true); => Configure(options => options.DisableScopeValidation = true);
/// <summary> /// <summary>
/// Enables the token endpoint. /// Enables the token endpoint.
@ -595,16 +603,6 @@ namespace Microsoft.Extensions.DependencyInjection
return Configure(options => options.Scopes.UnionWith(scopes)); return Configure(options => options.Scopes.UnionWith(scopes));
} }
/// <summary>
/// Makes client identification mandatory so that token and revocation
/// requests that don't specify a client_id are automatically rejected.
/// Note: enabling this option doesn't prevent public clients from using
/// the token and revocation endpoints, but specifying a client_id is required.
/// </summary>
/// <returns>The <see cref="OpenIddictServerBuilder"/>.</returns>
public OpenIddictServerBuilder RequireClientIdentification()
=> Configure(options => options.RequireClientIdentification = true);
/// <summary> /// <summary>
/// Sets the access token lifetime, after which client applications must retrieve /// Sets the access token lifetime, after which client applications must retrieve
/// a new access token by making a grant_type=refresh_token token request /// a new access token by making a grant_type=refresh_token token request

18
src/OpenIddict.Server/OpenIddictServerOptions.cs

@ -27,6 +27,13 @@ namespace OpenIddict.Server
ProviderType = typeof(OpenIddictServerProvider); ProviderType = typeof(OpenIddictServerProvider);
} }
/// <summary>
/// Gets or sets a boolean determining whether client identification is optional.
/// Enabling this option allows client applications to communicate with the token
/// and revocation endpoints without having to send their client identifier.
/// </summary>
public bool AcceptAnonymousClients { get; set; }
/// <summary> /// <summary>
/// Gets or sets the user-provided <see cref="OpenIdConnectServerProvider"/> that the OpenIddict server /// Gets or sets the user-provided <see cref="OpenIdConnectServerProvider"/> that the OpenIddict server
/// invokes to enable developer control over the entire authentication/authorization process. /// invokes to enable developer control over the entire authentication/authorization process.
@ -84,9 +91,9 @@ namespace OpenIddict.Server
public bool EnableRequestCaching { get; set; } public bool EnableRequestCaching { get; set; }
/// <summary> /// <summary>
/// Gets or sets a boolean indicating whether scope validation is enabled. /// Gets or sets a boolean indicating whether scope validation is disabled.
/// </summary> /// </summary>
public bool EnableScopeValidation { get; set; } public bool DisableScopeValidation { get; set; }
/// <summary> /// <summary>
/// Gets the OAuth2/OpenID Connect flows enabled for this application. /// Gets the OAuth2/OpenID Connect flows enabled for this application.
@ -98,13 +105,6 @@ namespace OpenIddict.Server
/// </summary> /// </summary>
public RandomNumberGenerator RandomNumberGenerator { get; set; } = RandomNumberGenerator.Create(); public RandomNumberGenerator RandomNumberGenerator { get; set; } = RandomNumberGenerator.Create();
/// <summary>
/// Gets or sets a boolean determining whether client identification is required.
/// Enabling this option requires registering a client application and sending a
/// valid client_id when communicating with the token and revocation endpoints.
/// </summary>
public bool RequireClientIdentification { get; set; }
/// <summary> /// <summary>
/// Gets the OAuth2/OpenID Connect scopes enabled for this application. /// Gets the OAuth2/OpenID Connect scopes enabled for this application.
/// </summary> /// </summary>

4
test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Authentication.cs

@ -205,8 +205,6 @@ namespace OpenIddict.Server.Tests
It.IsAny<CancellationToken>())) It.IsAny<CancellationToken>()))
.ReturnsAsync(ImmutableArray.Create<OpenIddictScope>()); .ReturnsAsync(ImmutableArray.Create<OpenIddictScope>());
})); }));
builder.EnableScopeValidation();
}); });
var client = new OpenIdConnectClient(server.CreateClient()); var client = new OpenIdConnectClient(server.CreateClient());
@ -257,7 +255,6 @@ namespace OpenIddict.Server.Tests
.ReturnsAsync(true); .ReturnsAsync(true);
})); }));
builder.EnableScopeValidation();
builder.RegisterScopes("registered_scope"); builder.RegisterScopes("registered_scope");
}); });
@ -331,7 +328,6 @@ namespace OpenIddict.Server.Tests
builder.RegisterScopes("scope_registered_in_options"); builder.RegisterScopes("scope_registered_in_options");
builder.EnableScopeValidation();
builder.Services.AddSingleton(manager); builder.Services.AddSingleton(manager);
}); });

14
test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Exchange.cs

@ -81,10 +81,7 @@ namespace OpenIddict.Server.Tests
public async Task ValidateTokenRequest_AuthorizationCodeRequestIsRejectedWhenRedirectUriIsMissing() public async Task ValidateTokenRequest_AuthorizationCodeRequestIsRejectedWhenRedirectUriIsMissing()
{ {
// Arrange // Arrange
var server = CreateAuthorizationServer(builder => var server = CreateAuthorizationServer();
{
builder.EnableScopeValidation();
});
var client = new OpenIdConnectClient(server.CreateClient()); var client = new OpenIdConnectClient(server.CreateClient());
@ -115,8 +112,6 @@ namespace OpenIddict.Server.Tests
It.IsAny<CancellationToken>())) It.IsAny<CancellationToken>()))
.ReturnsAsync(ImmutableArray.Create<OpenIddictScope>()); .ReturnsAsync(ImmutableArray.Create<OpenIddictScope>());
})); }));
builder.EnableScopeValidation();
}); });
var client = new OpenIdConnectClient(server.CreateClient()); var client = new OpenIdConnectClient(server.CreateClient());
@ -141,7 +136,6 @@ namespace OpenIddict.Server.Tests
// Arrange // Arrange
var server = CreateAuthorizationServer(builder => var server = CreateAuthorizationServer(builder =>
{ {
builder.EnableScopeValidation();
builder.RegisterScopes("registered_scope"); builder.RegisterScopes("registered_scope");
}); });
@ -182,7 +176,6 @@ namespace OpenIddict.Server.Tests
var server = CreateAuthorizationServer(builder => var server = CreateAuthorizationServer(builder =>
{ {
builder.EnableScopeValidation();
builder.RegisterScopes("scope_registered_in_options"); builder.RegisterScopes("scope_registered_in_options");
builder.Services.AddSingleton(manager); builder.Services.AddSingleton(manager);
@ -254,7 +247,10 @@ namespace OpenIddict.Server.Tests
public async Task ValidateTokenRequest_RequestWithoutClientIdIsRejectedWhenClientIdentificationIsRequired() public async Task ValidateTokenRequest_RequestWithoutClientIdIsRejectedWhenClientIdentificationIsRequired()
{ {
// Arrange // Arrange
var server = CreateAuthorizationServer(builder => builder.RequireClientIdentification()); var server = CreateAuthorizationServer(builder =>
{
builder.Configure(options => options.AcceptAnonymousClients = false);
});
var client = new OpenIdConnectClient(server.CreateClient()); var client = new OpenIdConnectClient(server.CreateClient());

5
test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Revocation.cs

@ -49,7 +49,10 @@ namespace OpenIddict.Server.Tests
public async Task ValidateRevocationRequest_RequestWithoutClientIdIsRejectedWhenClientIdentificationIsRequired() public async Task ValidateRevocationRequest_RequestWithoutClientIdIsRejectedWhenClientIdentificationIsRequired()
{ {
// Arrange // Arrange
var server = CreateAuthorizationServer(builder => builder.RequireClientIdentification()); var server = CreateAuthorizationServer(builder =>
{
builder.Configure(options => options.AcceptAnonymousClients = false);
});
var client = new OpenIdConnectClient(server.CreateClient()); var client = new OpenIdConnectClient(server.CreateClient());

3
test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.cs

@ -1450,6 +1450,9 @@ namespace OpenIddict.Server.Tests
.AddServer(options => .AddServer(options =>
{ {
// Accept anonymous clients by default.
options.AcceptAnonymousClients();
// Disable the transport security requirement during testing. // Disable the transport security requirement during testing.
options.DisableHttpsRequirement(); options.DisableHttpsRequirement();

12
test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs

@ -457,19 +457,19 @@ namespace OpenIddict.Server.Tests
} }
[Fact] [Fact]
public void EnableScopeValidation_ScopeValidationIsDisabled() public void DisableScopeValidation_ScopeValidationIsDisabled()
{ {
// Arrange // Arrange
var services = CreateServices(); var services = CreateServices();
var builder = CreateBuilder(services); var builder = CreateBuilder(services);
// Act // Act
builder.EnableScopeValidation(); builder.DisableScopeValidation();
var options = GetOptions(services); var options = GetOptions(services);
// Assert // Assert
Assert.True(options.EnableScopeValidation); Assert.True(options.DisableScopeValidation);
} }
[Fact] [Fact]
@ -505,19 +505,19 @@ namespace OpenIddict.Server.Tests
} }
[Fact] [Fact]
public void RequireClientIdentification_ClientIdentificationIsEnforced() public void AcceptAnonymousClients_ClientIdentificationIsOptional()
{ {
// Arrange // Arrange
var services = CreateServices(); var services = CreateServices();
var builder = CreateBuilder(services); var builder = CreateBuilder(services);
// Act // Act
builder.RequireClientIdentification(); builder.AcceptAnonymousClients();
var options = GetOptions(services); var options = GetOptions(services);
// Assert // Assert
Assert.True(options.RequireClientIdentification); Assert.True(options.AcceptAnonymousClients);
} }
[Fact] [Fact]

Loading…
Cancel
Save