6 changed files with 468 additions and 42 deletions
@ -0,0 +1,104 @@
@@ -0,0 +1,104 @@
|
||||
/* |
||||
* Copyright 2020-2024 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 org.springframework.security.oauth2.server.authorization.authentication; |
||||
|
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; |
||||
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AccessTokenResponseAuthenticationSuccessHandler; |
||||
import org.springframework.util.Assert; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
import java.util.function.Consumer; |
||||
|
||||
/** |
||||
* An {@link OAuth2AuthenticationContext} that holds an {@link OAuth2AccessTokenResponse.Builder} |
||||
* and is used when customizing the building of the {@link OAuth2AccessTokenResponse}. |
||||
* |
||||
* @author Dmitriy Dubson |
||||
* @see OAuth2AuthenticationContext |
||||
* @see OAuth2AccessTokenResponse |
||||
* @see OAuth2AccessTokenResponseAuthenticationSuccessHandler#setAccessTokenResponseCustomizer(Consumer) |
||||
* @since 1.3 |
||||
*/ |
||||
public final class OAuth2AccessTokenAuthenticationContext implements OAuth2AuthenticationContext { |
||||
private final Map<Object, Object> context; |
||||
|
||||
private OAuth2AccessTokenAuthenticationContext(Map<Object, Object> context) { |
||||
this.context = Collections.unmodifiableMap(new HashMap<>(context)); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
@Nullable |
||||
@Override |
||||
public <V> V get(Object key) { |
||||
return hasKey(key) ? (V) this.context.get(key) : null; |
||||
} |
||||
|
||||
@Override |
||||
public boolean hasKey(Object key) { |
||||
Assert.notNull(key, "key cannot be null"); |
||||
return this.context.containsKey(key); |
||||
} |
||||
|
||||
/** |
||||
* Returns the {@link OAuth2AccessTokenResponse.Builder} access token response builder |
||||
* @return the {@link OAuth2AccessTokenResponse.Builder} |
||||
*/ |
||||
public OAuth2AccessTokenResponse.Builder getAccessTokenResponse() { |
||||
return get(OAuth2AccessTokenResponse.Builder.class); |
||||
} |
||||
|
||||
/** |
||||
* Constructs a new {@link Builder} with the provided {@link OAuth2AccessTokenAuthenticationToken}. |
||||
* |
||||
* @param authentication the {@link OAuth2AccessTokenAuthenticationToken} |
||||
* @return the {@link Builder} |
||||
*/ |
||||
public static OAuth2AccessTokenAuthenticationContext.Builder with(OAuth2AccessTokenAuthenticationToken authentication) { |
||||
return new OAuth2AccessTokenAuthenticationContext.Builder(authentication); |
||||
} |
||||
|
||||
/** |
||||
* A builder for {@link OAuth2AccessTokenAuthenticationContext} |
||||
*/ |
||||
public static final class Builder extends AbstractBuilder<OAuth2AccessTokenAuthenticationContext, Builder> { |
||||
private Builder(OAuth2AccessTokenAuthenticationToken authentication) { |
||||
super(authentication); |
||||
} |
||||
|
||||
/** |
||||
* Sets the {@link OAuth2AccessTokenResponse.Builder} access token response builder |
||||
* @param accessTokenResponse the {@link OAuth2AccessTokenResponse.Builder} |
||||
* @return the {@link Builder} for further configuration |
||||
*/ |
||||
public Builder accessTokenResponse(OAuth2AccessTokenResponse.Builder accessTokenResponse) { |
||||
return put(OAuth2AccessTokenResponse.Builder.class, accessTokenResponse); |
||||
} |
||||
|
||||
/** |
||||
* Builds a new {@link OAuth2AccessTokenAuthenticationContext}. |
||||
* |
||||
* @return the {@link OAuth2AccessTokenAuthenticationContext} |
||||
*/ |
||||
public OAuth2AccessTokenAuthenticationContext build() { |
||||
Assert.notNull(get(OAuth2AccessTokenResponse.Builder.class), "accessTokenResponse cannot be null"); |
||||
|
||||
return new OAuth2AccessTokenAuthenticationContext(getContext()); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,115 @@
@@ -0,0 +1,115 @@
|
||||
/* |
||||
* Copyright 2020-2024 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 org.springframework.security.oauth2.server.authorization.web.authentication; |
||||
|
||||
import java.io.IOException; |
||||
import java.time.temporal.ChronoUnit; |
||||
import java.util.Map; |
||||
import java.util.function.Consumer; |
||||
|
||||
import jakarta.servlet.ServletException; |
||||
import jakarta.servlet.http.HttpServletRequest; |
||||
import jakarta.servlet.http.HttpServletResponse; |
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
import org.springframework.http.converter.HttpMessageConverter; |
||||
import org.springframework.http.server.ServletServerHttpResponse; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.security.oauth2.core.*; |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; |
||||
import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter; |
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationContext; |
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken; |
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.CollectionUtils; |
||||
|
||||
/** |
||||
* An implementation of an {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2AccessTokenAuthenticationToken} |
||||
* and returning the {@link OAuth2AccessTokenResponse Access Token Response}. |
||||
* |
||||
* @author Dmitriy Dubson |
||||
* @see AuthenticationSuccessHandler |
||||
* @see OAuth2AccessTokenResponseHttpMessageConverter |
||||
* @since 1.3 |
||||
*/ |
||||
public final class OAuth2AccessTokenResponseAuthenticationSuccessHandler implements AuthenticationSuccessHandler { |
||||
private final Log logger = LogFactory.getLog(getClass()); |
||||
|
||||
private final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenResponseConverter = |
||||
new OAuth2AccessTokenResponseHttpMessageConverter(); |
||||
|
||||
private Consumer<OAuth2AccessTokenAuthenticationContext> accessTokenResponseCustomizer; |
||||
|
||||
@Override |
||||
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { |
||||
if (!(authentication instanceof OAuth2AccessTokenAuthenticationToken accessTokenAuthentication)) { |
||||
if (this.logger.isErrorEnabled()) { |
||||
this.logger.error(Authentication.class.getSimpleName() + " must be of type " + |
||||
OAuth2AccessTokenAuthenticationToken.class.getName() + |
||||
" but was " + authentication.getClass().getName()); |
||||
} |
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR, "Unable to process the access token response.", null); |
||||
throw new OAuth2AuthenticationException(error); |
||||
} |
||||
|
||||
OAuth2AccessToken accessToken = accessTokenAuthentication.getAccessToken(); |
||||
OAuth2RefreshToken refreshToken = accessTokenAuthentication.getRefreshToken(); |
||||
Map<String, Object> additionalParameters = accessTokenAuthentication.getAdditionalParameters(); |
||||
|
||||
OAuth2AccessTokenResponse.Builder builder = |
||||
OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue()) |
||||
.tokenType(accessToken.getTokenType()) |
||||
.scopes(accessToken.getScopes()); |
||||
if (accessToken.getIssuedAt() != null && accessToken.getExpiresAt() != null) { |
||||
builder.expiresIn(ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt())); |
||||
} |
||||
if (refreshToken != null) { |
||||
builder.refreshToken(refreshToken.getTokenValue()); |
||||
} |
||||
if (!CollectionUtils.isEmpty(additionalParameters)) { |
||||
builder.additionalParameters(additionalParameters); |
||||
} |
||||
|
||||
if (this.accessTokenResponseCustomizer != null) { |
||||
// @formatter:off
|
||||
OAuth2AccessTokenAuthenticationContext accessTokenAuthenticationContext = |
||||
OAuth2AccessTokenAuthenticationContext.with(accessTokenAuthentication) |
||||
.accessTokenResponse(builder) |
||||
.build(); |
||||
// @formatter:on
|
||||
this.accessTokenResponseCustomizer.accept(accessTokenAuthenticationContext); |
||||
if (this.logger.isTraceEnabled()) { |
||||
this.logger.trace("Customized access token response"); |
||||
} |
||||
} |
||||
|
||||
OAuth2AccessTokenResponse accessTokenResponse = builder.build(); |
||||
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response); |
||||
this.accessTokenResponseConverter.write(accessTokenResponse, null, httpResponse); |
||||
} |
||||
|
||||
/** |
||||
* Sets the {@code Consumer} providing access to the {@link OAuth2AccessTokenAuthenticationContext} |
||||
* containing an {@link OAuth2AccessTokenResponse.Builder} and additional context information. |
||||
* |
||||
* @param accessTokenResponseCustomizer the {@code Consumer} providing access to the {@link OAuth2AccessTokenAuthenticationContext} containing an {@link OAuth2AccessTokenResponse.Builder} |
||||
*/ |
||||
public void setAccessTokenResponseCustomizer(Consumer<OAuth2AccessTokenAuthenticationContext> accessTokenResponseCustomizer) { |
||||
Assert.notNull(accessTokenResponseCustomizer, "accessTokenResponseCustomizer cannot be null"); |
||||
this.accessTokenResponseCustomizer = accessTokenResponseCustomizer; |
||||
} |
||||
} |
||||
@ -0,0 +1,71 @@
@@ -0,0 +1,71 @@
|
||||
/* |
||||
* Copyright 2020-2024 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 org.springframework.security.oauth2.server.authorization.authentication; |
||||
|
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; |
||||
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; |
||||
import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations; |
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; |
||||
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients; |
||||
|
||||
import java.security.Principal; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
||||
|
||||
/** |
||||
* Tests for {@link OAuth2AccessTokenAuthenticationContext} |
||||
* |
||||
* @author Dmitriy Dubson |
||||
*/ |
||||
public class OAuth2AccessTokenAuthenticationContextTest { |
||||
private final RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); |
||||
private final OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(this.registeredClient).build(); |
||||
private final Authentication principal = this.authorization.getAttribute(Principal.class.getName()); |
||||
private final OAuth2AccessTokenAuthenticationToken accessTokenAuthenticationToken = new OAuth2AccessTokenAuthenticationToken(registeredClient, principal, |
||||
authorization.getAccessToken().getToken(), authorization.getRefreshToken().getToken()); |
||||
|
||||
@Test |
||||
public void withWhenAuthenticationNullThenThrowIllegalArgumentException() { |
||||
assertThatThrownBy(() -> OAuth2AccessTokenAuthenticationContext.with(null)) |
||||
.isInstanceOf(IllegalArgumentException.class) |
||||
.hasMessage("authentication cannot be null"); |
||||
} |
||||
|
||||
@Test |
||||
public void setWhenValueNullThenThrowIllegalArgumentException() { |
||||
OAuth2AccessTokenAuthenticationContext.Builder builder = |
||||
OAuth2AccessTokenAuthenticationContext.with(this.accessTokenAuthenticationToken); |
||||
|
||||
assertThatThrownBy(() -> builder.accessTokenResponse(null)) |
||||
.isInstanceOf(IllegalArgumentException.class).hasMessage("value cannot be null"); |
||||
} |
||||
|
||||
@Test |
||||
public void buildWhenAllValuesProvidedThenAllValuesAreSet() { |
||||
OAuth2AccessTokenResponse.Builder accessTokenResponseBuilder = OAuth2AccessTokenResponse.withToken(this.accessTokenAuthenticationToken.getAccessToken().getTokenValue()); |
||||
OAuth2AccessTokenAuthenticationContext context = |
||||
OAuth2AccessTokenAuthenticationContext.with(this.accessTokenAuthenticationToken) |
||||
.accessTokenResponse(accessTokenResponseBuilder) |
||||
.build(); |
||||
|
||||
assertThat(context.<Authentication>getAuthentication()).isEqualTo(this.accessTokenAuthenticationToken); |
||||
assertThat(context.getAccessTokenResponse()).isEqualTo(accessTokenResponseBuilder); |
||||
} |
||||
} |
||||
@ -0,0 +1,173 @@
@@ -0,0 +1,173 @@
|
||||
/* |
||||
* Copyright 2020-2024 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 org.springframework.security.oauth2.server.authorization.web.authentication; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
import org.springframework.http.HttpStatus; |
||||
import org.springframework.http.converter.HttpMessageConverter; |
||||
import org.springframework.mock.http.client.MockClientHttpResponse; |
||||
import org.springframework.mock.web.MockHttpServletRequest; |
||||
import org.springframework.mock.web.MockHttpServletResponse; |
||||
import org.springframework.security.core.Authentication; |
||||
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.OAuth2ErrorCodes; |
||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken; |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; |
||||
import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter; |
||||
import org.springframework.security.oauth2.server.authorization.InMemoryOAuth2AuthorizationService; |
||||
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; |
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService; |
||||
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType; |
||||
import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations; |
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationContext; |
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken; |
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken; |
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationToken; |
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; |
||||
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients; |
||||
|
||||
import java.time.Instant; |
||||
import java.time.temporal.ChronoUnit; |
||||
import java.util.Collections; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
import java.util.function.Consumer; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
||||
import static org.assertj.core.api.Assertions.within; |
||||
|
||||
/** |
||||
* Tests for {@link OAuth2AccessTokenResponseAuthenticationSuccessHandler}. |
||||
* |
||||
* @author Dmitriy Dubson |
||||
*/ |
||||
public class OAuth2AccessTokenResponseAuthenticationSuccessHandlerTests { |
||||
private final RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); |
||||
|
||||
private final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter = |
||||
new OAuth2AccessTokenResponseHttpMessageConverter(); |
||||
|
||||
private final OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( |
||||
this.registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, this.registeredClient.getClientSecret()); |
||||
|
||||
private final OAuth2AccessTokenResponseAuthenticationSuccessHandler authenticationSuccessHandler = new OAuth2AccessTokenResponseAuthenticationSuccessHandler(); |
||||
|
||||
@Test |
||||
public void setAccessTokenResponseCustomizerWhenNullThenThrowIllegalArgumentException() { |
||||
// @formatter:off
|
||||
assertThatThrownBy(() -> this.authenticationSuccessHandler.setAccessTokenResponseCustomizer(null)) |
||||
.isInstanceOf(IllegalArgumentException.class) |
||||
.hasMessage("accessTokenResponseCustomizer cannot be null"); |
||||
// @formatter:on
|
||||
} |
||||
|
||||
@Test |
||||
public void onAuthenticationSuccessWhenProvidedRequestResponseAndAuthThenWritesAccessTokenToHttpResponse() throws Exception { |
||||
MockHttpServletRequest request = new MockHttpServletRequest(); |
||||
MockHttpServletResponse response = new MockHttpServletResponse(); |
||||
|
||||
Instant issuedAt = Instant.now(); |
||||
Instant expiresAt = issuedAt.plusSeconds(300); |
||||
OAuth2Authorization testAuthorization = TestOAuth2Authorizations.authorization(this.registeredClient).build(); |
||||
Map<String, Object> additionalParameters = Collections.singletonMap("param1", "value1"); |
||||
Authentication authentication = new OAuth2AccessTokenAuthenticationToken(this.registeredClient, clientPrincipal, |
||||
testAuthorization.getAccessToken().getToken(), testAuthorization.getRefreshToken().getToken(), |
||||
additionalParameters); |
||||
|
||||
this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, authentication); |
||||
|
||||
OAuth2AccessTokenResponse accessTokenResponse = readAccessTokenResponse(response); |
||||
assertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo("access-token"); |
||||
assertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER); |
||||
assertThat(accessTokenResponse.getAccessToken().getIssuedAt()).isCloseTo(issuedAt, within(2, ChronoUnit.SECONDS)); |
||||
assertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isCloseTo(expiresAt, within(2, ChronoUnit.SECONDS)); |
||||
assertThat(accessTokenResponse.getRefreshToken()).isNotNull(); |
||||
assertThat(accessTokenResponse.getRefreshToken().getTokenValue()).isEqualTo("refresh-token"); |
||||
assertThat(accessTokenResponse.getAdditionalParameters()).containsExactlyInAnyOrderEntriesOf( |
||||
Map.of("param1", "value1") |
||||
); |
||||
} |
||||
|
||||
@Test |
||||
public void onAuthenticationSuccessWhenAuthenticationIsNotInstanceOfOAuth2AccessTokenAuthenticationTokenThenThrowOAuth2AuthenticationException() { |
||||
MockHttpServletRequest request = new MockHttpServletRequest(); |
||||
MockHttpServletResponse response = new MockHttpServletResponse(); |
||||
|
||||
assertThatThrownBy(() -> |
||||
this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, Set.of(), Map.of()))) |
||||
.isInstanceOf(OAuth2AuthenticationException.class) |
||||
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) |
||||
.extracting("errorCode") |
||||
.isEqualTo(OAuth2ErrorCodes.SERVER_ERROR); |
||||
} |
||||
|
||||
@Test |
||||
public void onAuthenticationSuccessWhenAccessTokenResponseIsCustomizedViaAccessTokenResponseCustomizerThenResponseHasCustomizedFields() throws Exception { |
||||
MockHttpServletRequest request = new MockHttpServletRequest(); |
||||
MockHttpServletResponse response = new MockHttpServletResponse(); |
||||
OAuth2AuthorizationService authorizationService = new InMemoryOAuth2AuthorizationService(); |
||||
OAuth2Authorization testAuthorization = TestOAuth2Authorizations.authorization(this.registeredClient).build(); |
||||
authorizationService.save(testAuthorization); |
||||
|
||||
Instant issuedAt = Instant.now(); |
||||
Instant expiresAt = issuedAt.plusSeconds(300); |
||||
OAuth2AccessToken accessToken = testAuthorization.getAccessToken().getToken(); |
||||
OAuth2RefreshToken refreshToken = testAuthorization.getRefreshToken().getToken(); |
||||
Map<String, Object> additionalParameters = Collections.singletonMap("param1", "value1"); |
||||
Authentication authentication = new OAuth2AccessTokenAuthenticationToken(this.registeredClient, clientPrincipal, accessToken, refreshToken, additionalParameters); |
||||
|
||||
Consumer<OAuth2AccessTokenAuthenticationContext> accessTokenResponseCustomizer = (OAuth2AccessTokenAuthenticationContext authenticationContext) -> { |
||||
OAuth2AccessTokenAuthenticationToken authenticationToken = authenticationContext.getAuthentication(); |
||||
OAuth2AccessTokenResponse.Builder accessTokenResponse = authenticationContext.getAccessTokenResponse(); |
||||
OAuth2Authorization authorization = authorizationService.findByToken( |
||||
authenticationToken.getAccessToken().getTokenValue(), |
||||
OAuth2TokenType.ACCESS_TOKEN |
||||
); |
||||
Map<String, Object> customParams = Map.of( |
||||
"authorization_id", authorization.getId(), |
||||
"registered_client_id", authorization.getRegisteredClientId() |
||||
); |
||||
Map<String, Object> allParams = new HashMap<>(authenticationToken.getAdditionalParameters()); |
||||
allParams.putAll(customParams); |
||||
accessTokenResponse.additionalParameters(allParams); |
||||
}; |
||||
|
||||
this.authenticationSuccessHandler.setAccessTokenResponseCustomizer(accessTokenResponseCustomizer); |
||||
this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, authentication); |
||||
|
||||
OAuth2AccessTokenResponse accessTokenResponse = readAccessTokenResponse(response); |
||||
assertThat(accessTokenResponse.getAccessToken().getTokenValue()).isEqualTo("access-token"); |
||||
assertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER); |
||||
assertThat(accessTokenResponse.getAccessToken().getIssuedAt()).isCloseTo(issuedAt, within(2, ChronoUnit.SECONDS)); |
||||
assertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isCloseTo(expiresAt, within(2, ChronoUnit.SECONDS)); |
||||
assertThat(accessTokenResponse.getRefreshToken()).isNotNull(); |
||||
assertThat(accessTokenResponse.getRefreshToken().getTokenValue()).isEqualTo("refresh-token"); |
||||
assertThat(accessTokenResponse.getAdditionalParameters()).containsExactlyInAnyOrderEntriesOf( |
||||
Map.of("param1", "value1", "authorization_id", "id", "registered_client_id", "registration-1") |
||||
); |
||||
} |
||||
|
||||
private OAuth2AccessTokenResponse readAccessTokenResponse(MockHttpServletResponse response) throws Exception { |
||||
MockClientHttpResponse httpResponse = new MockClientHttpResponse( |
||||
response.getContentAsByteArray(), HttpStatus.valueOf(response.getStatus())); |
||||
return this.accessTokenHttpResponseConverter.read(OAuth2AccessTokenResponse.class, httpResponse); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue