diff --git a/samples/Mvc.Server/Startup.cs b/samples/Mvc.Server/Startup.cs
index 256ccc13..4aeb6a95 100644
--- a/samples/Mvc.Server/Startup.cs
+++ b/samples/Mvc.Server/Startup.cs
@@ -48,6 +48,9 @@ namespace Mvc.Server {
.AllowPasswordFlow()
.AllowRefreshTokenFlow()
+ // Make the "client_id" parameter mandatory when sending a token request.
+ .RequireClientIdentification()
+
// During development, you can disable the HTTPS requirement.
.DisableHttpsRequirement()
diff --git a/src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Exchange.cs b/src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Exchange.cs
index 4d830820..24921c9e 100644
--- a/src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Exchange.cs
+++ b/src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Exchange.cs
@@ -79,11 +79,24 @@ namespace OpenIddict.Infrastructure {
// See http://openid.net/specs/openid-connect-core-1_0.html#RefreshingAccessToken
// and https://tools.ietf.org/html/rfc6749#section-6 for more information.
- // At this stage, skip client authentication if the client identifier is missing.
+ // At this stage, skip client authentication if the client identifier is missing
+ // or reject the token request if client identification is set as required.
// 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)) {
+ // Reject the request if client identification is mandatory.
+ if (services.Options.RequireClientIdentification) {
+ services.Logger.LogError("The token request was rejected becaused the " +
+ "mandatory client_id parameter was missing or empty.");
+
+ context.Reject(
+ error: OpenIdConnectConstants.Errors.InvalidRequest,
+ description: "The mandatory 'client_id' parameter was missing.");
+
+ return;
+ }
+
services.Logger.LogInformation("The token request validation process was skipped " +
"because the client_id parameter was missing or empty.");
diff --git a/src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Revocation.cs b/src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Revocation.cs
index 9c24f466..c0da2164 100644
--- a/src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Revocation.cs
+++ b/src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Revocation.cs
@@ -31,11 +31,24 @@ namespace OpenIddict.Infrastructure {
return;
}
- // Skip client authentication if the client identifier is missing.
+ // Skip client authentication if the client identifier is missing or reject
+ // the revocation request if client identification is set as required.
// 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)) {
+ // Reject the request if client identification is mandatory.
+ if (services.Options.RequireClientIdentification) {
+ services.Logger.LogError("The revocation request was rejected becaused the " +
+ "mandatory client_id parameter was missing or empty.");
+
+ context.Reject(
+ error: OpenIdConnectConstants.Errors.InvalidRequest,
+ description: "The mandatory 'client_id' parameter was missing.");
+
+ return;
+ }
+
services.Logger.LogInformation("The revocation request validation process was skipped " +
"because the client_id parameter was missing or empty.");
diff --git a/src/OpenIddict.Core/OpenIddictBuilder.cs b/src/OpenIddict.Core/OpenIddictBuilder.cs
index b6f1a638..60886652 100644
--- a/src/OpenIddict.Core/OpenIddictBuilder.cs
+++ b/src/OpenIddict.Core/OpenIddictBuilder.cs
@@ -573,6 +573,16 @@ namespace Microsoft.AspNetCore.Builder {
return Configure(options => options.UserinfoEndpointPath = path);
}
+ ///
+ /// 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.
+ ///
+ public virtual OpenIddictBuilder RequireClientIdentification() {
+ return Configure(options => options.RequireClientIdentification = true);
+ }
+
///
/// Sets the access token lifetime, after which client applications must retrieve
/// a new access token by making a grant_type=refresh_token token request
diff --git a/src/OpenIddict.Core/OpenIddictOptions.cs b/src/OpenIddict.Core/OpenIddictOptions.cs
index 5f4d2cf4..3d156175 100644
--- a/src/OpenIddict.Core/OpenIddictOptions.cs
+++ b/src/OpenIddict.Core/OpenIddictOptions.cs
@@ -7,7 +7,6 @@
using System;
using System.Collections.Generic;
using AspNet.Security.OpenIdConnect.Server;
-using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Distributed;
namespace OpenIddict {
@@ -36,5 +35,12 @@ namespace OpenIddict {
/// Gets the list of the OpenIddict modules registered in the application.
///
public ICollection Modules { get; } = new List();
+
+ ///
+ /// 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.
+ ///
+ public bool RequireClientIdentification { get; set; }
}
}