Polish samples

Closes gh-1157
This commit is contained in:
Steve Riesenberg
2023-04-12 11:43:27 -05:00
parent be50f66b4c
commit 7c166a3a72
6 changed files with 95 additions and 25 deletions

View File

@@ -36,6 +36,7 @@ import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient; import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.OAuth2DeviceCode; import org.springframework.security.oauth2.core.OAuth2DeviceCode;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository; import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
@@ -105,8 +106,22 @@ public class DeviceController {
Map<String, Object> responseParameters = Map<String, Object> responseParameters =
this.webClient.post() this.webClient.post()
.uri(clientRegistration.getProviderDetails().getAuthorizationUri()) .uri(clientRegistration.getProviderDetails().getAuthorizationUri())
// .headers(headers -> headers.setBasicAuth(clientRegistration.getClientId(), .headers(headers -> {
// clientRegistration.getClientSecret())) /*
* This sample demonstrates the use of a public client that does not
* store credentials or authenticate with the authorization server.
*
* See DeviceClientAuthenticationProvider in the authorization server
* sample for an example customization that allows public clients.
*
* For a confidential client, change the client-authentication-method to
* client_secret_basic and set the client-secret to send the
* OAuth 2.0 Device Authorization Request with a clientId/clientSecret.
*/
if (!clientRegistration.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.NONE)) {
headers.setBasicAuth(clientRegistration.getClientId(), clientRegistration.getClientSecret());
}
})
.contentType(MediaType.APPLICATION_FORM_URLENCODED) .contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(BodyInserters.fromFormData(requestParameters)) .body(BodyInserters.fromFormData(requestParameters))
.retrieve() .retrieve()
@@ -142,19 +157,21 @@ public class DeviceController {
@RegisteredOAuth2AuthorizedClient("messaging-client-device-grant") @RegisteredOAuth2AuthorizedClient("messaging-client-device-grant")
OAuth2AuthorizedClient authorizedClient) { OAuth2AuthorizedClient authorizedClient) {
// The client will repeatedly poll until authorization is granted. /*
// * The client will repeatedly poll until authorization is granted.
// The OAuth2AuthorizedClientManager uses the device_code parameter *
// to make a token request, which returns authorization_pending until * The OAuth2AuthorizedClientManager uses the device_code parameter
// the user has granted authorization. * to make a token request, which returns authorization_pending until
// * the user has granted authorization.
// If the user has denied authorization, access_denied is returned and *
// polling should stop. * If the user has denied authorization, access_denied is returned and
// * polling should stop.
// If the device code expires, expired_token is returned and polling *
// should stop. * If the device code expires, expired_token is returned and polling
// * should stop.
// This endpoint simply returns 200 OK when client is authorized. *
* This endpoint simply returns 200 OK when the client is authorized.
*/
return ResponseEntity.status(HttpStatus.OK).build(); return ResponseEntity.status(HttpStatus.OK).build();
} }

View File

@@ -23,6 +23,7 @@ import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient; import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler; import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;
import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.OAuth2AuthorizationException; import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
@@ -58,7 +59,20 @@ public final class OAuth2DeviceAccessTokenResponseClient implements OAuth2Access
ClientRegistration clientRegistration = deviceGrantRequest.getClientRegistration(); ClientRegistration clientRegistration = deviceGrantRequest.getClientRegistration();
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
// headers.setBasicAuth(clientRegistration.getClientId(), clientRegistration.getClientSecret()); /*
* This sample demonstrates the use of a public client that does not
* store credentials or authenticate with the authorization server.
*
* See DeviceClientAuthenticationProvider in the authorization server
* sample for an example customization that allows public clients.
*
* For a confidential client, change the client-authentication-method
* to client_secret_basic and set the client-secret to send the
* OAuth 2.0 Token Request with a clientId/clientSecret.
*/
if (!clientRegistration.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.NONE)) {
headers.setBasicAuth(clientRegistration.getClientId(), clientRegistration.getClientSecret());
}
MultiValueMap<String, Object> requestParameters = new LinkedMultiValueMap<>(); MultiValueMap<String, Object> requestParameters = new LinkedMultiValueMap<>();
requestParameters.add(OAuth2ParameterNames.GRANT_TYPE, deviceGrantRequest.getGrantType().getValue()); requestParameters.add(OAuth2ParameterNames.GRANT_TYPE, deviceGrantRequest.getGrantType().getValue());

View File

@@ -17,6 +17,7 @@ package sample.authentication;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import sample.web.authentication.DeviceClientAuthenticationConverter;
import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
@@ -28,8 +29,17 @@ import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; 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.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/**
* @author Joe Grandja
* @author Steve Riesenberg
* @since 1.1
* @see DeviceClientAuthenticationToken
* @see DeviceClientAuthenticationConverter
* @see OAuth2ClientAuthenticationFilter
*/
public final class DeviceClientAuthenticationProvider implements AuthenticationProvider { public final class DeviceClientAuthenticationProvider implements AuthenticationProvider {
private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-3.2.1"; private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-3.2.1";
private final Log logger = LogFactory.getLog(getClass()); private final Log logger = LogFactory.getLog(getClass());

View File

@@ -23,6 +23,11 @@ import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken; import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
/**
* @author Joe Grandja
* @author Steve Riesenberg
* @since 1.1
*/
@Transient @Transient
public class DeviceClientAuthenticationToken extends OAuth2ClientAuthenticationToken { public class DeviceClientAuthenticationToken extends OAuth2ClientAuthenticationToken {

View File

@@ -74,20 +74,40 @@ public class SecurityConfig {
HttpSecurity http, RegisteredClientRepository registeredClientRepository, HttpSecurity http, RegisteredClientRepository registeredClientRepository,
AuthorizationServerSettings authorizationServerSettings) throws Exception { AuthorizationServerSettings authorizationServerSettings) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http); OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
/*
* This sample demonstrates the use of a public client that does not
* store credentials or authenticate with the authorization server.
*
* The following components show how to customize the authorization
* server to allow for device clients to perform requests to the
* OAuth 2.0 Device Authorization Endpoint and Token Endpoint without
* a clientId/clientSecret.
*
* CAUTION: These endpoints will not require any authentication, and can
* be accessed by any client that has a valid clientId.
*
* It is therefore RECOMMENDED to carefully monitor the use of these
* endpoints and employ any additional protections as needed, which is
* outside the scope of this sample.
*/
DeviceClientAuthenticationConverter deviceClientAuthenticationConverter =
new DeviceClientAuthenticationConverter(
authorizationServerSettings.getDeviceAuthorizationEndpoint());
DeviceClientAuthenticationProvider deviceClientAuthenticationProvider =
new DeviceClientAuthenticationProvider(registeredClientRepository);
// @formatter:off
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class) http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.deviceAuthorizationEndpoint((deviceAuthorizationEndpoint) -> deviceAuthorizationEndpoint .deviceAuthorizationEndpoint((deviceAuthorizationEndpoint) -> deviceAuthorizationEndpoint
.verificationUri("/activate") .verificationUri("/activate")
) )
.clientAuthentication((clientAuthentication) -> .clientAuthentication((clientAuthentication) -> clientAuthentication
clientAuthentication .authenticationConverter(deviceClientAuthenticationConverter)
.authenticationConverter( .authenticationProvider(deviceClientAuthenticationProvider)
new DeviceClientAuthenticationConverter(
authorizationServerSettings.getDeviceAuthorizationEndpoint()))
.authenticationProvider(
new DeviceClientAuthenticationProvider(
registeredClientRepository))
) )
.oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0 .oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0
// @formatter:on
// @formatter:off // @formatter:off
http http

View File

@@ -16,7 +16,6 @@
package sample.web.authentication; package sample.web.authentication;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import sample.authentication.DeviceClientAuthenticationToken; import sample.authentication.DeviceClientAuthenticationToken;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
@@ -33,6 +32,11 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/**
* @author Joe Grandja
* @author Steve Riesenberg
* @since 1.1
*/
public final class DeviceClientAuthenticationConverter implements AuthenticationConverter { public final class DeviceClientAuthenticationConverter implements AuthenticationConverter {
private final RequestMatcher deviceAuthorizationRequestMatcher; private final RequestMatcher deviceAuthorizationRequestMatcher;
private final RequestMatcher deviceAccessTokenRequestMatcher; private final RequestMatcher deviceAccessTokenRequestMatcher;