fix(oauth): Reject device code requests from unauthorised clients

The device authorization endpoint issued a device code to any registered
client, without checking that the client had registered the
`urn:ietf:params:oauth:grant-type:device_code` grant type. Such a client
could therefore start a device authorization flow and have the server
show an approval prompt to a user, even though the subsequent token
request was always going to be refused.

RFC 8628 section 3.2 states that, in the event of an error such as an
invalidly configured client, the device authorization endpoint responds
in the same way as the token endpoint specified in RFC 6749 section 5.2,
which defines `unauthorized_client` as "the authenticated client is not
authorized to use this authorization grant type".

Apply the same grant type check the token endpoint already performs, and
add the `unauthorized_client` error code it requires.
This commit is contained in:
Michel-Marie MAUDET authored and Ellis Git committed 2026-08-07 08:52:20 +00:00
1 parent 02bee2e898
commit 8c1bbf3d1a
3 files changed
+12 -1

No files matched your search

+1
View File
@@ -0,0 +1 @@
The OAuth 2.0 device authorization endpoint now rejects clients which did not register the device code grant type, instead of issuing them a device code. Contributed by @mmaudet.
+6
View File
@@ -178,6 +178,11 @@ impl OAuthError {
pub const fn invalid_grant(error_description: &'static str) -> Self {
Self::new_static(ErrorCode::InvalidGrant, error_description)
}
#[must_use]
pub const fn unauthorized_client(error_description: &'static str) -> Self {
Self::new_static(ErrorCode::UnauthorizedClient, error_description)
}
}
impl Display for OAuthError {
@@ -197,6 +202,7 @@ pub enum ErrorCode {
InvalidGrant,
InvalidClient,
InvalidClientMetadata,
UnauthorizedClient,
AuthorizationPending,
ExpiredToken,
}
+5 -1
View File
@@ -20,7 +20,7 @@ use url::Url;
use crate::{
Dep, config,
oauth::{
client_metadata::{ApplicationType, ClientMetadata, ResponseType},
client_metadata::{ApplicationType, ClientMetadata, GrantType, ResponseType},
grant::{
AuthorizationCodeQuery, AuthorizationCodeResponse, CodeChallengeMethod,
DeviceCodeRequest, DeviceCodeResponse, ErrorCode, OAuthError, ResponseMode, Scope,
@@ -329,6 +329,10 @@ impl Service {
return Err(OAuthError::new_static(ErrorCode::InvalidClient, "Invalid client ID"));
};
if !client_metadata.grant_types.contains(&GrantType::DeviceCode) {
return Err(OAuthError::unauthorized_client("Client cannot request this grant type"));
}
let requested_scopes = query
.scope
.to_scopes()