From 42b93be492380fc0da5be839343ec9bd7a23d9d0 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Tue, 7 Jul 2015 17:14:42 -0400 Subject: [PATCH] added uri-encoded client service, closes #857 --- .../client/OIDCAuthenticationFilter.java | 7 +- .../impl/DefaultClientUserDetailsService.java | 53 +++++---- .../UriEncodedClientUserDetailsService.java | 104 ++++++++++++++++++ .../webapp/WEB-INF/application-context.xml | 1 + 4 files changed, 138 insertions(+), 27 deletions(-) create mode 100644 openid-connect-common/src/main/java/org/mitre/oauth2/service/impl/UriEncodedClientUserDetailsService.java diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java index 9070d91e9..fbbe0a7b5 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java @@ -58,6 +58,7 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriUtils; import com.google.common.base.Strings; import com.google.common.collect.Iterables; @@ -332,9 +333,9 @@ public class OIDCAuthenticationFilter extends AbstractAuthenticationProcessingFi protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException { ClientHttpRequest httpRequest = super.createRequest(url, method); httpRequest.getHeaders().add("Authorization", - String.format("Basic %s", Base64.encode(String.format("%s:%s", clientConfig.getClientId(), clientConfig.getClientSecret())) )); - - + String.format("Basic %s", Base64.encode(String.format("%s:%s", + UriUtils.encodePathSegment(clientConfig.getClientId(), "UTF-8"), + UriUtils.encodePathSegment(clientConfig.getClientSecret(), "UTF-8"))))); return httpRequest; } diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/service/impl/DefaultClientUserDetailsService.java b/openid-connect-common/src/main/java/org/mitre/oauth2/service/impl/DefaultClientUserDetailsService.java index b662b8caa..7cbbafb69 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/service/impl/DefaultClientUserDetailsService.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/service/impl/DefaultClientUserDetailsService.java @@ -31,6 +31,7 @@ import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.oauth2.common.exceptions.InvalidClientException; import org.springframework.stereotype.Service; import com.google.common.base.Strings; @@ -52,31 +53,35 @@ public class DefaultClientUserDetailsService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String clientId) throws UsernameNotFoundException { - ClientDetailsEntity client = clientDetailsService.loadClientByClientId(clientId); - - if (client != null) { - - String password = Strings.nullToEmpty(client.getClientSecret()); - - if (client.getTokenEndpointAuthMethod() != null && - (client.getTokenEndpointAuthMethod().equals(AuthMethod.PRIVATE_KEY) || - client.getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_JWT))) { - - // Issue a random password each time to prevent password auth from being used (or skipped) - // for private key or shared key clients, see #715 - - password = new BigInteger(512, new SecureRandom()).toString(16); + try { + ClientDetailsEntity client = clientDetailsService.loadClientByClientId(clientId); + + if (client != null) { + + String password = Strings.nullToEmpty(client.getClientSecret()); + + if (client.getTokenEndpointAuthMethod() != null && + (client.getTokenEndpointAuthMethod().equals(AuthMethod.PRIVATE_KEY) || + client.getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_JWT))) { + + // Issue a random password each time to prevent password auth from being used (or skipped) + // for private key or shared key clients, see #715 + + password = new BigInteger(512, new SecureRandom()).toString(16); + } + + boolean enabled = true; + boolean accountNonExpired = true; + boolean credentialsNonExpired = true; + boolean accountNonLocked = true; + Collection authorities = new HashSet<>(client.getAuthorities()); + authorities.add(ROLE_CLIENT); + + return new User(clientId, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); + } else { + throw new UsernameNotFoundException("Client not found: " + clientId); } - - boolean enabled = true; - boolean accountNonExpired = true; - boolean credentialsNonExpired = true; - boolean accountNonLocked = true; - Collection authorities = new HashSet<>(client.getAuthorities()); - authorities.add(ROLE_CLIENT); - - return new User(clientId, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); - } else { + } catch (InvalidClientException e) { throw new UsernameNotFoundException("Client not found: " + clientId); } diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/service/impl/UriEncodedClientUserDetailsService.java b/openid-connect-common/src/main/java/org/mitre/oauth2/service/impl/UriEncodedClientUserDetailsService.java new file mode 100644 index 000000000..ee14c5055 --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/service/impl/UriEncodedClientUserDetailsService.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * 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.oauth2.service.impl; + +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.Collection; +import java.util.HashSet; + +import org.mitre.oauth2.model.ClientDetailsEntity; +import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod; +import org.mitre.oauth2.service.ClientDetailsEntityService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.oauth2.common.exceptions.InvalidClientException; +import org.springframework.stereotype.Service; +import org.springframework.web.util.UriUtils; + +import com.google.common.base.Strings; + +/** + * Loads client details based on URI encoding as passed in from basic auth. + * + * Should only get called if non-encoded provider fails. + * + * @author AANGANES + * + */ +@Service("uriEncodedClientUserDetailsService") +public class UriEncodedClientUserDetailsService implements UserDetailsService { + + private static GrantedAuthority ROLE_CLIENT = new SimpleGrantedAuthority("ROLE_CLIENT"); + + @Autowired + private ClientDetailsEntityService clientDetailsService; + + @Override + public UserDetails loadUserByUsername(String clientId) throws UsernameNotFoundException { + + try { + String decodedClientId = UriUtils.decode(clientId, "UTF-8"); + + ClientDetailsEntity client = clientDetailsService.loadClientByClientId(decodedClientId); + + if (client != null) { + + String encodedPassword = UriUtils.encodeQueryParam(Strings.nullToEmpty(client.getClientSecret()), "UTF-8"); + + if (client.getTokenEndpointAuthMethod() != null && + (client.getTokenEndpointAuthMethod().equals(AuthMethod.PRIVATE_KEY) || + client.getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_JWT))) { + + // Issue a random password each time to prevent password auth from being used (or skipped) + // for private key or shared key clients, see #715 + + encodedPassword = new BigInteger(512, new SecureRandom()).toString(16); + } + + boolean enabled = true; + boolean accountNonExpired = true; + boolean credentialsNonExpired = true; + boolean accountNonLocked = true; + Collection authorities = new HashSet<>(client.getAuthorities()); + authorities.add(ROLE_CLIENT); + + return new User(decodedClientId, encodedPassword, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); + } else { + throw new UsernameNotFoundException("Client not found: " + clientId); + } + } catch (UnsupportedEncodingException | InvalidClientException e) { + throw new UsernameNotFoundException("Client not found: " + clientId); + } + + } + + public ClientDetailsEntityService getClientDetailsService() { + return clientDetailsService; + } + + public void setClientDetailsService(ClientDetailsEntityService clientDetailsService) { + this.clientDetailsService = clientDetailsService; + } + +} 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 ea1cf3fc8..dbdf1f7f4 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 @@ -188,6 +188,7 @@ +