Polish demo sample

Issue gh-1189
This commit is contained in:
Joe Grandja
2023-05-05 15:41:07 -04:00
parent 1112204475
commit f99d868c39
11 changed files with 13 additions and 13 deletions

View File

@@ -1,119 +0,0 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.web.authentication;
import java.time.Clock;
import java.time.Duration;
import java.util.Collections;
import java.util.Map;
import java.util.function.Function;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.security.oauth2.client.ClientAuthorizationException;
import org.springframework.security.oauth2.client.OAuth2AuthorizationContext;
import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
import org.springframework.security.oauth2.core.OAuth2Token;
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.util.Assert;
/**
* @author Steve Riesenberg
* @since 1.1
*/
public final class DeviceCodeOAuth2AuthorizedClientProvider implements OAuth2AuthorizedClientProvider {
private OAuth2AccessTokenResponseClient<OAuth2DeviceGrantRequest> accessTokenResponseClient =
new OAuth2DeviceAccessTokenResponseClient();
private Duration clockSkew = Duration.ofSeconds(60);
private Clock clock = Clock.systemUTC();
public void setAccessTokenResponseClient(OAuth2AccessTokenResponseClient<OAuth2DeviceGrantRequest> accessTokenResponseClient) {
this.accessTokenResponseClient = accessTokenResponseClient;
}
public void setClockSkew(Duration clockSkew) {
this.clockSkew = clockSkew;
}
public void setClock(Clock clock) {
this.clock = clock;
}
@Override
public OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context) {
Assert.notNull(context, "context cannot be null");
ClientRegistration clientRegistration = context.getClientRegistration();
if (!AuthorizationGrantType.DEVICE_CODE.equals(clientRegistration.getAuthorizationGrantType())) {
return null;
}
OAuth2AuthorizedClient authorizedClient = context.getAuthorizedClient();
if (authorizedClient != null && !hasTokenExpired(authorizedClient.getAccessToken())) {
// If client is already authorized but access token is NOT expired than no
// need for re-authorization
return null;
}
if (authorizedClient != null && authorizedClient.getRefreshToken() != null) {
// If client is already authorized but access token is expired and a
// refresh token is available, delegate to refresh_token.
return null;
}
// *****************************************************************
// Get device_code set via DefaultOAuth2AuthorizedClientManager#setContextAttributesMapper()
// *****************************************************************
String deviceCode = context.getAttribute(OAuth2ParameterNames.DEVICE_CODE);
// Attempt to authorize the client, which will repeatedly fail until the user grants authorization
OAuth2DeviceGrantRequest deviceGrantRequest = new OAuth2DeviceGrantRequest(clientRegistration, deviceCode);
OAuth2AccessTokenResponse tokenResponse = getTokenResponse(clientRegistration, deviceGrantRequest);
return new OAuth2AuthorizedClient(clientRegistration, context.getPrincipal().getName(),
tokenResponse.getAccessToken(), tokenResponse.getRefreshToken());
}
private OAuth2AccessTokenResponse getTokenResponse(ClientRegistration clientRegistration,
OAuth2DeviceGrantRequest deviceGrantRequest) {
try {
return this.accessTokenResponseClient.getTokenResponse(deviceGrantRequest);
} catch (OAuth2AuthorizationException ex) {
throw new ClientAuthorizationException(ex.getError(), clientRegistration.getRegistrationId(), ex);
}
}
private boolean hasTokenExpired(OAuth2Token token) {
return this.clock.instant().isAfter(token.getExpiresAt().minus(this.clockSkew));
}
public static Function<OAuth2AuthorizeRequest, Map<String, Object>> deviceCodeContextAttributesMapper() {
return (authorizeRequest) -> {
HttpServletRequest request = authorizeRequest.getAttribute(HttpServletRequest.class.getName());
Assert.notNull(request, "request cannot be null");
// Obtain device code from request
String deviceCode = request.getParameter(OAuth2ParameterNames.DEVICE_CODE);
return (deviceCode != null) ? Collections.singletonMap(OAuth2ParameterNames.DEVICE_CODE, deviceCode) :
Collections.emptyMap();
};
}
}

View File

@@ -1,99 +0,0 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.web.authentication;
import java.util.Arrays;
import org.springframework.http.HttpHeaders;
import org.springframework.http.RequestEntity;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;
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.OAuth2Error;
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestOperations;
import org.springframework.web.client.RestTemplate;
/**
* @author Steve Riesenberg
* @since 1.1
*/
public final class OAuth2DeviceAccessTokenResponseClient implements OAuth2AccessTokenResponseClient<OAuth2DeviceGrantRequest> {
private RestOperations restOperations;
public OAuth2DeviceAccessTokenResponseClient() {
RestTemplate restTemplate = new RestTemplate(Arrays.asList(new FormHttpMessageConverter(),
new OAuth2AccessTokenResponseHttpMessageConverter()));
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
this.restOperations = restTemplate;
}
public void setRestOperations(RestOperations restOperations) {
this.restOperations = restOperations;
}
@Override
public OAuth2AccessTokenResponse getTokenResponse(OAuth2DeviceGrantRequest deviceGrantRequest) {
ClientRegistration clientRegistration = deviceGrantRequest.getClientRegistration();
HttpHeaders headers = new HttpHeaders();
/*
* 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<>();
requestParameters.add(OAuth2ParameterNames.GRANT_TYPE, deviceGrantRequest.getGrantType().getValue());
requestParameters.add(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId());
requestParameters.add(OAuth2ParameterNames.DEVICE_CODE, deviceGrantRequest.getDeviceCode());
// @formatter:off
RequestEntity<MultiValueMap<String, Object>> requestEntity =
RequestEntity.post(deviceGrantRequest.getClientRegistration().getProviderDetails().getTokenUri())
.headers(headers)
.body(requestParameters);
// @formatter:on
try {
return this.restOperations.exchange(requestEntity, OAuth2AccessTokenResponse.class).getBody();
} catch (RestClientException ex) {
OAuth2Error oauth2Error = new OAuth2Error("invalid_token_response",
"An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: "
+ ex.getMessage(), null);
throw new OAuth2AuthorizationException(oauth2Error, ex);
}
}
}

View File

@@ -1,41 +0,0 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.web.authentication;
import org.springframework.security.oauth2.client.endpoint.AbstractOAuth2AuthorizationGrantRequest;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.util.Assert;
/**
* @author Steve Riesenberg
* @since 1.1
*/
public final class OAuth2DeviceGrantRequest extends AbstractOAuth2AuthorizationGrantRequest {
private final String deviceCode;
public OAuth2DeviceGrantRequest(ClientRegistration clientRegistration, String deviceCode) {
super(AuthorizationGrantType.DEVICE_CODE, clientRegistration);
Assert.hasText(deviceCode, "deviceCode cannot be empty");
this.deviceCode = deviceCode;
}
public String getDeviceCode() {
return this.deviceCode;
}
}