From ba51df0c37a877f4481d2774eb9d4e7336a91917 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Fri, 13 Mar 2015 18:30:09 -0400 Subject: [PATCH] consolidated client credential filter beans (note: imports magic from secoauth) --- .../webapp/WEB-INF/application-context.xml | 48 ++++------ ...rerClientAssertionTokenEndpointFilter.java | 93 ++++++++++++------- .../filter/MultiUrlRequestMatcher.java | 73 +++++++++++++++ 3 files changed, 152 insertions(+), 62 deletions(-) create mode 100644 openid-connect-server/src/main/java/org/mitre/openid/connect/filter/MultiUrlRequestMatcher.java diff --git a/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml b/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml index 10b2473a0..ea1cf3fc8 100644 --- a/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml +++ b/openid-connect-server-webapp/src/main/webapp/WEB-INF/application-context.xml @@ -22,10 +22,12 @@ xmlns:context="http://www.springframework.org/schema/context" xmlns:security="http://www.springframework.org/schema/security" xmlns:oauth="http://www.springframework.org/schema/security/oauth2" + xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd + http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> @@ -80,8 +82,8 @@ - - + + @@ -135,9 +137,9 @@ authentication-manager-ref="clientAuthenticationManager"> - + - + - + - + @@ -164,36 +166,26 @@ - - - - - - - - + + + + /introspect + /revoke + /token + + - + - + - + + - - - - - - - - - - - diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/assertion/JWTBearerClientAssertionTokenEndpointFilter.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/assertion/JWTBearerClientAssertionTokenEndpointFilter.java index 7dfc72048..d6ec49773 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/assertion/JWTBearerClientAssertionTokenEndpointFilter.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/assertion/JWTBearerClientAssertionTokenEndpointFilter.java @@ -22,14 +22,23 @@ package org.mitre.openid.connect.assertion; import java.io.IOException; import java.text.ParseException; +import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.mockito.AdditionalMatchers; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; +import org.springframework.security.oauth2.common.exceptions.BadClientCredentialsException; import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter; +import org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; +import org.springframework.security.web.util.matcher.RequestMatcher; import com.google.common.base.Strings; import com.nimbusds.jwt.JWT; @@ -41,14 +50,34 @@ import com.nimbusds.jwt.JWTParser; * @author jricher * */ -public class JWTBearerClientAssertionTokenEndpointFilter extends ClientCredentialsTokenEndpointFilter { +public class JWTBearerClientAssertionTokenEndpointFilter extends AbstractAuthenticationProcessingFilter { - public JWTBearerClientAssertionTokenEndpointFilter() { - super(); + private AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint(); + + public JWTBearerClientAssertionTokenEndpointFilter(RequestMatcher additionalMatcher) { + super(new ClientAssertionRequestMatcher(additionalMatcher)); + // If authentication fails the type is "Form" + ((OAuth2AuthenticationEntryPoint) authenticationEntryPoint).setTypeName("Form"); } - public JWTBearerClientAssertionTokenEndpointFilter(String path) { - super(path); + @Override + public void afterPropertiesSet() { + super.afterPropertiesSet(); + setAuthenticationFailureHandler(new AuthenticationFailureHandler() { + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, + AuthenticationException exception) throws IOException, ServletException { + if (exception instanceof BadCredentialsException) { + exception = new BadCredentialsException(exception.getMessage(), new BadClientCredentialsException()); + } + authenticationEntryPoint.commence(request, response, exception); + } + }); + setAuthenticationSuccessHandler(new AuthenticationSuccessHandler() { + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, + Authentication authentication) throws IOException, ServletException { + // no-op - just allow filter chain to continue to token endpoint + } + }); } /** @@ -74,42 +103,38 @@ public class JWTBearerClientAssertionTokenEndpointFilter extends ClientCredentia } } - /** - * Check to see if the "client_assertion_type" and "client_assertion" parameters are present and contain the right values. - */ @Override - protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) { - // check for appropriate parameters - String assertionType = request.getParameter("client_assertion_type"); - String assertion = request.getParameter("client_assertion"); + protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, + FilterChain chain, Authentication authResult) throws IOException, ServletException { + super.successfulAuthentication(request, response, chain, authResult); + chain.doFilter(request, response); + } - if (Strings.isNullOrEmpty(assertionType) || Strings.isNullOrEmpty(assertion)) { - return false; - } else if (!assertionType.equals("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")) { - return false; + private static class ClientAssertionRequestMatcher implements RequestMatcher { + + private RequestMatcher additionalMatcher; + + public ClientAssertionRequestMatcher(RequestMatcher additionalMatcher) { + this.additionalMatcher = additionalMatcher; } + + @Override + public boolean matches(HttpServletRequest request) { + // check for appropriate parameters + String assertionType = request.getParameter("client_assertion_type"); + String assertion = request.getParameter("client_assertion"); - - // Can't call to superclass here b/c client creds would break for lack of client_id - // return super.requiresAuthentication(request, response); - - String uri = request.getRequestURI(); - int pathParamIndex = uri.indexOf(';'); - - if (pathParamIndex > 0) { - // strip everything after the first semi-colon - uri = uri.substring(0, pathParamIndex); + if (Strings.isNullOrEmpty(assertionType) || Strings.isNullOrEmpty(assertion)) { + return false; + } else if (!assertionType.equals("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")) { + return false; + } + + return additionalMatcher.matches(request); } - - if ("".equals(request.getContextPath())) { - return uri.endsWith(getFilterProcessesUrl()); - } - - return uri.endsWith(request.getContextPath() + getFilterProcessesUrl()); - + } - } diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/filter/MultiUrlRequestMatcher.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/filter/MultiUrlRequestMatcher.java new file mode 100644 index 000000000..9e62f4354 --- /dev/null +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/filter/MultiUrlRequestMatcher.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright 2015 The MITRE Corporation + * and the MIT Kerberos and Internet Trust Consortium + * + * 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.mitre.openid.connect.filter; + +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.security.web.util.UrlUtils; +import org.springframework.security.web.util.matcher.RequestMatcher; +import org.springframework.util.Assert; + +import com.google.common.collect.ImmutableSet; + +/** + * @author jricher + * + */ +public class MultiUrlRequestMatcher implements RequestMatcher { + private final Set filterProcessesUrls; + + public MultiUrlRequestMatcher(Set filterProcessesUrls) { + for (String filterProcessesUrl : filterProcessesUrls) { + Assert.hasLength(filterProcessesUrl, "filterProcessesUrl must be specified"); + Assert.isTrue(UrlUtils.isValidRedirectUrl(filterProcessesUrl), filterProcessesUrl + " isn't a valid redirect URL"); + } + this.filterProcessesUrls = ImmutableSet.copyOf(filterProcessesUrls); + } + + public boolean matches(HttpServletRequest request) { + String uri = request.getRequestURI(); + int pathParamIndex = uri.indexOf(';'); + + if (pathParamIndex > 0) { + // strip everything after the first semi-colon + uri = uri.substring(0, pathParamIndex); + } + + if ("".equals(request.getContextPath())) { + // if any one of the URLs match, return true + for (String filterProcessesUrl : filterProcessesUrls) { + if (uri.endsWith(filterProcessesUrl)) { + return true; + } + } + return false; + } + + for (String filterProcessesUrl : filterProcessesUrls) { + if (uri.endsWith(request.getContextPath() + filterProcessesUrl)) { + return true; + } + } + + return false; + } + +}