diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/AbstractOAuth2AuthorizationServerMetadata.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/AbstractOAuth2AuthorizationServerMetadata.java
index b7c7b81f..1c382949 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/AbstractOAuth2AuthorizationServerMetadata.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/AbstractOAuth2AuthorizationServerMetadata.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020-2022 the original author or authors.
+ * Copyright 2020-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,6 +38,7 @@ import org.springframework.util.Assert;
* @since 0.1.1
* @see 3.2. Authorization Server Metadata Response
* @see 4.2. OpenID Provider Configuration Response
+ * @see 4. Device Authorization Grant Metadata
*/
public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth2AuthorizationServerMetadataClaimAccessor, Serializable {
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
@@ -96,6 +97,17 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth
return claim(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT, authorizationEndpoint);
}
+ /**
+ * Use this {@code device_authorization_endpoint} in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
+ *
+ * @param deviceAuthorizationEndpoint the {@code URL} of the OAuth 2.0 Device Authorization Endpoint
+ * @return the {@link AbstractBuilder} for further configuration
+ * @since 1.1
+ */
+ public B deviceAuthorizationEndpoint(String deviceAuthorizationEndpoint) {
+ return claim(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT, deviceAuthorizationEndpoint);
+ }
+
/**
* Use this {@code token_endpoint} in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, REQUIRED.
*
@@ -346,6 +358,9 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth
validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.ISSUER), "issuer must be a valid URL");
Assert.notNull(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT), "authorizationEndpoint cannot be null");
validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT), "authorizationEndpoint must be a valid URL");
+ if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT) != null) {
+ validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT), "deviceAuthorizationEndpoint must be a valid URL");
+ }
Assert.notNull(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT), "tokenEndpoint cannot be null");
validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT), "tokenEndpoint must be a valid URL");
if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED) != null) {
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationService.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationService.java
index abdc15d7..701042ad 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationService.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationService.java
@@ -155,7 +155,9 @@ public final class InMemoryOAuth2AuthorizationService implements OAuth2Authoriza
matchesAuthorizationCode(authorization, token) ||
matchesAccessToken(authorization, token) ||
matchesIdToken(authorization, token) ||
- matchesRefreshToken(authorization, token);
+ matchesRefreshToken(authorization, token) ||
+ matchesDeviceCode(authorization, token) ||
+ matchesUserCode(authorization, token);
} else if (OAuth2ParameterNames.STATE.equals(tokenType.getValue())) {
return matchesState(authorization, token);
} else if (OAuth2ParameterNames.CODE.equals(tokenType.getValue())) {
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2Authorization.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2Authorization.java
index afdfc3a6..ef8bb69d 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2Authorization.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2Authorization.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020-2023 the original author or authors.
+ * Copyright 2020-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -253,18 +253,6 @@ public class OAuth2Authorization implements Serializable {
*/
public static final String INVALIDATED_METADATA_NAME = TOKEN_METADATA_NAMESPACE.concat("invalidated");
- /**
- * The name of the metadata that indicates if access has been denied by the resource owner.
- * Used with the OAuth 2.0 Device Authorization Grant.
- */
- public static final String ACCESS_DENIED_METADATA_NAME = TOKEN_METADATA_NAMESPACE.concat("access_denied");
-
- /**
- * The name of the metadata that indicates if access has been denied by the resource owner.
- * Used with the OAuth 2.0 Device Authorization Grant.
- */
- public static final String ACCESS_GRANTED_METADATA_NAME = TOKEN_METADATA_NAMESPACE.concat("access_granted");
-
/**
* The name of the metadata used for the claims of the token.
*/
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimAccessor.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimAccessor.java
index 8767843f..089944d1 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimAccessor.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimAccessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020-2022 the original author or authors.
+ * Copyright 2020-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@ import org.springframework.security.oauth2.core.ClaimAccessor;
* @see OAuth2AuthorizationServerMetadataClaimNames
* @see 2. Authorization Server Metadata
* @see 3. OpenID Provider Metadata
+ * @see 4. Device Authorization Grant Metadata
*/
public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAccessor {
@@ -51,6 +52,16 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc
return getClaimAsURL(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT);
}
+ /**
+ * Returns the {@code URL} of the OAuth 2.0 Device Authorization Endpoint {@code (device_authorization_endpoint)}.
+ *
+ * @return the {@code URL} of the OAuth 2.0 Device Authorization Endpoint
+ * @since 1.1
+ */
+ default URL getDeviceAuthorizationEndpoint() {
+ return getClaimAsURL(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT);
+ }
+
/**
* Returns the {@code URL} of the OAuth 2.0 Token Endpoint {@code (token_endpoint)}.
*
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimNames.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimNames.java
index a4fb116a..831e0ec7 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimNames.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimNames.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020-2022 the original author or authors.
+ * Copyright 2020-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ package org.springframework.security.oauth2.server.authorization;
* @since 0.1.1
* @see 2. Authorization Server Metadata
* @see 3. OpenID Provider Metadata
+ * @see 4. Device Authorization Grant Metadata
*/
public class OAuth2AuthorizationServerMetadataClaimNames {
@@ -36,6 +37,12 @@ public class OAuth2AuthorizationServerMetadataClaimNames {
*/
public static final String AUTHORIZATION_ENDPOINT = "authorization_endpoint";
+ /**
+ * {@code device_authorization_endpoint} - the {@code URL} of the OAuth 2.0 Device Authorization Endpoint
+ * @since 1.1
+ */
+ public static final String DEVICE_AUTHORIZATION_ENDPOINT = "device_authorization_endpoint";
+
/**
* {@code token_endpoint} - the {@code URL} of the OAuth 2.0 Token Endpoint
*/
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationContext.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationContext.java
index 343526fd..c8e572c7 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationContext.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020-2023 the original author or authors.
+ * Copyright 2020-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -113,10 +113,6 @@ public final class OAuth2AuthorizationConsentAuthenticationContext implements OA
super(authentication);
}
- private Builder(OAuth2DeviceAuthorizationConsentAuthenticationToken authentication) {
- super(authentication);
- }
-
/**
* Sets the {@link OAuth2AuthorizationConsent.Builder authorization consent builder}.
*
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationProvider.java
index 9603393d..8cdd789b 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationProvider.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationProvider.java
@@ -91,6 +91,12 @@ public final class OAuth2AuthorizationConsentAuthenticationProvider implements A
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
+ if (authentication instanceof OAuth2DeviceAuthorizationConsentAuthenticationToken) {
+ // This is NOT an OAuth 2.0 Authorization Consent for the Authorization Code Grant,
+ // return null and let OAuth2DeviceAuthorizationConsentAuthenticationProvider handle it instead
+ return null;
+ }
+
OAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthentication =
(OAuth2AuthorizationConsentAuthenticationToken) authentication;
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationConsentAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationConsentAuthenticationProvider.java
index 07e8f584..a3cc37aa 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationConsentAuthenticationProvider.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationConsentAuthenticationProvider.java
@@ -15,7 +15,6 @@
*/
package org.springframework.security.oauth2.server.authorization.authentication;
-import java.security.Principal;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@@ -24,6 +23,7 @@ import java.util.function.Consumer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
@@ -33,7 +33,6 @@ import org.springframework.security.oauth2.core.OAuth2DeviceCode;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.OAuth2UserCode;
-import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;
@@ -45,8 +44,8 @@ import org.springframework.security.oauth2.server.authorization.client.Registere
import org.springframework.util.Assert;
/**
- * An {@link AuthenticationProvider} implementation for the OAuth 2.0 Authorization Consent
- * used in the Device Authorization Grant.
+ * An {@link AuthenticationProvider} implementation for the Device Authorization Consent
+ * used in the OAuth 2.0 Device Authorization Grant.
*
* @author Steve Riesenberg
* @since 1.1
@@ -61,7 +60,7 @@ import org.springframework.util.Assert;
*/
public final class OAuth2DeviceAuthorizationConsentAuthenticationProvider implements AuthenticationProvider {
- private static final String DEFAULT_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1";
+ private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";
static final OAuth2TokenType STATE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.STATE);
private final Log logger = LogFactory.getLog(getClass());
@@ -104,7 +103,11 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationProvider implem
this.logger.trace("Retrieved authorization with device authorization consent state");
}
+ // The authorization must be associated to the current principal
Authentication principal = (Authentication) deviceAuthorizationConsentAuthentication.getPrincipal();
+ if (!isPrincipalAuthenticated(principal) || !principal.getName().equals(authorization.getPrincipalName())) {
+ throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE);
+ }
RegisteredClient registeredClient = this.registeredClientRepository.findByClientId(
deviceAuthorizationConsentAuthentication.getClientId());
@@ -116,12 +119,8 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationProvider implem
this.logger.trace("Retrieved registered client");
}
- OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(
- OAuth2AuthorizationRequest.class.getName());
- Set requestedScopes = authorizationRequest.getScopes();
- Set authorizedScopes = deviceAuthorizationConsentAuthentication.getScopes() != null ?
- new HashSet<>(deviceAuthorizationConsentAuthentication.getScopes()) :
- new HashSet<>();
+ Set requestedScopes = authorization.getAttribute(OAuth2ParameterNames.SCOPE);
+ Set authorizedScopes = new HashSet<>(deviceAuthorizationConsentAuthentication.getScopes());
if (!requestedScopes.containsAll(authorizedScopes)) {
throwError(OAuth2ErrorCodes.INVALID_SCOPE, OAuth2ParameterNames.SCOPE);
}
@@ -162,7 +161,6 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationProvider implem
.authorizationConsent(authorizationConsentBuilder)
.registeredClient(registeredClient)
.authorization(authorization)
- .authorizationRequest(authorizationRequest)
.build();
// @formatter:on
this.authorizationConsentCustomizer.accept(authorizationConsentAuthenticationContext);
@@ -187,15 +185,16 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationProvider implem
}
authorization = OAuth2Authorization.from(authorization)
.token(deviceCodeToken.getToken(), metadata ->
- metadata.put(OAuth2Authorization.Token.ACCESS_DENIED_METADATA_NAME, true))
+ metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true))
.token(userCodeToken.getToken(), metadata ->
metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true))
+ .attributes(attrs -> attrs.remove(OAuth2ParameterNames.STATE))
.build();
this.authorizationService.save(authorization);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Invalidated device code and user code because authorization consent was denied");
}
- throw new OAuth2AuthenticationException(OAuth2ErrorCodes.ACCESS_DENIED);
+ throwError(OAuth2ErrorCodes.ACCESS_DENIED, OAuth2ParameterNames.CLIENT_ID);
}
OAuth2AuthorizationConsent authorizationConsent = authorizationConsentBuilder.build();
@@ -206,26 +205,23 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationProvider implem
}
}
- OAuth2Authorization updatedAuthorization = OAuth2Authorization.from(authorization)
- .principalName(principal.getName())
+ authorization = OAuth2Authorization.from(authorization)
.authorizedScopes(authorizedScopes)
- .token(deviceCodeToken.getToken(), metadata -> metadata
- .put(OAuth2Authorization.Token.ACCESS_GRANTED_METADATA_NAME, true))
- .token(userCodeToken.getToken(), metadata -> metadata
- .put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true))
- .attribute(Principal.class.getName(), principal)
+ .token(userCodeToken.getToken(), metadata ->
+ metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true))
.attributes(attrs -> attrs.remove(OAuth2ParameterNames.STATE))
+ .attributes(attrs -> attrs.remove(OAuth2ParameterNames.SCOPE))
.build();
- this.authorizationService.save(updatedAuthorization);
+ this.authorizationService.save(authorization);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Saved authorization with authorized scopes");
// This log is kept separate for consistency with other providers
- this.logger.trace("Authenticated authorization consent request");
+ this.logger.trace("Authenticated device authorization consent request");
}
- return new OAuth2DeviceVerificationAuthenticationToken(registeredClient.getClientId(), principal,
- deviceAuthorizationConsentAuthentication.getUserCode());
+ return new OAuth2DeviceVerificationAuthenticationToken(principal,
+ deviceAuthorizationConsentAuthentication.getUserCode(), registeredClient.getClientId());
}
@Override
@@ -244,10 +240,9 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationProvider implem
* prior to {@link OAuth2AuthorizationConsentService#save(OAuth2AuthorizationConsent)}.
*
The {@link Authentication} of type
* {@link OAuth2DeviceAuthorizationConsentAuthenticationToken}.
- *
The {@link RegisteredClient} associated with the authorization request.
+ *
The {@link RegisteredClient} associated with the device authorization request.
*
The {@link OAuth2Authorization} associated with the state token presented in the
- * authorization consent request.
- *
The {@link OAuth2AuthorizationRequest} associated with the authorization consent request.
+ * device authorization consent request.
*
*
* @param authorizationConsentCustomizer the {@code Consumer} providing access to the
@@ -258,8 +253,14 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationProvider implem
this.authorizationConsentCustomizer = authorizationConsentCustomizer;
}
+ private static boolean isPrincipalAuthenticated(Authentication principal) {
+ return principal != null &&
+ !AnonymousAuthenticationToken.class.isAssignableFrom(principal.getClass()) &&
+ principal.isAuthenticated();
+ }
+
private static void throwError(String errorCode, String parameterName) {
- OAuth2Error error = new OAuth2Error(errorCode, "OAuth 2.0 Parameter: " + parameterName, DEFAULT_ERROR_URI);
+ OAuth2Error error = new OAuth2Error(errorCode, "OAuth 2.0 Parameter: " + parameterName, ERROR_URI);
throw new OAuth2AuthenticationException(error);
}
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationConsentAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationConsentAuthenticationToken.java
index 20cf0b0c..fbfe3d35 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationConsentAuthenticationToken.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationConsentAuthenticationToken.java
@@ -21,16 +21,19 @@ import java.util.Map;
import java.util.Set;
import org.springframework.lang.Nullable;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.server.authorization.util.SpringAuthorizationServerVersion;
import org.springframework.util.Assert;
/**
- * An {@link Authentication} implementation for the Authorization Consent used
+ * An {@link Authentication} implementation for the Device Authorization Consent used
* in the OAuth 2.0 Device Authorization Grant.
*
* @author Steve Riesenberg
* @since 1.1
+ * @see AbstractAuthenticationToken
+ * @see OAuth2DeviceAuthorizationConsentAuthenticationProvider
*/
public class OAuth2DeviceAuthorizationConsentAuthenticationToken extends OAuth2AuthorizationConsentAuthenticationToken {
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
@@ -43,7 +46,7 @@ public class OAuth2DeviceAuthorizationConsentAuthenticationToken extends OAuth2A
* @param authorizationUri the authorization URI
* @param clientId the client identifier
* @param principal the {@code Principal} (Resource Owner)
- * @param userCode the user code associated with the device authorization request
+ * @param userCode the user code associated with the device authorization response
* @param state the state
* @param authorizedScopes the authorized scope(s)
* @param additionalParameters the additional parameters
@@ -64,7 +67,7 @@ public class OAuth2DeviceAuthorizationConsentAuthenticationToken extends OAuth2A
* @param authorizationUri the authorization URI
* @param clientId the client identifier
* @param principal the {@code Principal} (Resource Owner)
- * @param userCode the user code associated with the device authorization request
+ * @param userCode the user code associated with the device authorization response
* @param state the state
* @param requestedScopes the requested scope(s)
* @param authorizedScopes the authorized scope(s)
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationRequestAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationRequestAuthenticationProvider.java
index 70a4a220..86edb109 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationRequestAuthenticationProvider.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationRequestAuthenticationProvider.java
@@ -18,6 +18,7 @@ package org.springframework.security.oauth2.server.authorization.authentication;
import java.security.Principal;
import java.time.Instant;
import java.util.Base64;
+import java.util.HashSet;
import java.util.Set;
import org.apache.commons.logging.Log;
@@ -37,7 +38,6 @@ import org.springframework.security.oauth2.core.OAuth2DeviceCode;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.OAuth2UserCode;
-import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
@@ -48,6 +48,7 @@ import org.springframework.security.oauth2.server.authorization.token.DefaultOAu
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.util.Assert;
+import org.springframework.util.CollectionUtils;
import static org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthenticationProviderUtils.getAuthenticatedClientElseThrowInvalidClient;
@@ -100,11 +101,19 @@ public final class OAuth2DeviceAuthorizationRequestAuthenticationProvider implem
this.logger.trace("Retrieved registered client");
}
- // Validate client grant types has device_code grant type
if (!registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.DEVICE_CODE)) {
throwError(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT, OAuth2ParameterNames.CLIENT_ID);
}
+ Set requestedScopes = deviceAuthorizationRequestAuthentication.getScopes();
+ if (!CollectionUtils.isEmpty(requestedScopes)) {
+ for (String requestedScope : requestedScopes) {
+ if (!registeredClient.getScopes().contains(requestedScope)) {
+ throwError(OAuth2ErrorCodes.INVALID_SCOPE, OAuth2ParameterNames.SCOPE);
+ }
+ }
+ }
+
if (this.logger.isTraceEnabled()) {
this.logger.trace("Validated device authorization request parameters");
}
@@ -128,7 +137,7 @@ public final class OAuth2DeviceAuthorizationRequestAuthenticationProvider implem
}
if (this.logger.isTraceEnabled()) {
- logger.trace("Generated device code");
+ this.logger.trace("Generated device code");
}
// Generate a low-entropy string to use as the user code
@@ -141,21 +150,9 @@ public final class OAuth2DeviceAuthorizationRequestAuthenticationProvider implem
}
if (this.logger.isTraceEnabled()) {
- logger.trace("Generated user code");
+ this.logger.trace("Generated user code");
}
- String authorizationUri = deviceAuthorizationRequestAuthentication.getAuthorizationUri();
-
- Set requestedScopes = deviceAuthorizationRequestAuthentication.getScopes();
-
- // @formatter:off
- OAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()
- .authorizationUri(authorizationUri)
- .clientId(registeredClient.getClientId())
- .scopes(requestedScopes)
- .build();
- // @formatter:on
-
// @formatter:off
OAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(registeredClient)
.principalName(clientPrincipal.getName())
@@ -163,7 +160,7 @@ public final class OAuth2DeviceAuthorizationRequestAuthenticationProvider implem
.token(deviceCode)
.token(userCode)
.attribute(Principal.class.getName(), clientPrincipal)
- .attribute(OAuth2AuthorizationRequest.class.getName(), authorizationRequest)
+ .attribute(OAuth2ParameterNames.SCOPE, new HashSet<>(requestedScopes))
.build();
// @formatter:on
this.authorizationService.save(authorization);
@@ -176,7 +173,8 @@ public final class OAuth2DeviceAuthorizationRequestAuthenticationProvider implem
this.logger.trace("Authenticated device authorization request");
}
- return new OAuth2DeviceAuthorizationRequestAuthenticationToken(clientPrincipal, requestedScopes, deviceCode, userCode);
+ return new OAuth2DeviceAuthorizationRequestAuthenticationToken(
+ clientPrincipal, requestedScopes, deviceCode, userCode);
}
@Override
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationRequestAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationRequestAuthenticationToken.java
index 41429364..c80d9b64 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationRequestAuthenticationToken.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationRequestAuthenticationToken.java
@@ -16,6 +16,7 @@
package org.springframework.security.oauth2.server.authorization.authentication;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@@ -29,12 +30,13 @@ import org.springframework.security.oauth2.server.authorization.util.SpringAutho
import org.springframework.util.Assert;
/**
- * An {@link Authentication} implementation for the OAuth 2.0 Device Authorization Request
- * used in the Device Authorization Grant.
+ * An {@link Authentication} implementation for the Device Authorization Request
+ * used in the OAuth 2.0 Device Authorization Grant.
*
* @author Steve Riesenberg
* @since 1.1
* @see AbstractAuthenticationToken
+ * @see OAuth2ClientAuthenticationToken
* @see OAuth2DeviceAuthorizationRequestAuthenticationProvider
*/
public class OAuth2DeviceAuthorizationRequestAuthenticationToken extends AbstractAuthenticationToken {
@@ -65,7 +67,10 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationToken extends Abstrac
scopes != null ?
new HashSet<>(scopes) :
Collections.emptySet());
- this.additionalParameters = additionalParameters;
+ this.additionalParameters = Collections.unmodifiableMap(
+ additionalParameters != null ?
+ new HashMap<>(additionalParameters) :
+ Collections.emptyMap());
this.deviceCode = null;
this.userCode = null;
}
@@ -109,16 +114,16 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationToken extends Abstrac
/**
* Returns the authorization {@code URI}.
*
- * @return the authorization {@code URI}.
+ * @return the authorization {@code URI}
*/
public String getAuthorizationUri() {
- return authorizationUri;
+ return this.authorizationUri;
}
/**
* Returns the requested scope(s).
*
- * @return the requested scope(s).
+ * @return the requested scope(s)
*/
public Set getScopes() {
return this.scopes;
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceCodeAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceCodeAuthenticationProvider.java
index 03653937..5e08ea54 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceCodeAuthenticationProvider.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceCodeAuthenticationProvider.java
@@ -26,7 +26,6 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClaimAccessor;
-import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2DeviceCode;
@@ -34,7 +33,7 @@ import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.core.OAuth2Token;
-import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
+import org.springframework.security.oauth2.core.OAuth2UserCode;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
@@ -46,8 +45,11 @@ import org.springframework.security.oauth2.server.authorization.token.OAuth2Toke
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.util.Assert;
+import static org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthenticationProviderUtils.getAuthenticatedClientElseThrowInvalidClient;
+
/**
- * An {@link AuthenticationProvider} implementation for the OAuth 2.0 Device Authorization Grant.
+ * An {@link AuthenticationProvider} implementation for the Device Access Token Request
+ * used in the OAuth 2.0 Device Authorization Grant.
*
* @author Steve Riesenberg
* @since 1.1
@@ -94,8 +96,8 @@ public final class OAuth2DeviceCodeAuthenticationProvider implements Authenticat
OAuth2DeviceCodeAuthenticationToken deviceCodeAuthentication =
(OAuth2DeviceCodeAuthenticationToken) authentication;
- OAuth2ClientAuthenticationToken clientPrincipal = OAuth2AuthenticationProviderUtils
- .getAuthenticatedClientElseThrowInvalidClient(deviceCodeAuthentication);
+ OAuth2ClientAuthenticationToken clientPrincipal =
+ getAuthenticatedClientElseThrowInvalidClient(deviceCodeAuthentication);
RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();
if (this.logger.isTraceEnabled()) {
@@ -112,19 +114,17 @@ public final class OAuth2DeviceCodeAuthenticationProvider implements Authenticat
this.logger.trace("Retrieved authorization with device code");
}
- OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(
- OAuth2AuthorizationRequest.class.getName());
-
+ OAuth2Authorization.Token userCode = authorization.getToken(OAuth2UserCode.class);
OAuth2Authorization.Token deviceCode = authorization.getToken(OAuth2DeviceCode.class);
- if (!registeredClient.getClientId().equals(authorizationRequest.getClientId())) {
+ if (!registeredClient.getId().equals(authorization.getRegisteredClientId())) {
if (!deviceCode.isInvalidated()) {
// Invalidate the device code given that a different client is attempting to use it
authorization = OAuth2AuthenticationProviderUtils.invalidate(authorization, deviceCode.getToken());
this.authorizationService.save(authorization);
if (this.logger.isWarnEnabled()) {
this.logger.warn(LogMessage.format(
- "Invalidated device code used by registered client '%s'", registeredClient.getId()));
+ "Invalidated device code used by registered client '%s'", authorization.getRegisteredClientId()));
}
}
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
@@ -133,29 +133,6 @@ public final class OAuth2DeviceCodeAuthenticationProvider implements Authenticat
// In https://www.rfc-editor.org/rfc/rfc8628.html#section-3.5,
// the following error codes are defined:
- // access_denied
- // The authorization request was denied.
- if (Boolean.TRUE.equals(deviceCode.getMetadata(OAuth2Authorization.Token.ACCESS_DENIED_METADATA_NAME))) {
- OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.ACCESS_DENIED, null, DEVICE_ERROR_URI);
- throw new OAuth2AuthenticationException(error);
- }
-
- // expired_token
- // The "device_code" has expired, and the device authorization
- // session has concluded. The client MAY commence a new device
- // authorization request but SHOULD wait for user interaction before
- // restarting to avoid unnecessary polling.
- if (deviceCode.isExpired()) {
- OAuth2Error error = new OAuth2Error(EXPIRED_TOKEN, null, DEVICE_ERROR_URI);
- throw new OAuth2AuthenticationException(error);
- }
-
- // slow_down
- // A variant of "authorization_pending", the authorization request is
- // still pending and polling should continue, but the interval MUST
- // be increased by 5 seconds for this and all subsequent requests.
- // Note: This error is not handled in the framework.
-
// authorization_pending
// The authorization request is still pending as the end user hasn't
// yet completed the user-interaction steps (Section 3.3). The
@@ -166,17 +143,43 @@ public final class OAuth2DeviceCodeAuthenticationProvider implements Authenticat
// Section 3.2), or 5 seconds if none was provided, and respect any
// increase in the polling interval required by the "slow_down"
// error.
- if (!Boolean.TRUE.equals(deviceCode.getMetadata(OAuth2Authorization.Token.ACCESS_GRANTED_METADATA_NAME))) {
+ if (!userCode.isInvalidated()) {
OAuth2Error error = new OAuth2Error(AUTHORIZATION_PENDING, null, DEVICE_ERROR_URI);
throw new OAuth2AuthenticationException(error);
}
- if (!deviceCode.isActive()) {
- throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
+ // slow_down
+ // A variant of "authorization_pending", the authorization request is
+ // still pending and polling should continue, but the interval MUST
+ // be increased by 5 seconds for this and all subsequent requests.
+ // NOTE: This error is not handled in the framework.
+
+ // access_denied
+ // The authorization request was denied.
+ if (deviceCode.isInvalidated()) {
+ OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.ACCESS_DENIED, null, DEVICE_ERROR_URI);
+ throw new OAuth2AuthenticationException(error);
+ }
+
+ // expired_token
+ // The "device_code" has expired, and the device authorization
+ // session has concluded. The client MAY commence a new device
+ // authorization request but SHOULD wait for user interaction before
+ // restarting to avoid unnecessary polling.
+ if (deviceCode.isExpired()) {
+ // Invalidate the device code
+ authorization = OAuth2AuthenticationProviderUtils.invalidate(authorization, deviceCode.getToken());
+ this.authorizationService.save(authorization);
+ if (this.logger.isWarnEnabled()) {
+ this.logger.warn(LogMessage.format(
+ "Invalidated device code used by registered client '%s'", authorization.getRegisteredClientId()));
+ }
+ OAuth2Error error = new OAuth2Error(EXPIRED_TOKEN, null, DEVICE_ERROR_URI);
+ throw new OAuth2AuthenticationException(error);
}
if (this.logger.isTraceEnabled()) {
- this.logger.trace("Validated token request parameters");
+ this.logger.trace("Validated device token request parameters");
}
// @formatter:off
@@ -222,10 +225,7 @@ public final class OAuth2DeviceCodeAuthenticationProvider implements Authenticat
// ----- Refresh token -----
OAuth2RefreshToken refreshToken = null;
- if (registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.REFRESH_TOKEN) &&
- // Do not issue refresh token to public client
- !clientPrincipal.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.NONE)) {
-
+ if (registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.REFRESH_TOKEN)) {
tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build();
OAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext);
if (!(generatedRefreshToken instanceof OAuth2RefreshToken)) {
@@ -250,6 +250,10 @@ public final class OAuth2DeviceCodeAuthenticationProvider implements Authenticat
this.logger.trace("Saved authorization");
}
+ if (this.logger.isTraceEnabled()) {
+ this.logger.trace("Authenticated device token request");
+ }
+
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, refreshToken);
}
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceCodeAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceCodeAuthenticationToken.java
index 2df84805..29f7cfdf 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceCodeAuthenticationToken.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceCodeAuthenticationToken.java
@@ -23,7 +23,8 @@ import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.util.Assert;
/**
- * An {@link Authentication} implementation used for the OAuth 2.0 Device Authorization Grant.
+ * An {@link Authentication} implementation for the Device Access Token Request
+ * used in the OAuth 2.0 Device Authorization Grant.
*
* @author Steve Riesenberg
* @since 1.1
@@ -41,7 +42,8 @@ public class OAuth2DeviceCodeAuthenticationToken extends OAuth2AuthorizationGran
* @param clientPrincipal the authenticated client principal
* @param additionalParameters the additional parameters
*/
- public OAuth2DeviceCodeAuthenticationToken(String deviceCode, Authentication clientPrincipal, @Nullable Map additionalParameters) {
+ public OAuth2DeviceCodeAuthenticationToken(String deviceCode, Authentication clientPrincipal,
+ @Nullable Map additionalParameters) {
super(AuthorizationGrantType.DEVICE_CODE, clientPrincipal, additionalParameters);
Assert.hasText(deviceCode, "deviceCode cannot be empty");
this.deviceCode = deviceCode;
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationProvider.java
index e4ca833d..d0b0c2e9 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationProvider.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationProvider.java
@@ -29,10 +29,8 @@ import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.keygen.Base64StringKeyGenerator;
import org.springframework.security.crypto.keygen.StringKeyGenerator;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
-import org.springframework.security.oauth2.core.OAuth2DeviceCode;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.OAuth2UserCode;
-import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;
@@ -41,16 +39,17 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
+import org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;
+import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.util.Assert;
/**
- * An {@link AuthenticationProvider} implementation for the Verification {@code URI}
- * (submission of the user code)} used in the OAuth 2.0 Device Authorization Grant.
+ * An {@link AuthenticationProvider} implementation for the Device Verification Request
+ * (submission of the user code) used in the OAuth 2.0 Device Authorization Grant.
*
* @author Steve Riesenberg
* @since 1.1
* @see OAuth2DeviceVerificationAuthenticationToken
- * @see OAuth2AuthorizationConsent
* @see OAuth2DeviceAuthorizationRequestAuthenticationProvider
* @see OAuth2DeviceAuthorizationConsentAuthenticationProvider
* @see OAuth2DeviceCodeAuthenticationProvider
@@ -105,6 +104,15 @@ public final class OAuth2DeviceVerificationAuthenticationProvider implements Aut
this.logger.trace("Retrieved authorization with user code");
}
+ Authentication principal = (Authentication) deviceVerificationAuthentication.getPrincipal();
+ if (!isPrincipalAuthenticated(principal)) {
+ if (this.logger.isTraceEnabled()) {
+ this.logger.trace("Did not authenticate device verification request since principal not authenticated");
+ }
+ // Return the device verification request as-is where isAuthenticated() is false
+ return deviceVerificationAuthentication;
+ }
+
RegisteredClient registeredClient = this.registeredClientRepository.findById(
authorization.getRegisteredClientId());
@@ -112,31 +120,21 @@ public final class OAuth2DeviceVerificationAuthenticationProvider implements Aut
this.logger.trace("Retrieved registered client");
}
- Authentication principal = (Authentication) deviceVerificationAuthentication.getPrincipal();
- if (!isPrincipalAuthenticated(principal)) {
- if (this.logger.isTraceEnabled()) {
- this.logger.trace("Did not authenticate device authorization request since principal not authenticated");
- }
- // Return the authorization request as-is where isAuthenticated() is false
- return deviceVerificationAuthentication;
- }
-
- OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName());
+ Set requestedScopes = authorization.getAttribute(OAuth2ParameterNames.SCOPE);
OAuth2AuthorizationConsent currentAuthorizationConsent = this.authorizationConsentService.findById(
registeredClient.getId(), principal.getName());
- Set currentAuthorizedScopes = currentAuthorizationConsent != null ?
- currentAuthorizationConsent.getScopes() : null;
-
- if (requiresAuthorizationConsent(registeredClient, authorizationRequest, currentAuthorizationConsent)) {
+ if (requiresAuthorizationConsent(requestedScopes, currentAuthorizationConsent)) {
String state = DEFAULT_STATE_GENERATOR.generateKey();
authorization = OAuth2Authorization.from(authorization)
+ .principalName(principal.getName())
+ .attribute(Principal.class.getName(), principal)
.attribute(OAuth2ParameterNames.STATE, state)
.build();
if (this.logger.isTraceEnabled()) {
- logger.trace("Generated authorization consent state");
+ this.logger.trace("Generated device authorization consent state");
}
this.authorizationService.save(authorization);
@@ -145,33 +143,39 @@ public final class OAuth2DeviceVerificationAuthenticationProvider implements Aut
this.logger.trace("Saved authorization");
}
- return new OAuth2DeviceAuthorizationConsentAuthenticationToken(authorizationRequest.getAuthorizationUri(),
+ Set currentAuthorizedScopes = currentAuthorizationConsent != null ?
+ currentAuthorizationConsent.getScopes() : null;
+
+ AuthorizationServerSettings authorizationServerSettings =
+ AuthorizationServerContextHolder.getContext().getAuthorizationServerSettings();
+ String deviceVerificationUri = authorizationServerSettings.getDeviceVerificationEndpoint();
+
+ return new OAuth2DeviceAuthorizationConsentAuthenticationToken(deviceVerificationUri,
registeredClient.getClientId(), principal, deviceVerificationAuthentication.getUserCode(), state,
- authorizationRequest.getScopes(), currentAuthorizedScopes);
+ requestedScopes, currentAuthorizedScopes);
}
- OAuth2Authorization.Token deviceCode = authorization.getToken(OAuth2DeviceCode.class);
OAuth2Authorization.Token userCode = authorization.getToken(OAuth2UserCode.class);
- OAuth2Authorization updatedAuthorization = OAuth2Authorization.from(authorization)
+ // @formatter:off
+ authorization = OAuth2Authorization.from(authorization)
.principalName(principal.getName())
- .authorizedScopes(authorizationRequest.getScopes())
- .token(deviceCode.getToken(), metadata -> metadata
- .put(OAuth2Authorization.Token.ACCESS_GRANTED_METADATA_NAME, true))
+ .authorizedScopes(requestedScopes)
.token(userCode.getToken(), metadata -> metadata
.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true))
.attribute(Principal.class.getName(), principal)
- .attributes(attrs -> attrs.remove(OAuth2ParameterNames.STATE))
+ .attributes(attributes -> attributes.remove(OAuth2ParameterNames.SCOPE))
.build();
- this.authorizationService.save(updatedAuthorization);
+ // @formatter:on
+ this.authorizationService.save(authorization);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Saved authorization with authorized scopes");
// This log is kept separate for consistency with other providers
- this.logger.trace("Authenticated authorization consent request");
+ this.logger.trace("Authenticated device verification request");
}
- return new OAuth2DeviceVerificationAuthenticationToken(registeredClient.getClientId(), principal,
- deviceVerificationAuthentication.getUserCode());
+ return new OAuth2DeviceVerificationAuthenticationToken(principal,
+ deviceVerificationAuthentication.getUserCode(), registeredClient.getClientId());
}
@Override
@@ -179,15 +183,11 @@ public final class OAuth2DeviceVerificationAuthenticationProvider implements Aut
return OAuth2DeviceVerificationAuthenticationToken.class.isAssignableFrom(authentication);
}
- private static boolean requiresAuthorizationConsent(RegisteredClient registeredClient,
- OAuth2AuthorizationRequest authorizationRequest, OAuth2AuthorizationConsent authorizationConsent) {
-
- if (!registeredClient.getClientSettings().isRequireAuthorizationConsent()) {
- return false;
- }
+ private static boolean requiresAuthorizationConsent(
+ Set requestedScopes, OAuth2AuthorizationConsent authorizationConsent) {
if (authorizationConsent != null &&
- authorizationConsent.getScopes().containsAll(authorizationRequest.getScopes())) {
+ authorizationConsent.getScopes().containsAll(requestedScopes)) {
return false;
}
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationToken.java
index f24b0600..40d40de2 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationToken.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationToken.java
@@ -16,6 +16,7 @@
package org.springframework.security.oauth2.server.authorization.authentication;
import java.util.Collections;
+import java.util.HashMap;
import java.util.Map;
import org.springframework.lang.Nullable;
@@ -25,7 +26,7 @@ import org.springframework.security.oauth2.server.authorization.util.SpringAutho
import org.springframework.util.Assert;
/**
- * An {@link Authentication} implementation for the Verification {@code URI}
+ * An {@link Authentication} implementation for the Device Verification Request
* (submission of the user code) used in the OAuth 2.0 Device Authorization Grant.
*
* @author Steve Riesenberg
@@ -35,44 +36,47 @@ import org.springframework.util.Assert;
*/
public class OAuth2DeviceVerificationAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
- private final String clientId;
private final Authentication principal;
private final String userCode;
private final Map additionalParameters;
+ private final String clientId;
/**
* Constructs an {@code OAuth2DeviceVerificationAuthenticationToken} using the provided parameters.
*
* @param principal the {@code Principal} (Resource Owner)
- * @param userCode the user code associated with the device authorization request
+ * @param userCode the user code associated with the device authorization response
* @param additionalParameters the additional parameters
*/
public OAuth2DeviceVerificationAuthenticationToken(Authentication principal, String userCode,
@Nullable Map additionalParameters) {
super(Collections.emptyList());
Assert.notNull(principal, "principal cannot be null");
- Assert.notNull(userCode, "userCode cannot be null");
- this.clientId = null;
+ Assert.hasText(userCode, "userCode cannot be empty");
this.principal = principal;
this.userCode = userCode;
- this.additionalParameters = additionalParameters;
+ this.additionalParameters = Collections.unmodifiableMap(
+ additionalParameters != null ?
+ new HashMap<>(additionalParameters) :
+ Collections.emptyMap());
+ this.clientId = null;
}
/**
* Constructs an {@code OAuth2DeviceVerificationAuthenticationToken} using the provided parameters.
*
- * @param clientId the client identifier
* @param principal the {@code Principal} (Resource Owner)
- * @param userCode the user code associated with the device authorization request
+ * @param userCode the user code associated with the device authorization response
+ * @param clientId the client identifier
*/
- public OAuth2DeviceVerificationAuthenticationToken(String clientId, Authentication principal, String userCode) {
+ public OAuth2DeviceVerificationAuthenticationToken(Authentication principal, String userCode, String clientId) {
super(Collections.emptyList());
- Assert.hasText(clientId, "clientId cannot be empty");
Assert.notNull(principal, "principal cannot be null");
- Assert.notNull(userCode, "userCode cannot be null");
- this.clientId = clientId;
+ Assert.hasText(userCode, "userCode cannot be empty");
+ Assert.hasText(clientId, "clientId cannot be empty");
this.principal = principal;
this.userCode = userCode;
+ this.clientId = clientId;
this.additionalParameters = null;
setAuthenticated(true);
}
@@ -87,15 +91,6 @@ public class OAuth2DeviceVerificationAuthenticationToken extends AbstractAuthent
return "";
}
- /**
- * Returns the client identifier.
- *
- * @return the client identifier
- */
- public String getClientId() {
- return this.clientId;
- }
-
/**
* Returns the user code.
*
@@ -114,4 +109,13 @@ public class OAuth2DeviceVerificationAuthenticationToken extends AbstractAuthent
return this.additionalParameters;
}
+ /**
+ * Returns the client identifier.
+ *
+ * @return the client identifier
+ */
+ public String getClientId() {
+ return this.clientId;
+ }
+
}
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerConfigurer.java
index 121d15a2..dc6d204e 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerConfigurer.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerConfigurer.java
@@ -67,6 +67,8 @@ import org.springframework.util.Assert;
* @see OAuth2TokenEndpointConfigurer
* @see OAuth2TokenIntrospectionEndpointConfigurer
* @see OAuth2TokenRevocationEndpointConfigurer
+ * @see OAuth2DeviceAuthorizationEndpointConfigurer
+ * @see OAuth2DeviceVerificationEndpointConfigurer
* @see OidcConfigurer
* @see RegisteredClientRepository
* @see OAuth2AuthorizationService
@@ -316,7 +318,8 @@ public final class OAuth2AuthorizationServerConfigurer
new OrRequestMatcher(
getRequestMatcher(OAuth2TokenEndpointConfigurer.class),
getRequestMatcher(OAuth2TokenIntrospectionEndpointConfigurer.class),
- getRequestMatcher(OAuth2TokenRevocationEndpointConfigurer.class))
+ getRequestMatcher(OAuth2TokenRevocationEndpointConfigurer.class),
+ getRequestMatcher(OAuth2DeviceAuthorizationEndpointConfigurer.class))
);
}
}
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceAuthorizationEndpointConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceAuthorizationEndpointConfigurer.java
index b9e5d615..765548f3 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceAuthorizationEndpointConfigurer.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceAuthorizationEndpointConfigurer.java
@@ -26,7 +26,9 @@ import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
+import org.springframework.security.oauth2.core.endpoint.OAuth2DeviceAuthorizationResponse;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationRequestAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationRequestAuthenticationToken;
@@ -41,6 +43,7 @@ import org.springframework.security.web.authentication.AuthenticationSuccessHand
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
/**
* Configurer for the OAuth 2.0 Device Authorization Endpoint.
@@ -53,8 +56,8 @@ import org.springframework.util.Assert;
public final class OAuth2DeviceAuthorizationEndpointConfigurer extends AbstractOAuth2Configurer {
private RequestMatcher requestMatcher;
- private final List authenticationConverters = new ArrayList<>();
- private Consumer> authenticationConvertersConsumer = (authenticationConverters) -> {};
+ private final List deviceAuthorizationRequestConverters = new ArrayList<>();
+ private Consumer> deviceAuthorizationRequestConvertersConsumer = (deviceAuthorizationRequestConverters) -> {};
private final List authenticationProviders = new ArrayList<>();
private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> {};
private AuthenticationSuccessHandler deviceAuthorizationResponseHandler;
@@ -77,7 +80,7 @@ public final class OAuth2DeviceAuthorizationEndpointConfigurer extends AbstractO
*/
public OAuth2DeviceAuthorizationEndpointConfigurer deviceAuthorizationRequestConverter(AuthenticationConverter deviceAuthorizationRequestConverter) {
Assert.notNull(deviceAuthorizationRequestConverter, "deviceAuthorizationRequestConverter cannot be null");
- this.authenticationConverters.add(deviceAuthorizationRequestConverter);
+ this.deviceAuthorizationRequestConverters.add(deviceAuthorizationRequestConverter);
return this;
}
@@ -92,7 +95,7 @@ public final class OAuth2DeviceAuthorizationEndpointConfigurer extends AbstractO
public OAuth2DeviceAuthorizationEndpointConfigurer deviceAuthorizationRequestConverters(
Consumer> deviceAuthorizationRequestConvertersConsumer) {
Assert.notNull(deviceAuthorizationRequestConvertersConsumer, "deviceAuthorizationRequestConvertersConsumer cannot be null");
- this.authenticationConvertersConsumer = deviceAuthorizationRequestConvertersConsumer;
+ this.deviceAuthorizationRequestConvertersConsumer = deviceAuthorizationRequestConvertersConsumer;
return this;
}
@@ -125,7 +128,7 @@ public final class OAuth2DeviceAuthorizationEndpointConfigurer extends AbstractO
/**
* Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2DeviceAuthorizationRequestAuthenticationToken}
- * and returning the Device Authorization Response.
+ * and returning the {@link OAuth2DeviceAuthorizationResponse Device Authorization Response}.
*
* @param deviceAuthorizationResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2DeviceAuthorizationRequestAuthenticationToken}
* @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further configuration
@@ -136,10 +139,10 @@ public final class OAuth2DeviceAuthorizationEndpointConfigurer extends AbstractO
}
/**
- * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2DeviceAuthorizationRequestAuthenticationToken}
+ * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
* and returning the {@link OAuth2Error Error Response}.
*
- * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2DeviceAuthorizationRequestAuthenticationToken}
+ * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
* @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further configuration
*/
public OAuth2DeviceAuthorizationEndpointConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) {
@@ -184,10 +187,10 @@ public final class OAuth2DeviceAuthorizationEndpointConfigurer extends AbstractO
authenticationManager, authorizationServerSettings.getDeviceAuthorizationEndpoint());
List authenticationConverters = createDefaultAuthenticationConverters();
- if (!this.authenticationConverters.isEmpty()) {
- authenticationConverters.addAll(0, this.authenticationConverters);
+ if (!this.deviceAuthorizationRequestConverters.isEmpty()) {
+ authenticationConverters.addAll(0, this.deviceAuthorizationRequestConverters);
}
- this.authenticationConvertersConsumer.accept(authenticationConverters);
+ this.deviceAuthorizationRequestConvertersConsumer.accept(authenticationConverters);
deviceAuthorizationEndpointFilter.setAuthenticationConverter(
new DelegatingAuthenticationConverter(authenticationConverters));
if (this.deviceAuthorizationResponseHandler != null) {
@@ -196,7 +199,7 @@ public final class OAuth2DeviceAuthorizationEndpointConfigurer extends AbstractO
if (this.errorResponseHandler != null) {
deviceAuthorizationEndpointFilter.setAuthenticationFailureHandler(this.errorResponseHandler);
}
- if (this.verificationUri != null) {
+ if (StringUtils.hasText(this.verificationUri)) {
deviceAuthorizationEndpointFilter.setVerificationUri(this.verificationUri);
}
builder.addFilterAfter(postProcess(deviceAuthorizationEndpointFilter), AuthorizationFilter.class);
@@ -214,7 +217,7 @@ public final class OAuth2DeviceAuthorizationEndpointConfigurer extends AbstractO
return authenticationConverters;
}
- private List createDefaultAuthenticationProviders(HttpSecurity builder) {
+ private static List createDefaultAuthenticationProviders(HttpSecurity builder) {
List authenticationProviders = new ArrayList<>();
OAuth2AuthorizationService authorizationService = OAuth2ConfigurerUtils.getAuthorizationService(builder);
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceVerificationEndpointConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceVerificationEndpointConfigurer.java
index c973bddd..bc50f7b4 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceVerificationEndpointConfigurer.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceVerificationEndpointConfigurer.java
@@ -30,7 +30,6 @@ import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
-import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationConsentAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationConsentAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceVerificationAuthenticationProvider;
@@ -41,10 +40,10 @@ import org.springframework.security.oauth2.server.authorization.web.OAuth2Device
import org.springframework.security.oauth2.server.authorization.web.authentication.DelegatingAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2DeviceAuthorizationConsentAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2DeviceVerificationAuthenticationConverter;
-import org.springframework.security.web.access.intercept.AuthorizationFilter;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
+import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
@@ -62,8 +61,8 @@ import org.springframework.util.StringUtils;
public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOAuth2Configurer {
private RequestMatcher requestMatcher;
- private final List authenticationConverters = new ArrayList<>();
- private Consumer> authenticationConvertersConsumer = (authenticationConverters) -> {};
+ private final List deviceVerificationRequestConverters = new ArrayList<>();
+ private Consumer> deviceVerificationRequestConvertersConsumer = (deviceVerificationRequestConverters) -> {};
private final List authenticationProviders = new ArrayList<>();
private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> {};
private AuthenticationSuccessHandler deviceVerificationResponseHandler;
@@ -78,15 +77,15 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA
}
/**
- * Sets the {@link AuthenticationConverter} used when attempting to extract a Device Verification Request (or Consent) from {@link HttpServletRequest}
+ * Sets the {@link AuthenticationConverter} used when attempting to extract a Device Verification Request (or Device Authorization Consent) from {@link HttpServletRequest}
* to an instance of {@link OAuth2DeviceVerificationAuthenticationToken} or {@link OAuth2DeviceAuthorizationConsentAuthenticationToken} used for authenticating the request.
*
- * @param deviceVerificationRequestConverter the {@link AuthenticationConverter} used when attempting to extract a Device Authorization Request from {@link HttpServletRequest}
+ * @param deviceVerificationRequestConverter the {@link AuthenticationConverter} used when attempting to extract a Device Verification Request (or Device Authorization Consent) from {@link HttpServletRequest}
* @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further configuration
*/
public OAuth2DeviceVerificationEndpointConfigurer deviceVerificationRequestConverter(AuthenticationConverter deviceVerificationRequestConverter) {
Assert.notNull(deviceVerificationRequestConverter, "deviceVerificationRequestConverter cannot be null");
- this.authenticationConverters.add(deviceVerificationRequestConverter);
+ this.deviceVerificationRequestConverters.add(deviceVerificationRequestConverter);
return this;
}
@@ -101,14 +100,14 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA
public OAuth2DeviceVerificationEndpointConfigurer deviceVerificationRequestConverters(
Consumer> deviceVerificationRequestConvertersConsumer) {
Assert.notNull(deviceVerificationRequestConvertersConsumer, "deviceVerificationRequestConvertersConsumer cannot be null");
- this.authenticationConvertersConsumer = deviceVerificationRequestConvertersConsumer;
+ this.deviceVerificationRequestConvertersConsumer = deviceVerificationRequestConvertersConsumer;
return this;
}
/**
- * Adds an {@link AuthenticationProvider} used for authenticating an {@link OAuth2DeviceVerificationAuthenticationToken}.
+ * Adds an {@link AuthenticationProvider} used for authenticating an {@link OAuth2DeviceVerificationAuthenticationToken} or {@link OAuth2DeviceAuthorizationConsentAuthenticationToken}.
*
- * @param authenticationProvider an {@link AuthenticationProvider} used for authenticating an {@link OAuth2DeviceVerificationAuthenticationToken}
+ * @param authenticationProvider an {@link AuthenticationProvider} used for authenticating an {@link OAuth2DeviceVerificationAuthenticationToken} or {@link OAuth2DeviceAuthorizationConsentAuthenticationToken}
* @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further configuration
*/
public OAuth2DeviceVerificationEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) {
@@ -133,10 +132,10 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA
}
/**
- * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2DeviceAuthorizationConsentAuthenticationToken}
+ * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2DeviceVerificationAuthenticationToken}
* and returning the response.
*
- * @param deviceVerificationResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationToken}
+ * @param deviceVerificationResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2DeviceVerificationAuthenticationToken}
* @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further configuration
*/
public OAuth2DeviceVerificationEndpointConfigurer deviceVerificationResponseHandler(AuthenticationSuccessHandler deviceVerificationResponseHandler) {
@@ -166,9 +165,9 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA
*
*
*
{@code client_id} - the client identifier
- *
{@code scope} - a space-delimited list of scopes present in the authorization request
+ *
{@code scope} - a space-delimited list of scopes present in the device authorization request
*
{@code state} - a CSRF protection token
- *
@code code} - the user code
+ *
{@code user_code} - the user code
*
*
* In general, the consent page should create a form that submits
@@ -181,7 +180,7 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA
*
It must include the received {@code state} as an HTTP parameter
*
It must include the list of {@code scope}s the {@code Resource Owner}
* consented to as an HTTP parameter
- *
It must include the user {@code code} as an HTTP parameter
+ *
It must include the received {@code user_code} as an HTTP parameter
*
*
* @param consentPage the URI of the custom consent page to redirect to if consent is required (e.g. "/oauth2/consent")
@@ -198,9 +197,11 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA
OAuth2ConfigurerUtils.getAuthorizationServerSettings(builder);
this.requestMatcher = new OrRequestMatcher(
new AntPathRequestMatcher(
- authorizationServerSettings.getDeviceVerificationEndpoint(), HttpMethod.GET.name()),
+ authorizationServerSettings.getDeviceVerificationEndpoint(),
+ HttpMethod.GET.name()),
new AntPathRequestMatcher(
- authorizationServerSettings.getDeviceVerificationEndpoint(), HttpMethod.POST.name()));
+ authorizationServerSettings.getDeviceVerificationEndpoint(),
+ HttpMethod.POST.name()));
List authenticationProviders = createDefaultAuthenticationProviders(builder);
if (!this.authenticationProviders.isEmpty()) {
@@ -214,18 +215,18 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA
@Override
public void configure(HttpSecurity builder) {
AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);
-
AuthorizationServerSettings authorizationServerSettings =
OAuth2ConfigurerUtils.getAuthorizationServerSettings(builder);
OAuth2DeviceVerificationEndpointFilter deviceVerificationEndpointFilter =
new OAuth2DeviceVerificationEndpointFilter(
- authenticationManager, authorizationServerSettings.getDeviceVerificationEndpoint());
+ authenticationManager,
+ authorizationServerSettings.getDeviceVerificationEndpoint());
List authenticationConverters = createDefaultAuthenticationConverters();
- if (!this.authenticationConverters.isEmpty()) {
- authenticationConverters.addAll(0, this.authenticationConverters);
+ if (!this.deviceVerificationRequestConverters.isEmpty()) {
+ authenticationConverters.addAll(0, this.deviceVerificationRequestConverters);
}
- this.authenticationConvertersConsumer.accept(authenticationConverters);
+ this.deviceVerificationRequestConvertersConsumer.accept(authenticationConverters);
deviceVerificationEndpointFilter.setAuthenticationConverter(
new DelegatingAuthenticationConverter(authenticationConverters));
if (this.deviceVerificationResponseHandler != null) {
@@ -237,7 +238,7 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA
if (StringUtils.hasText(this.consentPage)) {
deviceVerificationEndpointFilter.setConsentPage(this.consentPage);
}
- builder.addFilterAfter(postProcess(deviceVerificationEndpointFilter), AuthorizationFilter.class);
+ builder.addFilterBefore(postProcess(deviceVerificationEndpointFilter), AbstractPreAuthenticatedProcessingFilter.class);
}
@Override
@@ -247,6 +248,7 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA
private static List createDefaultAuthenticationConverters() {
List authenticationConverters = new ArrayList<>();
+
authenticationConverters.add(new OAuth2DeviceVerificationAuthenticationConverter());
authenticationConverters.add(new OAuth2DeviceAuthorizationConsentAuthenticationConverter());
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2AuthorizationServerMetadataHttpMessageConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2AuthorizationServerMetadataHttpMessageConverter.java
index 50ae02d7..af156ec0 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2AuthorizationServerMetadataHttpMessageConverter.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2AuthorizationServerMetadataHttpMessageConverter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020-2022 the original author or authors.
+ * Copyright 2020-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -136,6 +136,7 @@ public class OAuth2AuthorizationServerMetadataHttpMessageConverter
Map> claimConverters = new HashMap<>();
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.ISSUER, urlConverter);
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT, urlConverter);
+ claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT, urlConverter);
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT, urlConverter);
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED, collectionStringConverter);
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI, urlConverter);
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilter.java
index 608aafd5..35752f24 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilter.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilter.java
@@ -94,6 +94,7 @@ public final class OidcProviderConfigurationEndpointFilter extends OncePerReques
OidcProviderConfiguration.Builder providerConfiguration = OidcProviderConfiguration.builder()
.issuer(issuer)
.authorizationEndpoint(asUrl(issuer, authorizationServerSettings.getAuthorizationEndpoint()))
+ .deviceAuthorizationEndpoint(asUrl(issuer, authorizationServerSettings.getDeviceAuthorizationEndpoint()))
.tokenEndpoint(asUrl(issuer, authorizationServerSettings.getTokenEndpoint()))
.tokenEndpointAuthenticationMethods(clientAuthenticationMethods())
.jwkSetUrl(asUrl(issuer, authorizationServerSettings.getJwkSetEndpoint()))
@@ -103,6 +104,7 @@ public final class OidcProviderConfigurationEndpointFilter extends OncePerReques
.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())
.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
.grantType(AuthorizationGrantType.REFRESH_TOKEN.getValue())
+ .grantType(AuthorizationGrantType.DEVICE_CODE.getValue())
.tokenRevocationEndpoint(asUrl(issuer, authorizationServerSettings.getTokenRevocationEndpoint()))
.tokenRevocationEndpointAuthenticationMethods(clientAuthenticationMethods())
.tokenIntrospectionEndpoint(asUrl(issuer, authorizationServerSettings.getTokenIntrospectionEndpoint()))
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/AuthorizationServerSettings.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/AuthorizationServerSettings.java
index 7dc50231..57af7f7e 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/AuthorizationServerSettings.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/AuthorizationServerSettings.java
@@ -55,16 +55,18 @@ public final class AuthorizationServerSettings extends AbstractSettings {
/**
* Returns the OAuth 2.0 Device Authorization endpoint. The default is {@code /oauth2/device_authorization}.
*
- * @return the Authorization endpoint
+ * @return the Device Authorization endpoint
+ * @since 1.1
*/
public String getDeviceAuthorizationEndpoint() {
return getSetting(ConfigurationSettingNames.AuthorizationServer.DEVICE_AUTHORIZATION_ENDPOINT);
}
/**
- * Returns the OAuth 2.0 Device VERIFICATION endpoint. The default is {@code /oauth2/device_verification}.
+ * Returns the OAuth 2.0 Device Verification endpoint. The default is {@code /oauth2/device_verification}.
*
- * @return the Authorization endpoint
+ * @return the Device Verification endpoint
+ * @since 1.1
*/
public String getDeviceVerificationEndpoint() {
return getSetting(ConfigurationSettingNames.AuthorizationServer.DEVICE_VERIFICATION_ENDPOINT);
@@ -198,6 +200,7 @@ public final class AuthorizationServerSettings extends AbstractSettings {
*
* @param deviceAuthorizationEndpoint the Device Authorization endpoint
* @return the {@link Builder} for further configuration
+ * @since 1.1
*/
public Builder deviceAuthorizationEndpoint(String deviceAuthorizationEndpoint) {
return setting(ConfigurationSettingNames.AuthorizationServer.DEVICE_AUTHORIZATION_ENDPOINT, deviceAuthorizationEndpoint);
@@ -208,6 +211,7 @@ public final class AuthorizationServerSettings extends AbstractSettings {
*
* @param deviceVerificationEndpoint the Device Verification endpoint
* @return the {@link Builder} for further configuration
+ * @since 1.1
*/
public Builder deviceVerificationEndpoint(String deviceVerificationEndpoint) {
return setting(ConfigurationSettingNames.AuthorizationServer.DEVICE_VERIFICATION_ENDPOINT, deviceVerificationEndpoint);
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/TokenSettings.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/TokenSettings.java
index 16c364b3..2cbb024c 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/TokenSettings.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/TokenSettings.java
@@ -67,9 +67,9 @@ public final class TokenSettings extends AbstractSettings {
}
/**
- * Returns the time-to-live for a device code. The default is 30 minutes.
+ * Returns the time-to-live for a device code. The default is 5 minutes.
*
- * @return the time-to-live for an authorization code
+ * @return the time-to-live for a device code
* @since 1.1
*/
public Duration getDeviceCodeTimeToLive() {
@@ -113,7 +113,7 @@ public final class TokenSettings extends AbstractSettings {
.authorizationCodeTimeToLive(Duration.ofMinutes(5))
.accessTokenTimeToLive(Duration.ofMinutes(5))
.accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED)
- .deviceCodeTimeToLive(Duration.ofMinutes(30))
+ .deviceCodeTimeToLive(Duration.ofMinutes(5))
.reuseRefreshTokens(true)
.refreshTokenTimeToLive(Duration.ofMinutes(60))
.idTokenSignatureAlgorithm(SignatureAlgorithm.RS256);
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java
index c0e31f39..c561260c 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020-2022 the original author or authors.
+ * Copyright 2020-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -92,6 +92,7 @@ public final class OAuth2AuthorizationServerMetadataEndpointFilter extends OnceP
OAuth2AuthorizationServerMetadata.Builder authorizationServerMetadata = OAuth2AuthorizationServerMetadata.builder()
.issuer(issuer)
.authorizationEndpoint(asUrl(issuer, authorizationServerSettings.getAuthorizationEndpoint()))
+ .deviceAuthorizationEndpoint(asUrl(issuer, authorizationServerSettings.getDeviceAuthorizationEndpoint()))
.tokenEndpoint(asUrl(issuer, authorizationServerSettings.getTokenEndpoint()))
.tokenEndpointAuthenticationMethods(clientAuthenticationMethods())
.jwkSetUrl(asUrl(issuer, authorizationServerSettings.getJwkSetEndpoint()))
@@ -99,6 +100,7 @@ public final class OAuth2AuthorizationServerMetadataEndpointFilter extends OnceP
.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())
.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
.grantType(AuthorizationGrantType.REFRESH_TOKEN.getValue())
+ .grantType(AuthorizationGrantType.DEVICE_CODE.getValue())
.tokenRevocationEndpoint(asUrl(issuer, authorizationServerSettings.getTokenRevocationEndpoint()))
.tokenRevocationEndpointAuthenticationMethods(clientAuthenticationMethods())
.tokenIntrospectionEndpoint(asUrl(issuer, authorizationServerSettings.getTokenIntrospectionEndpoint()))
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceAuthorizationEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceAuthorizationEndpointFilter.java
index 480c7d40..f5473f92 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceAuthorizationEndpointFilter.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceAuthorizationEndpointFilter.java
@@ -27,6 +27,7 @@ import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpResponse;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
@@ -40,7 +41,6 @@ import org.springframework.security.oauth2.core.endpoint.OAuth2DeviceAuthorizati
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.http.converter.OAuth2DeviceAuthorizationResponseHttpMessageConverter;
import org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;
-import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationException;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationRequestAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationRequestAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;
@@ -56,7 +56,7 @@ import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.UriComponentsBuilder;
/**
- * A {@code Filter} for the OAuth 2.0 Device Authorization Grant,
+ * A {@code Filter} for the OAuth 2.0 Device Authorization endpoint,
* which handles the processing of the OAuth 2.0 Device Authorization Request.
*
* @author Steve Riesenberg
@@ -72,20 +72,18 @@ public final class OAuth2DeviceAuthorizationEndpointFilter extends OncePerReques
private static final String DEFAULT_DEVICE_AUTHORIZATION_ENDPOINT_URI = "/oauth2/device_authorization";
- private static final String DEFAULT_DEVICE_VERIFICATION_URI = "/oauth2/device_verification";
-
private final AuthenticationManager authenticationManager;
private final RequestMatcher deviceAuthorizationEndpointMatcher;
private final HttpMessageConverter deviceAuthorizationHttpResponseConverter =
new OAuth2DeviceAuthorizationResponseHttpMessageConverter();
private final HttpMessageConverter errorHttpResponseConverter =
new OAuth2ErrorHttpMessageConverter();
- private AuthenticationConverter authenticationConverter;
private AuthenticationDetailsSource authenticationDetailsSource =
new WebAuthenticationDetailsSource();
+ private AuthenticationConverter authenticationConverter;
private AuthenticationSuccessHandler authenticationSuccessHandler = this::sendDeviceAuthorizationResponse;
private AuthenticationFailureHandler authenticationFailureHandler = this::sendErrorResponse;
- private String verificationUri = DEFAULT_DEVICE_VERIFICATION_URI;
+ private String verificationUri = OAuth2DeviceVerificationEndpointFilter.DEFAULT_DEVICE_VERIFICATION_ENDPOINT_URI;
/**
* Constructs an {@code OAuth2DeviceAuthorizationEndpointFilter} using the provided parameters.
@@ -121,17 +119,17 @@ public final class OAuth2DeviceAuthorizationEndpointFilter extends OncePerReques
}
try {
- OAuth2DeviceAuthorizationRequestAuthenticationToken deviceAuthorizationRequestAuthenticationToken =
- (OAuth2DeviceAuthorizationRequestAuthenticationToken) this.authenticationConverter.convert(request);
- deviceAuthorizationRequestAuthenticationToken.setDetails(
- this.authenticationDetailsSource.buildDetails(request));
+ Authentication deviceAuthorizationRequestAuthentication = this.authenticationConverter.convert(request);
+ if (deviceAuthorizationRequestAuthentication instanceof AbstractAuthenticationToken) {
+ ((AbstractAuthenticationToken) deviceAuthorizationRequestAuthentication)
+ .setDetails(this.authenticationDetailsSource.buildDetails(request));
+ }
- OAuth2DeviceAuthorizationRequestAuthenticationToken deviceAuthorizationRequestAuthenticationTokenResult =
- (OAuth2DeviceAuthorizationRequestAuthenticationToken) this.authenticationManager.authenticate(
- deviceAuthorizationRequestAuthenticationToken);
+ Authentication deviceAuthorizationRequestAuthenticationResult =
+ this.authenticationManager.authenticate(deviceAuthorizationRequestAuthentication);
this.authenticationSuccessHandler.onAuthenticationSuccess(request, response,
- deviceAuthorizationRequestAuthenticationTokenResult);
+ deviceAuthorizationRequestAuthenticationResult);
} catch (OAuth2AuthenticationException ex) {
SecurityContextHolder.clearContext();
if (this.logger.isTraceEnabled()) {
@@ -141,17 +139,6 @@ public final class OAuth2DeviceAuthorizationEndpointFilter extends OncePerReques
}
}
- /**
- * Sets the {@link AuthenticationConverter} used when attempting to extract a Device Authorization Request from {@link HttpServletRequest}
- * to an instance of {@link OAuth2DeviceAuthorizationRequestAuthenticationToken} used for authenticating the request.
- *
- * @param authenticationConverter the {@link AuthenticationConverter} used when attempting to extract a DeviceAuthorization Request from {@link HttpServletRequest}
- */
- public void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {
- Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
- this.authenticationConverter = authenticationConverter;
- }
-
/**
* Sets the {@link AuthenticationDetailsSource} used for building an authentication details instance from {@link HttpServletRequest}.
*
@@ -162,9 +149,20 @@ public final class OAuth2DeviceAuthorizationEndpointFilter extends OncePerReques
this.authenticationDetailsSource = authenticationDetailsSource;
}
+ /**
+ * Sets the {@link AuthenticationConverter} used when attempting to extract a Device Authorization Request from {@link HttpServletRequest}
+ * to an instance of {@link OAuth2DeviceAuthorizationRequestAuthenticationToken} used for authenticating the request.
+ *
+ * @param authenticationConverter the {@link AuthenticationConverter} used when attempting to extract a Device Authorization Request from {@link HttpServletRequest}
+ */
+ public void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {
+ Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
+ this.authenticationConverter = authenticationConverter;
+ }
+
/**
* Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2DeviceAuthorizationRequestAuthenticationToken}
- * and returning the Device Authorization Response.
+ * and returning the {@link OAuth2DeviceAuthorizationResponse Device Authorization Response}.
*
* @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2DeviceAuthorizationRequestAuthenticationToken}
*/
@@ -174,10 +172,10 @@ public final class OAuth2DeviceAuthorizationEndpointFilter extends OncePerReques
}
/**
- * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2DeviceAuthorizationRequestAuthenticationToken}
+ * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
* and returning the {@link OAuth2Error Error Response}.
*
- * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationException}
+ * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
*/
public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
Assert.notNull(authenticationFailureHandler, "authenticationFailureHandler cannot be null");
@@ -198,11 +196,11 @@ public final class OAuth2DeviceAuthorizationEndpointFilter extends OncePerReques
private void sendDeviceAuthorizationResponse(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException {
- OAuth2DeviceAuthorizationRequestAuthenticationToken deviceAuthorizationRequestAuthenticationToken =
+ OAuth2DeviceAuthorizationRequestAuthenticationToken deviceAuthorizationRequestAuthentication =
(OAuth2DeviceAuthorizationRequestAuthenticationToken) authentication;
- OAuth2DeviceCode deviceCode = deviceAuthorizationRequestAuthenticationToken.getDeviceCode();
- OAuth2UserCode userCode = deviceAuthorizationRequestAuthenticationToken.getUserCode();
+ OAuth2DeviceCode deviceCode = deviceAuthorizationRequestAuthentication.getDeviceCode();
+ OAuth2UserCode userCode = deviceAuthorizationRequestAuthentication.getUserCode();
// Generate the fully-qualified verification URI
String issuerUri = AuthorizationServerContextHolder.getContext().getIssuer();
@@ -237,5 +235,3 @@ public final class OAuth2DeviceAuthorizationEndpointFilter extends OncePerReques
}
}
-
-
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceVerificationEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceVerificationEndpointFilter.java
index 0b13c264..5e0e5dc1 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceVerificationEndpointFilter.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceVerificationEndpointFilter.java
@@ -63,9 +63,9 @@ import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.UriComponentsBuilder;
/**
- * A {@code Filter} for the OAuth 2.0 Device Authorization Grant, which handles
- * the processing of the Verification {@code URI} (submission of the user code)
- * and OAuth 2.0 Authorization Consent.
+ * A {@code Filter} for the OAuth 2.0 Device Authorization Grant,
+ * which handles the processing of the Device Verification Request (submission of the user code)
+ * and the Device Authorization Consent.
*
* @author Steve Riesenberg
* @since 1.1
@@ -79,7 +79,7 @@ import org.springframework.web.util.UriComponentsBuilder;
*/
public final class OAuth2DeviceVerificationEndpointFilter extends OncePerRequestFilter {
- private static final String DEFAULT_DEVICE_VERIFICATION_URI = "/oauth2/device_verification";
+ static final String DEFAULT_DEVICE_VERIFICATION_ENDPOINT_URI = "/oauth2/device_verification";
private final AuthenticationManager authenticationManager;
private final RequestMatcher deviceVerificationEndpointMatcher;
@@ -93,16 +93,16 @@ public final class OAuth2DeviceVerificationEndpointFilter extends OncePerRequest
private String consentPage;
/**
- * Construct an {@code OAuth2DeviceVerificationEndpointFilter} using the provided parameters.
+ * Constructs an {@code OAuth2DeviceVerificationEndpointFilter} using the provided parameters.
*
* @param authenticationManager the authentication manager
*/
public OAuth2DeviceVerificationEndpointFilter(AuthenticationManager authenticationManager) {
- this(authenticationManager, DEFAULT_DEVICE_VERIFICATION_URI);
+ this(authenticationManager, DEFAULT_DEVICE_VERIFICATION_ENDPOINT_URI);
}
/**
- * Construct an {@code OAuth2DeviceVerificationEndpointFilter} using the provided parameters.
+ * Constructs an {@code OAuth2DeviceVerificationEndpointFilter} using the provided parameters.
*
* @param authenticationManager the authentication manager
* @param deviceVerificationEndpointUri the endpoint {@code URI} for device verification requests
@@ -184,11 +184,11 @@ public final class OAuth2DeviceVerificationEndpointFilter extends OncePerRequest
}
/**
- * Sets the {@link AuthenticationConverter} used when attempting to extract an Authorization Request (or Consent) from {@link HttpServletRequest}
+ * Sets the {@link AuthenticationConverter} used when attempting to extract a Device Verification Request (or Device Authorization Consent) from {@link HttpServletRequest}
* to an instance of {@link OAuth2DeviceVerificationAuthenticationToken} or {@link OAuth2DeviceAuthorizationConsentAuthenticationToken}
* used for authenticating the request.
*
- * @param authenticationConverter the {@link AuthenticationConverter} used when attempting to extract an Authorization Request (or Consent) from {@link HttpServletRequest}
+ * @param authenticationConverter the {@link AuthenticationConverter} used when attempting to extract a Device Verification Request (or Device Authorization Consent) from {@link HttpServletRequest}
*/
public void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
@@ -207,10 +207,10 @@ public final class OAuth2DeviceVerificationEndpointFilter extends OncePerRequest
}
/**
- * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2DeviceVerificationAuthenticationToken}
+ * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
* and returning the {@link OAuth2Error Error Response}.
*
- * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2DeviceVerificationAuthenticationToken}
+ * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
*/
public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
Assert.notNull(authenticationFailureHandler, "authenticationFailureHandler cannot be null");
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilter.java
index 91d67a90..e44106dd 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilter.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilter.java
@@ -49,6 +49,7 @@ import org.springframework.security.oauth2.server.authorization.authentication.O
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationGrantAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationProvider;
+import org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceCodeAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2RefreshTokenAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.web.authentication.DelegatingAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeAuthenticationConverter;
@@ -92,6 +93,7 @@ import org.springframework.web.filter.OncePerRequestFilter;
* @see OAuth2AuthorizationCodeAuthenticationProvider
* @see OAuth2RefreshTokenAuthenticationProvider
* @see OAuth2ClientCredentialsAuthenticationProvider
+ * @see OAuth2DeviceCodeAuthenticationProvider
* @see Section 3.2 Token Endpoint
*/
public final class OAuth2TokenEndpointFilter extends OncePerRequestFilter {
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceAuthorizationConsentAuthenticationConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceAuthorizationConsentAuthenticationConverter.java
index 7d6f5bfc..d6652376 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceAuthorizationConsentAuthenticationConverter.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceAuthorizationConsentAuthenticationConverter.java
@@ -35,7 +35,7 @@ import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
/**
- * Attempts to extract an Authorization Consent from {@link HttpServletRequest}
+ * Attempts to extract a Device Authorization Consent from {@link HttpServletRequest}
* for the OAuth 2.0 Device Authorization Grant and then converts it to an
* {@link OAuth2DeviceAuthorizationConsentAuthenticationToken} used for
* authenticating the request.
@@ -48,14 +48,14 @@ import org.springframework.util.StringUtils;
*/
public final class OAuth2DeviceAuthorizationConsentAuthenticationConverter implements AuthenticationConverter {
- private static final String DEFAULT_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1";
- private static final String DEVICE_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc8628#section-3.3";
+ private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";
private static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken(
"anonymous", "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
@Override
public Authentication convert(HttpServletRequest request) {
- if (!"POST".equals(request.getMethod())) {
+ if (!"POST".equals(request.getMethod()) ||
+ request.getParameter(OAuth2ParameterNames.STATE) == null) {
return null;
}
@@ -63,22 +63,14 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationConverter imple
String authorizationUri = request.getRequestURL().toString();
- // user_code (REQUIRED)
- String userCode = parameters.getFirst(OAuth2ParameterNames.USER_CODE);
- if (!StringUtils.hasText(userCode) || parameters.get(OAuth2ParameterNames.USER_CODE).size() != 1) {
- OAuth2EndpointUtils.throwError(
- OAuth2ErrorCodes.INVALID_REQUEST,
- OAuth2ParameterNames.USER_CODE,
- DEVICE_ERROR_URI);
- }
-
// client_id (REQUIRED)
String clientId = parameters.getFirst(OAuth2ParameterNames.CLIENT_ID);
- if (!StringUtils.hasText(clientId) || parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) {
+ if (!StringUtils.hasText(clientId) ||
+ parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) {
OAuth2EndpointUtils.throwError(
OAuth2ErrorCodes.INVALID_REQUEST,
OAuth2ParameterNames.CLIENT_ID,
- DEFAULT_ERROR_URI);
+ ERROR_URI);
}
Authentication principal = SecurityContextHolder.getContext().getAuthentication();
@@ -86,13 +78,24 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationConverter imple
principal = ANONYMOUS_AUTHENTICATION;
}
+ // user_code (REQUIRED)
+ String userCode = parameters.getFirst(OAuth2ParameterNames.USER_CODE);
+ if (!StringUtils.hasText(userCode) ||
+ parameters.get(OAuth2ParameterNames.USER_CODE).size() != 1) {
+ OAuth2EndpointUtils.throwError(
+ OAuth2ErrorCodes.INVALID_REQUEST,
+ OAuth2ParameterNames.USER_CODE,
+ ERROR_URI);
+ }
+
// state (REQUIRED)
String state = parameters.getFirst(OAuth2ParameterNames.STATE);
- if (!StringUtils.hasText(state) || parameters.get(OAuth2ParameterNames.STATE).size() != 1) {
+ if (!StringUtils.hasText(state) ||
+ parameters.get(OAuth2ParameterNames.STATE).size() != 1) {
OAuth2EndpointUtils.throwError(
OAuth2ErrorCodes.INVALID_REQUEST,
OAuth2ParameterNames.STATE,
- DEFAULT_ERROR_URI);
+ ERROR_URI);
}
// scope (OPTIONAL)
@@ -104,9 +107,9 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationConverter imple
Map additionalParameters = new HashMap<>();
parameters.forEach((key, value) -> {
if (!key.equals(OAuth2ParameterNames.CLIENT_ID) &&
+ !key.equals(OAuth2ParameterNames.USER_CODE) &&
!key.equals(OAuth2ParameterNames.STATE) &&
- !key.equals(OAuth2ParameterNames.SCOPE) &&
- !key.equals(OAuth2ParameterNames.USER_CODE)) {
+ !key.equals(OAuth2ParameterNames.SCOPE)) {
additionalParameters.put(key, value.get(0));
}
});
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceCodeAuthenticationConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceCodeAuthenticationConverter.java
index d0fcaa27..8738b21e 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceCodeAuthenticationConverter.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceCodeAuthenticationConverter.java
@@ -20,6 +20,7 @@ import java.util.Map;
import jakarta.servlet.http.HttpServletRequest;
+import org.springframework.lang.Nullable;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
@@ -32,7 +33,7 @@ import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
/**
- * Attempts to extract an Access Token Request from {@link HttpServletRequest} for the
+ * Attempts to extract a Device Access Token Request from {@link HttpServletRequest} for the
* OAuth 2.0 Device Authorization Grant and then converts it to an
* {@link OAuth2DeviceCodeAuthenticationToken} used for authenticating the
* authorization grant.
@@ -45,6 +46,7 @@ import org.springframework.util.StringUtils;
*/
public final class OAuth2DeviceCodeAuthenticationConverter implements AuthenticationConverter {
+ @Nullable
@Override
public Authentication convert(HttpServletRequest request) {
// grant_type (REQUIRED)
@@ -59,7 +61,8 @@ public final class OAuth2DeviceCodeAuthenticationConverter implements Authentica
// device_code (REQUIRED)
String deviceCode = parameters.getFirst(OAuth2ParameterNames.DEVICE_CODE);
- if (!StringUtils.hasText(deviceCode) || parameters.get(OAuth2ParameterNames.DEVICE_CODE).size() != 1) {
+ if (!StringUtils.hasText(deviceCode) ||
+ parameters.get(OAuth2ParameterNames.DEVICE_CODE).size() != 1) {
OAuth2EndpointUtils.throwError(
OAuth2ErrorCodes.INVALID_REQUEST,
OAuth2ParameterNames.DEVICE_CODE,
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceVerificationAuthenticationConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceVerificationAuthenticationConverter.java
index 4d3bf3e2..b5248352 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceVerificationAuthenticationConverter.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceVerificationAuthenticationConverter.java
@@ -46,7 +46,7 @@ import org.springframework.util.StringUtils;
*/
public final class OAuth2DeviceVerificationAuthenticationConverter implements AuthenticationConverter {
- private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc8628#section-3.3";
+ private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";
private static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken(
"anonymous", "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
@@ -64,7 +64,8 @@ public final class OAuth2DeviceVerificationAuthenticationConverter implements Au
// user_code (REQUIRED)
String userCode = parameters.getFirst(OAuth2ParameterNames.USER_CODE);
- if (!StringUtils.hasText(userCode) || parameters.get(OAuth2ParameterNames.USER_CODE).size() != 1) {
+ if (!StringUtils.hasText(userCode) ||
+ parameters.get(OAuth2ParameterNames.USER_CODE).size() != 1) {
OAuth2EndpointUtils.throwError(
OAuth2ErrorCodes.INVALID_REQUEST,
OAuth2ParameterNames.USER_CODE,
diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ClientCredentialsGrantTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ClientCredentialsGrantTests.java
index 4fb563f2..9b097db8 100644
--- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ClientCredentialsGrantTests.java
+++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ClientCredentialsGrantTests.java
@@ -24,12 +24,13 @@ import java.util.Base64;
import java.util.List;
import java.util.function.Consumer;
-import com.nimbusds.jose.jwk.JWKSet;
-import com.nimbusds.jose.jwk.source.JWKSource;
-import com.nimbusds.jose.proc.SecurityContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
+
+import com.nimbusds.jose.jwk.JWKSet;
+import com.nimbusds.jose.jwk.source.JWKSource;
+import com.nimbusds.jose.proc.SecurityContext;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilterTests.java
index 3d175e67..6e457252 100644
--- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilterTests.java
+++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilterTests.java
@@ -126,7 +126,7 @@ public class OidcProviderConfigurationEndpointFilterTests {
assertThat(providerConfigurationResponse).contains("\"jwks_uri\":\"https://example.com/issuer1/oauth2/v1/jwks\"");
assertThat(providerConfigurationResponse).contains("\"scopes_supported\":[\"openid\"]");
assertThat(providerConfigurationResponse).contains("\"response_types_supported\":[\"code\"]");
- assertThat(providerConfigurationResponse).contains("\"grant_types_supported\":[\"authorization_code\",\"client_credentials\",\"refresh_token\"]");
+ assertThat(providerConfigurationResponse).contains("\"grant_types_supported\":[\"authorization_code\",\"client_credentials\",\"refresh_token\",\"urn:ietf:params:oauth:grant-type:device_code\"]");
assertThat(providerConfigurationResponse).contains("\"revocation_endpoint\":\"https://example.com/issuer1/oauth2/v1/revoke\"");
assertThat(providerConfigurationResponse).contains("\"revocation_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\"]");
assertThat(providerConfigurationResponse).contains("\"introspection_endpoint\":\"https://example.com/issuer1/oauth2/v1/introspect\"");
diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/settings/TokenSettingsTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/settings/TokenSettingsTests.java
index 230f3ca3..d1552dfe 100644
--- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/settings/TokenSettingsTests.java
+++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/settings/TokenSettingsTests.java
@@ -36,9 +36,9 @@ public class TokenSettingsTests {
TokenSettings tokenSettings = TokenSettings.builder().build();
assertThat(tokenSettings.getSettings()).hasSize(7);
assertThat(tokenSettings.getAuthorizationCodeTimeToLive()).isEqualTo(Duration.ofMinutes(5));
- assertThat(tokenSettings.getDeviceCodeTimeToLive()).isEqualTo(Duration.ofMinutes(30));
assertThat(tokenSettings.getAccessTokenTimeToLive()).isEqualTo(Duration.ofMinutes(5));
assertThat(tokenSettings.getAccessTokenFormat()).isEqualTo(OAuth2TokenFormat.SELF_CONTAINED);
+ assertThat(tokenSettings.getDeviceCodeTimeToLive()).isEqualTo(Duration.ofMinutes(5));
assertThat(tokenSettings.isReuseRefreshTokens()).isTrue();
assertThat(tokenSettings.getRefreshTokenTimeToLive()).isEqualTo(Duration.ofMinutes(60));
assertThat(tokenSettings.getIdTokenSignatureAlgorithm()).isEqualTo(SignatureAlgorithm.RS256);
diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilterTests.java
index 1d5f6d9e..b68f077d 100644
--- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilterTests.java
+++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilterTests.java
@@ -122,7 +122,7 @@ public class OAuth2AuthorizationServerMetadataEndpointFilterTests {
assertThat(authorizationServerMetadataResponse).contains("\"token_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\"]");
assertThat(authorizationServerMetadataResponse).contains("\"jwks_uri\":\"https://example.com/issuer1/oauth2/v1/jwks\"");
assertThat(authorizationServerMetadataResponse).contains("\"response_types_supported\":[\"code\"]");
- assertThat(authorizationServerMetadataResponse).contains("\"grant_types_supported\":[\"authorization_code\",\"client_credentials\",\"refresh_token\"]");
+ assertThat(authorizationServerMetadataResponse).contains("\"grant_types_supported\":[\"authorization_code\",\"client_credentials\",\"refresh_token\",\"urn:ietf:params:oauth:grant-type:device_code\"]");
assertThat(authorizationServerMetadataResponse).contains("\"revocation_endpoint\":\"https://example.com/issuer1/oauth2/v1/revoke\"");
assertThat(authorizationServerMetadataResponse).contains("\"revocation_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\"]");
assertThat(authorizationServerMetadataResponse).contains("\"introspection_endpoint\":\"https://example.com/issuer1/oauth2/v1/introspect\"");
diff --git a/samples/device-client/src/main/java/sample/config/SecurityConfig.java b/samples/device-client/src/main/java/sample/config/SecurityConfig.java
index 4f823847..525189e8 100644
--- a/samples/device-client/src/main/java/sample/config/SecurityConfig.java
+++ b/samples/device-client/src/main/java/sample/config/SecurityConfig.java
@@ -28,7 +28,7 @@ import org.springframework.security.web.authentication.LoginUrlAuthenticationEnt
* @author Steve Riesenberg
* @since 1.1
*/
-@Configuration
+@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
public class SecurityConfig {
diff --git a/samples/device-grant-authorizationserver/samples-device-grant-authorizationserver.gradle b/samples/device-grant-authorizationserver/samples-device-grant-authorizationserver.gradle
index 9b13324e..cc860e04 100644
--- a/samples/device-grant-authorizationserver/samples-device-grant-authorizationserver.gradle
+++ b/samples/device-grant-authorizationserver/samples-device-grant-authorizationserver.gradle
@@ -18,9 +18,7 @@ dependencies {
implementation "org.springframework.boot:spring-boot-starter-security"
implementation "org.springframework.boot:spring-boot-starter-jdbc"
implementation project(":spring-security-oauth2-authorization-server")
- implementation "org.springframework.boot:spring-boot-starter-oauth2-client"
implementation "org.springframework.boot:spring-boot-starter-thymeleaf"
- implementation "org.springframework:spring-webflux"
implementation "org.webjars:webjars-locator-core"
implementation "org.webjars:bootstrap:3.4.1"
implementation "org.webjars:jquery:3.4.1"
diff --git a/samples/device-grant-authorizationserver/src/main/java/sample/config/SecurityConfig.java b/samples/device-grant-authorizationserver/src/main/java/sample/config/SecurityConfig.java
index ab66f4b8..2ea1b33d 100644
--- a/samples/device-grant-authorizationserver/src/main/java/sample/config/SecurityConfig.java
+++ b/samples/device-grant-authorizationserver/src/main/java/sample/config/SecurityConfig.java
@@ -56,7 +56,7 @@ import org.springframework.security.web.authentication.LoginUrlAuthenticationEnt
* @author Steve Riesenberg
* @since 1.1
*/
-@Configuration
+@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
public class SecurityConfig {
@@ -100,7 +100,7 @@ public class SecurityConfig {
public UserDetailsService userDetailsService() {
// @formatter:off
UserDetails userDetails = User.withDefaultPasswordEncoder()
- .username("user")
+ .username("user1")
.password("password")
.roles("USER")
.build();
@@ -144,7 +144,7 @@ public class SecurityConfig {
return new ImmutableJWKSet<>(jwkSet);
}
- private static KeyPair generateRsaKey() {
+ private static KeyPair generateRsaKey() {
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
@@ -167,4 +167,4 @@ public class SecurityConfig {
return AuthorizationServerSettings.builder().build();
}
-}
\ No newline at end of file
+}
diff --git a/samples/device-grant-authorizationserver/src/main/java/sample/web/DeviceController.java b/samples/device-grant-authorizationserver/src/main/java/sample/web/DeviceController.java
index 3b40e2b2..418f241c 100644
--- a/samples/device-grant-authorizationserver/src/main/java/sample/web/DeviceController.java
+++ b/samples/device-grant-authorizationserver/src/main/java/sample/web/DeviceController.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2023 the original author or authors.
+ * Copyright 2020-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/samples/device-grant-authorizationserver/src/main/resources/templates/activate.html b/samples/device-grant-authorizationserver/src/main/resources/templates/activate.html
index fa5d76cb..4607e0de 100644
--- a/samples/device-grant-authorizationserver/src/main/resources/templates/activate.html
+++ b/samples/device-grant-authorizationserver/src/main/resources/templates/activate.html
@@ -12,7 +12,7 @@