diff --git a/src/OpenIddict.Abstractions/OpenIddictResources.resx b/src/OpenIddict.Abstractions/OpenIddictResources.resx
index e7939bab..db1dd8f7 100644
--- a/src/OpenIddict.Abstractions/OpenIddictResources.resx
+++ b/src/OpenIddict.Abstractions/OpenIddictResources.resx
@@ -1564,6 +1564,9 @@ To apply post-logout redirection responses, create a class implementing 'IOpenId
The authentication properties must not contain an '.issuer', '.provider_name' or '.registration_id' property when using a forwarded authentication scheme/type.
+
+ A client identifier must be specified in the client registration or web provider options when using 'response_type=none', the authorization code/hybrid/implicit flows or the device authorization flow.
+
The security token is missing.
diff --git a/src/OpenIddict.Client/OpenIddictClientHandlers.cs b/src/OpenIddict.Client/OpenIddictClientHandlers.cs
index f7add9a7..169aba2d 100644
--- a/src/OpenIddict.Client/OpenIddictClientHandlers.cs
+++ b/src/OpenIddict.Client/OpenIddictClientHandlers.cs
@@ -4640,7 +4640,21 @@ public static partial class OpenIddictClientHandlers
throw new ArgumentNullException(nameof(context));
}
- context.ClientId ??= context.Registration.ClientId;
+ context.ClientId ??= context.Registration.ClientId switch
+ {
+ { Length: > 0 } value => value,
+
+ // Note: the client identifier is required for the authorization code/hybrid/implicit and device flows.
+ // If no client identifier was attached to the registration, abort the challenge demand immediately.
+ _ when context.GrantType is GrantTypes.AuthorizationCode or GrantTypes.DeviceCode or GrantTypes.Implicit
+ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0418)),
+
+ // Note: the client identifier is also required for the special response_type=none flow.
+ _ when context.GrantType is null && context.ResponseType is ResponseTypes.None
+ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0418)),
+
+ _ => null
+ };
return default;
}