Browse Source

Add token endpoint implementation

Fixes gh-67
kratostaine 5 years ago
parent
commit
8dbdd6640c

+ 10 - 0
core/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2Authorization.java

@@ -33,6 +33,7 @@ import java.util.function.Consumer;
  *
  * @author Joe Grandja
  * @author Krisztian Toth
+ * @author Madhu Bhat
  * @since 0.0.1
  * @see RegisteredClient
  * @see OAuth2AccessToken
@@ -74,6 +75,15 @@ public class OAuth2Authorization implements Serializable {
 		return this.accessToken;
 	}
 
+	/**
+	 * Sets the access token {@link OAuth2AccessToken} in the {@link OAuth2Authorization}.
+	 *
+	 * @param accessToken the access token
+	 */
+	public final void setAccessToken(OAuth2AccessToken accessToken) {
+		this.accessToken = accessToken;
+	}
+
 	/**
 	 * Returns the attribute(s) associated to the authorization.
 	 *

+ 10 - 0
core/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AccessTokenAuthenticationToken.java

@@ -25,6 +25,7 @@ import java.util.Collections;
 
 /**
  * @author Joe Grandja
+ * @author Madhu Bhat
  */
 public class OAuth2AccessTokenAuthenticationToken extends AbstractAuthenticationToken {
 	private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
@@ -49,4 +50,13 @@ public class OAuth2AccessTokenAuthenticationToken extends AbstractAuthentication
 	public Object getPrincipal() {
 		return null;
 	}
+
+	/**
+	 * Returns the access token {@link OAuth2AccessToken}.
+	 *
+	 * @return the access token
+	 */
+	public OAuth2AccessToken getAccessToken() {
+		return this.accessToken;
+	}
 }

+ 10 - 0
core/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationToken.java

@@ -24,6 +24,7 @@ import java.util.Collections;
 
 /**
  * @author Joe Grandja
+ * @author Madhu Bhat
  */
 public class OAuth2AuthorizationCodeAuthenticationToken extends AbstractAuthenticationToken {
 	private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
@@ -57,4 +58,13 @@ public class OAuth2AuthorizationCodeAuthenticationToken extends AbstractAuthenti
 	public Object getPrincipal() {
 		return null;
 	}
+
+	/**
+	 * Returns the code.
+	 *
+	 * @return the code
+	 */
+	public String getCode() {
+		return this.code;
+	}
 }

+ 148 - 1
core/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilter.java

@@ -15,10 +15,31 @@
  */
 package org.springframework.security.oauth2.server.authorization.web;
 
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import org.springframework.core.convert.converter.Converter;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
+import org.springframework.security.oauth2.core.OAuth2AccessToken;
+import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
+import org.springframework.security.oauth2.core.OAuth2Error;
+import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
+import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
+import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
 import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
+import org.springframework.security.oauth2.server.authorization.TokenType;
+import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;
+import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken;
+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;
 import org.springframework.web.filter.OncePerRequestFilter;
 
 import javax.servlet.FilterChain;
@@ -26,19 +47,145 @@ import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
+import java.io.Writer;
 
 /**
+ * This {@code Filter} is used by the client to obtain an access token by presenting
+ * its authorization grant.
+ *
+ * <p>
+ * It converts the OAuth 2.0 Access Token Request to {@link OAuth2AuthorizationCodeAuthenticationToken},
+ * which is then authenticated by the {@link AuthenticationManager} and gets back
+ * {@link OAuth2AccessTokenAuthenticationToken} which has the {@link OAuth2AccessToken} if the request
+ * was successfully authenticated. The {@link OAuth2AccessToken} is then updated in the in-flight {@link OAuth2Authorization}
+ * and sent back to the client. In case the authentication fails, an HTTP 401 (Unauthorized) response is returned.
+ *
+ * <p>
+ * By default, this {@code Filter} responds to access token requests
+ * at the {@code URI} {@code /oauth2/token} and {@code HttpMethod} {@code POST}
+ * using the default {@link AntPathRequestMatcher}.
+ *
+ * <p>
+ * The default base {@code URI} {@code /oauth2/token} may be overridden
+ * via the constructor {@link #OAuth2TokenEndpointFilter(OAuth2AuthorizationService, AuthenticationManager, String)}.
+ *
  * @author Joe Grandja
+ * @author Madhu Bhat
  */
 public class OAuth2TokenEndpointFilter extends OncePerRequestFilter {
-	private Converter<HttpServletRequest, Authentication> authorizationGrantConverter;
+	/**
+	 * The default endpoint {@code URI} for access token requests.
+	 */
+	private static final String DEFAULT_TOKEN_ENDPOINT_URI = "/oauth2/token";
+
+	private Converter<HttpServletRequest, Authentication> authorizationGrantConverter = this::convert;
 	private AuthenticationManager authenticationManager;
 	private OAuth2AuthorizationService authorizationService;
+	private RequestMatcher uriMatcher;
+	private ObjectMapper objectMapper = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL);
+
+	/**
+	 * Constructs an {@code OAuth2TokenEndpointFilter} using the provided parameters.
+	 *
+	 * @param authorizationService  the authorization service implementation
+	 * @param authenticationManager the authentication manager implementation
+	 */
+	public OAuth2TokenEndpointFilter(OAuth2AuthorizationService authorizationService, AuthenticationManager authenticationManager) {
+		Assert.notNull(authorizationService, "authorizationService cannot be null");
+		Assert.notNull(authenticationManager, "authenticationManager cannot be null");
+		this.authenticationManager = authenticationManager;
+		this.authorizationService = authorizationService;
+		this.uriMatcher = new AntPathRequestMatcher(DEFAULT_TOKEN_ENDPOINT_URI, HttpMethod.POST.name());
+	}
+
+	/**
+	 * Constructs an {@code OAuth2TokenEndpointFilter} using the provided parameters.
+	 *
+	 * @param authorizationService  the authorization service implementation
+	 * @param authenticationManager the authentication manager implementation
+	 * @param tokenEndpointUri      the token endpoint's uri
+	 */
+	public OAuth2TokenEndpointFilter(OAuth2AuthorizationService authorizationService, AuthenticationManager authenticationManager,
+			String tokenEndpointUri) {
+		Assert.notNull(authorizationService, "authorizationService cannot be null");
+		Assert.notNull(authenticationManager, "authenticationManager cannot be null");
+		Assert.hasText(tokenEndpointUri, "tokenEndpointUri cannot be empty");
+		this.authenticationManager = authenticationManager;
+		this.authorizationService = authorizationService;
+		this.uriMatcher = new AntPathRequestMatcher(tokenEndpointUri, HttpMethod.POST.name());
+	}
 
 	@Override
 	protected void doFilterInternal(HttpServletRequest request,
 			HttpServletResponse response, FilterChain filterChain)
 			throws ServletException, IOException {
+		if (uriMatcher.matches(request)) {
+			try {
+				if (validateAccessTokenRequest(request)) {
+					OAuth2AuthorizationCodeAuthenticationToken authCodeAuthToken =
+							(OAuth2AuthorizationCodeAuthenticationToken) authorizationGrantConverter.convert(request);
+					OAuth2AccessTokenAuthenticationToken accessTokenAuthenticationToken =
+							(OAuth2AccessTokenAuthenticationToken) authenticationManager.authenticate(authCodeAuthToken);
+					if (accessTokenAuthenticationToken.isAuthenticated()) {
+						OAuth2Authorization authorization = authorizationService
+								.findByTokenAndTokenType(authCodeAuthToken.getCode(), TokenType.AUTHORIZATION_CODE);
+						authorization.setAccessToken(accessTokenAuthenticationToken.getAccessToken());
+						authorizationService.save(authorization);
+						writeSuccessResponse(response, accessTokenAuthenticationToken.getAccessToken());
+					} else {
+						throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT));
+					}
+				}
+			} catch (OAuth2AuthenticationException exception) {
+				SecurityContextHolder.clearContext();
+				writeFailureResponse(response, exception.getError());
+			}
+		} else {
+			filterChain.doFilter(request, response);
+		}
+	}
+
+	private boolean validateAccessTokenRequest(HttpServletRequest request) {
+		if (StringUtils.isEmpty(request.getParameter(OAuth2ParameterNames.CODE))
+				|| StringUtils.isEmpty(request.getParameter(OAuth2ParameterNames.REDIRECT_URI))
+				|| StringUtils.isEmpty(request.getParameter(OAuth2ParameterNames.GRANT_TYPE))) {
+			throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST));
+		} else if (!AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equals(request.getParameter(OAuth2ParameterNames.GRANT_TYPE))) {
+			throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.UNSUPPORTED_GRANT_TYPE));
+		}
+		return true;
+	}
+
+	private OAuth2AuthorizationCodeAuthenticationToken convert(HttpServletRequest request) {
+		Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();
+		return new OAuth2AuthorizationCodeAuthenticationToken(
+				request.getParameter(OAuth2ParameterNames.CODE),
+				clientPrincipal,
+				request.getParameter(OAuth2ParameterNames.REDIRECT_URI)
+		);
+	}
+
+	private void writeSuccessResponse(HttpServletResponse response, OAuth2AccessToken body) throws IOException {
+		try (Writer out = response.getWriter()) {
+			response.setStatus(HttpStatus.OK.value());
+			response.setContentType(MediaType.APPLICATION_JSON_VALUE);
+			response.setCharacterEncoding("UTF-8");
+			response.setHeader(HttpHeaders.CACHE_CONTROL, "no-store");
+			response.setHeader(HttpHeaders.PRAGMA, "no-cache");
+			out.write(objectMapper.writeValueAsString(body));
+		}
+	}
 
+	private void writeFailureResponse(HttpServletResponse response, OAuth2Error error) throws IOException {
+		try (Writer out = response.getWriter()) {
+			if (error.getErrorCode().equals(OAuth2ErrorCodes.INVALID_CLIENT)) {
+				response.setStatus(HttpStatus.UNAUTHORIZED.value());
+			} else {
+				response.setStatus(HttpStatus.BAD_REQUEST.value());
+			}
+			response.setContentType(MediaType.APPLICATION_JSON_VALUE);
+			response.setCharacterEncoding("UTF-8");
+			out.write(objectMapper.writeValueAsString(error));
+		}
 	}
 }

+ 230 - 0
core/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilterTests.java

@@ -0,0 +1,230 @@
+/*
+ * Copyright 2020 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;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
+import org.springframework.security.oauth2.core.OAuth2AccessToken;
+import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
+import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
+import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationAttributeNames;
+import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
+import org.springframework.security.oauth2.server.authorization.TokenType;
+import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;
+import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
+import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
+
+import javax.servlet.FilterChain;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.time.Instant;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for {@link OAuth2TokenEndpointFilter}.
+ *
+ * @author Madhu Bhat
+ */
+public class OAuth2TokenEndpointFilterTests {
+
+	private OAuth2TokenEndpointFilter filter;
+	private OAuth2AuthorizationService authorizationService = mock(OAuth2AuthorizationService.class);
+	private AuthenticationManager authenticationManager = mock(AuthenticationManager.class);
+	private FilterChain filterChain = mock(FilterChain.class);
+	private String requestUri;
+	private static final RegisteredClient REGISTERED_CLIENT = TestRegisteredClients.registeredClient().build();
+	private static final String PRINCIPAL_NAME = "principal";
+	private static final String AUTHORIZATION_CODE = "code";
+
+	@Before
+	public void setUp() {
+		this.filter = new OAuth2TokenEndpointFilter(this.authorizationService, this.authenticationManager);
+		this.requestUri = "/oauth2/token";
+	}
+
+	@Test
+	public void constructorServiceAndManagerWhenNullThenThrowIllegalArgumentException() {
+		assertThatThrownBy(() -> {
+			new OAuth2TokenEndpointFilter(null, null);
+		}).isInstanceOf(IllegalArgumentException.class);
+	}
+
+	@Test
+	public void constructorServiceAndManagerAndEndpointWhenNullThenThrowIllegalArgumentException() {
+		assertThatThrownBy(() -> {
+			new OAuth2TokenEndpointFilter(null, null, null);
+		}).isInstanceOf(IllegalArgumentException.class);
+	}
+
+	@Test
+	public void doFilterWhenNotTokenRequestThenNextFilter() throws Exception {
+		this.requestUri = "/path";
+		MockHttpServletRequest request = new MockHttpServletRequest("GET", this.requestUri);
+		request.setServletPath(this.requestUri);
+		MockHttpServletResponse response = new MockHttpServletResponse();
+
+		this.filter.doFilter(request, response, this.filterChain);
+
+		verify(this.filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));
+	}
+
+	@Test
+	public void doFilterWhenAccessTokenRequestWithoutGrantTypeThenRespondWithBadRequest() throws Exception {
+		MockHttpServletRequest request = new MockHttpServletRequest("POST", this.requestUri);
+		request.addParameter(OAuth2ParameterNames.CODE, "testAuthCode");
+		request.addParameter(OAuth2ParameterNames.REDIRECT_URI, "testRedirectUri");
+		request.setServletPath(this.requestUri);
+		MockHttpServletResponse response = new MockHttpServletResponse();
+
+		this.filter.doFilter(request, response, this.filterChain);
+
+		verifyNoInteractions(this.filterChain);
+		assertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());
+		assertThat(response.getContentAsString()).isEqualTo("{\"errorCode\":\"invalid_request\"}");
+	}
+
+	@Test
+	public void doFilterWhenAccessTokenRequestWithoutCodeThenRespondWithBadRequest() throws Exception {
+		MockHttpServletRequest request = new MockHttpServletRequest("POST", this.requestUri);
+		request.addParameter(OAuth2ParameterNames.GRANT_TYPE, "testGrantType");
+		request.addParameter(OAuth2ParameterNames.REDIRECT_URI, "testRedirectUri");
+		request.setServletPath(this.requestUri);
+		MockHttpServletResponse response = new MockHttpServletResponse();
+
+		this.filter.doFilter(request, response, this.filterChain);
+
+		verifyNoInteractions(this.filterChain);
+		assertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());
+		assertThat(response.getContentAsString()).isEqualTo("{\"errorCode\":\"invalid_request\"}");
+	}
+
+	@Test
+	public void doFilterWhenAccessTokenRequestWithoutRedirectUriThenRespondWithBadRequest() throws Exception {
+		MockHttpServletRequest request = new MockHttpServletRequest("POST", this.requestUri);
+		request.addParameter(OAuth2ParameterNames.GRANT_TYPE, "testGrantType");
+		request.addParameter(OAuth2ParameterNames.CODE, "testAuthCode");
+		request.setServletPath(this.requestUri);
+		MockHttpServletResponse response = new MockHttpServletResponse();
+
+		this.filter.doFilter(request, response, this.filterChain);
+
+		verifyNoInteractions(this.filterChain);
+		assertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());
+		assertThat(response.getContentAsString()).isEqualTo("{\"errorCode\":\"invalid_request\"}");
+	}
+
+	@Test
+	public void doFilterWhenAccessTokenRequestWithoutAuthCodeGrantTypeThenRespondWithBadRequest() throws Exception {
+		MockHttpServletRequest request = new MockHttpServletRequest("POST", this.requestUri);
+		request.addParameter(OAuth2ParameterNames.GRANT_TYPE, "testGrantType");
+		request.addParameter(OAuth2ParameterNames.CODE, "testAuthCode");
+		request.addParameter(OAuth2ParameterNames.REDIRECT_URI, "testRedirectUri");
+		request.setServletPath(this.requestUri);
+		MockHttpServletResponse response = new MockHttpServletResponse();
+
+		this.filter.doFilter(request, response, this.filterChain);
+
+		verifyNoInteractions(this.filterChain);
+		assertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());
+		assertThat(response.getContentAsString()).isEqualTo("{\"errorCode\":\"unsupported_grant_type\"}");
+	}
+
+	@Test
+	public void doFilterWhenAccessTokenRequestIsNotAuthenticatedThenRespondWithUnauthorized() throws Exception {
+		MockHttpServletRequest request = new MockHttpServletRequest("POST", this.requestUri);
+		request.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue());
+		request.addParameter(OAuth2ParameterNames.CODE, "testAuthCode");
+		request.addParameter(OAuth2ParameterNames.REDIRECT_URI, "testRedirectUri");
+		request.setServletPath(this.requestUri);
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		Authentication clientPrincipal = mock(Authentication.class);
+		RegisteredClient registeredClient = mock(RegisteredClient.class);
+
+		OAuth2AccessToken accessToken = new OAuth2AccessToken(
+				OAuth2AccessToken.TokenType.BEARER,  "testToken", Instant.now().minusSeconds(60), Instant.now());
+		OAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)
+				.principalName(PRINCIPAL_NAME)
+				.attribute(OAuth2AuthorizationAttributeNames.CODE, AUTHORIZATION_CODE)
+				.build();
+		OAuth2AccessTokenAuthenticationToken accessTokenAuthenticationToken =
+				new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken);
+		accessTokenAuthenticationToken.setAuthenticated(false);
+
+		when(this.authorizationService.findByTokenAndTokenType(anyString(), any(TokenType.class))).thenReturn(authorization);
+		when(this.authenticationManager.authenticate(any(Authentication.class))).thenReturn(accessTokenAuthenticationToken);
+
+		this.filter.doFilter(request, response, this.filterChain);
+
+		verifyNoInteractions(this.filterChain);
+		verify(this.authorizationService, times(0)).save(authorization);
+		verify(this.authenticationManager, times(1)).authenticate(any(Authentication.class));
+		assertThat(response.getStatus()).isEqualTo(HttpStatus.UNAUTHORIZED.value());
+		assertThat(response.getContentAsString())
+				.isEqualTo("{\"errorCode\":\"invalid_client\"}");
+	}
+
+	@Test
+	public void doFilterWhenValidAccessTokenRequestThenRespondWithAccessToken() throws Exception {
+		MockHttpServletRequest request = new MockHttpServletRequest("POST", this.requestUri);
+		request.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue());
+		request.addParameter(OAuth2ParameterNames.CODE, "testAuthCode");
+		request.addParameter(OAuth2ParameterNames.REDIRECT_URI, "testRedirectUri");
+		request.setServletPath(this.requestUri);
+		MockHttpServletResponse response = new MockHttpServletResponse();
+		Authentication clientPrincipal = mock(Authentication.class);
+		RegisteredClient registeredClient = mock(RegisteredClient.class);
+
+		OAuth2AccessToken accessToken = new OAuth2AccessToken(
+				OAuth2AccessToken.TokenType.BEARER,  "testToken", Instant.now().minusSeconds(60), Instant.now());
+		OAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)
+				.principalName(PRINCIPAL_NAME)
+				.attribute(OAuth2AuthorizationAttributeNames.CODE, AUTHORIZATION_CODE)
+				.build();
+		OAuth2AccessTokenAuthenticationToken accessTokenAuthenticationToken =
+				new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken);
+		accessTokenAuthenticationToken.setAuthenticated(true);
+
+		when(this.authorizationService.findByTokenAndTokenType(anyString(), any(TokenType.class))).thenReturn(authorization);
+		when(this.authenticationManager.authenticate(any(Authentication.class))).thenReturn(accessTokenAuthenticationToken);
+
+		this.filter.doFilter(request, response, this.filterChain);
+
+		verifyNoInteractions(this.filterChain);
+		verify(this.authorizationService, times(1)).save(authorization);
+		verify(this.authenticationManager, times(1)).authenticate(any(Authentication.class));
+		assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
+		assertThat(response.getContentAsString()).contains("\"tokenValue\":\"testToken\"");
+		assertThat(response.getContentAsString()).contains("\"tokenType\":{\"value\":\"Bearer\"}");
+		assertThat(response.getHeader(HttpHeaders.CACHE_CONTROL)).isEqualTo("no-store");
+		assertThat(response.getHeader(HttpHeaders.PRAGMA)).isEqualTo("no-cache");
+	}
+}