keycloak-aplcache
Changes
adapters/oidc/spring-security/src/main/java/org/keycloak/adapters/springsecurity/authentication/KeycloakAuthenticationFailureHandler.java 41(+41 -0)
Details
diff --git a/adapters/oidc/spring-security/src/main/java/org/keycloak/adapters/springsecurity/authentication/KeycloakAuthenticationFailureHandler.java b/adapters/oidc/spring-security/src/main/java/org/keycloak/adapters/springsecurity/authentication/KeycloakAuthenticationFailureHandler.java
new file mode 100644
index 0000000..fcff678
--- /dev/null
+++ b/adapters/oidc/spring-security/src/main/java/org/keycloak/adapters/springsecurity/authentication/KeycloakAuthenticationFailureHandler.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * 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
+ *
+ * http://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.keycloak.adapters.springsecurity.authentication;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.authentication.AuthenticationFailureHandler;
+
+/**
+ * To return the forbidden code with the corresponding message.
+ *
+ * @author emilienbondu
+ *
+ */
+public class KeycloakAuthenticationFailureHandler implements AuthenticationFailureHandler {
+
+ @Override
+ public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
+ response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unable to authenticate using the Authorization header");
+ }
+}
diff --git a/adapters/oidc/spring-security/src/main/java/org/keycloak/adapters/springsecurity/filter/KeycloakAuthenticationProcessingFilter.java b/adapters/oidc/spring-security/src/main/java/org/keycloak/adapters/springsecurity/filter/KeycloakAuthenticationProcessingFilter.java
index f9fb19e..fabf30e 100644
--- a/adapters/oidc/spring-security/src/main/java/org/keycloak/adapters/springsecurity/filter/KeycloakAuthenticationProcessingFilter.java
+++ b/adapters/oidc/spring-security/src/main/java/org/keycloak/adapters/springsecurity/filter/KeycloakAuthenticationProcessingFilter.java
@@ -17,6 +17,13 @@
package org.keycloak.adapters.springsecurity.filter;
+import java.io.IOException;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
import org.keycloak.adapters.AdapterDeploymentContext;
import org.keycloak.adapters.AdapterTokenStore;
import org.keycloak.adapters.KeycloakDeployment;
@@ -25,7 +32,7 @@ import org.keycloak.adapters.spi.AuthChallenge;
import org.keycloak.adapters.spi.AuthOutcome;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.springsecurity.KeycloakAuthenticationException;
-import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationEntryPoint;
+import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationFailureHandler;
import org.keycloak.adapters.springsecurity.authentication.SpringSecurityRequestAuthenticator;
import org.keycloak.adapters.springsecurity.facade.SimpleHttpFacade;
import org.keycloak.adapters.springsecurity.token.AdapterTokenStoreFactory;
@@ -41,19 +48,12 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
-import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-
/**
* Provides a Keycloak authentication processing filter.
*
@@ -61,17 +61,15 @@ import java.io.IOException;
* @version $Revision: 1 $
*/
public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter implements ApplicationContextAware {
- public static final String DEFAULT_LOGIN_URL = "/sso/login";
+
public static final String AUTHORIZATION_HEADER = "Authorization";
public static final String SCHEME_BEARER = "bearer ";
public static final String SCHEME_BASIC = "basic ";
/**
- * Request matcher that matches requests to the {@link KeycloakAuthenticationEntryPoint#DEFAULT_LOGIN_URI default login URI}
- * and any request with a <code>Authorization</code> header.
+ * Request matcher that matches all requests.
*/
- public static final RequestMatcher DEFAULT_REQUEST_MATCHER =
- new OrRequestMatcher(new AntPathRequestMatcher(DEFAULT_LOGIN_URL), new RequestHeaderRequestMatcher(AUTHORIZATION_HEADER));
+ private static RequestMatcher DEFAULT_REQUEST_MATCHER = new AntPathRequestMatcher("/**");
private static final Logger log = LoggerFactory.getLogger(KeycloakAuthenticationProcessingFilter.class);
@@ -89,7 +87,7 @@ public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticati
*/
public KeycloakAuthenticationProcessingFilter(AuthenticationManager authenticationManager) {
this(authenticationManager, DEFAULT_REQUEST_MATCHER);
- setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler(DEFAULT_LOGIN_URL));
+ setAuthenticationFailureHandler(new KeycloakAuthenticationFailureHandler());
}
/**
@@ -140,7 +138,18 @@ public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticati
log.debug("Auth outcome: {}", result);
if (AuthOutcome.FAILED.equals(result)) {
- throw new KeycloakAuthenticationException("Auth outcome: " + result);
+ AuthChallenge challenge = authenticator.getChallenge();
+ if (challenge != null) {
+ challenge.challenge(facade);
+ }
+ throw new KeycloakAuthenticationException("Invalid authorization header, see WWW-Authenticate header for details");
+ }
+ if (AuthOutcome.NOT_ATTEMPTED.equals(result)) {
+ AuthChallenge challenge = authenticator.getChallenge();
+ if (challenge != null) {
+ challenge.challenge(facade);
+ }
+ throw new KeycloakAuthenticationException("Authorization header not found, see WWW-Authenticate header");
}
else if (AuthOutcome.AUTHENTICATED.equals(result)) {
@@ -211,22 +220,10 @@ public class KeycloakAuthenticationProcessingFilter extends AbstractAuthenticati
}
@Override
- protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
- AuthenticationException failed) throws IOException, ServletException {
-
- if (this.isBearerTokenRequest(request)) {
- SecurityContextHolder.clearContext();
- response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unable to authenticate bearer token");
- return;
- }
- else if (this.isBasicAuthRequest(request)) {
- SecurityContextHolder.clearContext();
- response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unable to authenticate with basic authentication");
- return;
- }
-
- super.unsuccessfulAuthentication(request, response, failed);
- }
+ protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
+ AuthenticationException failed) throws IOException, ServletException {
+ super.unsuccessfulAuthentication(request, response, failed);
+ }
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
diff --git a/adapters/oidc/spring-security/src/test/java/org/keycloak/adapters/springsecurity/filter/KeycloakAuthenticationProcessingFilterTest.java b/adapters/oidc/spring-security/src/test/java/org/keycloak/adapters/springsecurity/filter/KeycloakAuthenticationProcessingFilterTest.java
index 515de4b..2414c38 100755
--- a/adapters/oidc/spring-security/src/test/java/org/keycloak/adapters/springsecurity/filter/KeycloakAuthenticationProcessingFilterTest.java
+++ b/adapters/oidc/spring-security/src/test/java/org/keycloak/adapters/springsecurity/filter/KeycloakAuthenticationProcessingFilterTest.java
@@ -27,6 +27,7 @@ import org.keycloak.adapters.OidcKeycloakAccount;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.springsecurity.KeycloakAuthenticationException;
import org.keycloak.adapters.springsecurity.account.KeycloakRole;
+import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationFailureHandler;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.keycloak.common.enums.SslRequired;
import org.keycloak.common.util.KeycloakUriBuilder;
@@ -89,6 +90,8 @@ public class KeycloakAuthenticationProcessingFilterTest {
@Mock
private AuthenticationFailureHandler failureHandler;
+
+ private KeycloakAuthenticationFailureHandler keycloakFailureHandler;
@Mock
private OidcKeycloakAccount keycloakAccount;
@@ -106,6 +109,7 @@ public class KeycloakAuthenticationProcessingFilterTest {
MockitoAnnotations.initMocks(this);
request = spy(new MockHttpServletRequest());
filter = new KeycloakAuthenticationProcessingFilter(authenticationManager);
+ keycloakFailureHandler = new KeycloakAuthenticationFailureHandler();
filter.setApplicationContext(applicationContext);
filter.setAuthenticationSuccessHandler(successHandler);
@@ -155,10 +159,12 @@ public class KeycloakAuthenticationProcessingFilterTest {
when(keycloakDeployment.getStateCookieName()).thenReturn("kc-cookie");
when(keycloakDeployment.getSslRequired()).thenReturn(SslRequired.NONE);
when(keycloakDeployment.isBearerOnly()).thenReturn(Boolean.FALSE);
- filter.attemptAuthentication(request, response);
-
- verify(response).setStatus(302);
- verify(response).setHeader(eq("Location"), startsWith("http://localhost:8080/auth"));
+ try {
+ filter.attemptAuthentication(request, response);
+ } catch (KeycloakAuthenticationException e) {
+ verify(response).setStatus(302);
+ verify(response).setHeader(eq("Location"), startsWith("http://localhost:8080/auth"));
+ }
}
@Test(expected = KeycloakAuthenticationException.class)
@@ -210,8 +216,7 @@ public class KeycloakAuthenticationProcessingFilterTest {
AuthenticationException exception = new BadCredentialsException("OOPS");
this.setBearerAuthHeader(request);
filter.unsuccessfulAuthentication(request, response, exception);
- verify(response).sendError(eq(HttpServletResponse.SC_UNAUTHORIZED), anyString());
- verify(failureHandler, never()).onAuthenticationFailure(any(HttpServletRequest.class), any(HttpServletResponse.class),
+ verify(failureHandler).onAuthenticationFailure(any(HttpServletRequest.class), any(HttpServletResponse.class),
any(AuthenticationException.class));
}
@@ -220,10 +225,18 @@ public class KeycloakAuthenticationProcessingFilterTest {
AuthenticationException exception = new BadCredentialsException("OOPS");
this.setBasicAuthHeader(request);
filter.unsuccessfulAuthentication(request, response, exception);
- verify(response).sendError(eq(HttpServletResponse.SC_UNAUTHORIZED), anyString());
- verify(failureHandler, never()).onAuthenticationFailure(any(HttpServletRequest.class), any(HttpServletResponse.class),
+ verify(failureHandler).onAuthenticationFailure(any(HttpServletRequest.class), any(HttpServletResponse.class),
any(AuthenticationException.class));
}
+
+ @Test
+ public void testDefaultFailureHanlder() throws Exception {
+ AuthenticationException exception = new BadCredentialsException("OOPS");
+ filter.setAuthenticationFailureHandler(keycloakFailureHandler);
+ filter.unsuccessfulAuthentication(request, response, exception);
+
+ verify(response).sendError(eq(HttpServletResponse.SC_UNAUTHORIZED), any(String.class));
+ }
@Test(expected = UnsupportedOperationException.class)
public void testSetAllowSessionCreation() throws Exception {