001/* 002 * Copyright 2012-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.autoconfigure.security.oauth2.client; 018 019import java.util.HashMap; 020import java.util.Map; 021 022import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties.Provider; 023import org.springframework.boot.context.properties.PropertyMapper; 024import org.springframework.boot.convert.ApplicationConversionService; 025import org.springframework.core.convert.ConversionException; 026import org.springframework.security.config.oauth2.client.CommonOAuth2Provider; 027import org.springframework.security.oauth2.client.registration.ClientRegistration; 028import org.springframework.security.oauth2.client.registration.ClientRegistration.Builder; 029import org.springframework.security.oauth2.client.registration.ClientRegistrations; 030import org.springframework.security.oauth2.core.AuthenticationMethod; 031import org.springframework.security.oauth2.core.AuthorizationGrantType; 032import org.springframework.security.oauth2.core.ClientAuthenticationMethod; 033import org.springframework.util.StringUtils; 034 035/** 036 * Adapter class to convert {@link OAuth2ClientProperties} to a 037 * {@link ClientRegistration}. 038 * 039 * @author Phillip Webb 040 * @author Thiago Hirata 041 * @author Madhura Bhave 042 * @author MyeongHyeon Lee 043 * @since 2.1.0 044 */ 045public final class OAuth2ClientPropertiesRegistrationAdapter { 046 047 private OAuth2ClientPropertiesRegistrationAdapter() { 048 } 049 050 public static Map<String, ClientRegistration> getClientRegistrations( 051 OAuth2ClientProperties properties) { 052 Map<String, ClientRegistration> clientRegistrations = new HashMap<>(); 053 properties.getRegistration().forEach((key, value) -> clientRegistrations.put(key, 054 getClientRegistration(key, value, properties.getProvider()))); 055 return clientRegistrations; 056 } 057 058 private static ClientRegistration getClientRegistration(String registrationId, 059 OAuth2ClientProperties.Registration properties, 060 Map<String, Provider> providers) { 061 Builder builder = getBuilderFromIssuerIfPossible(registrationId, 062 properties.getProvider(), providers); 063 if (builder == null) { 064 builder = getBuilder(registrationId, properties.getProvider(), providers); 065 } 066 PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); 067 map.from(properties::getClientId).to(builder::clientId); 068 map.from(properties::getClientSecret).to(builder::clientSecret); 069 map.from(properties::getClientAuthenticationMethod) 070 .as(ClientAuthenticationMethod::new) 071 .to(builder::clientAuthenticationMethod); 072 map.from(properties::getAuthorizationGrantType).as(AuthorizationGrantType::new) 073 .to(builder::authorizationGrantType); 074 map.from(properties::getRedirectUri).to(builder::redirectUriTemplate); 075 map.from(properties::getScope).as((scope) -> StringUtils.toStringArray(scope)) 076 .to(builder::scope); 077 map.from(properties::getClientName).to(builder::clientName); 078 return builder.build(); 079 } 080 081 private static Builder getBuilderFromIssuerIfPossible(String registrationId, 082 String configuredProviderId, Map<String, Provider> providers) { 083 String providerId = (configuredProviderId != null) ? configuredProviderId 084 : registrationId; 085 if (providers.containsKey(providerId)) { 086 Provider provider = providers.get(providerId); 087 String issuer = provider.getIssuerUri(); 088 if (issuer != null) { 089 String cleanedIssuer = cleanIssuerPath(issuer); 090 Builder builder = ClientRegistrations 091 .fromOidcIssuerLocation(cleanedIssuer) 092 .registrationId(registrationId); 093 return getBuilder(builder, provider); 094 } 095 } 096 return null; 097 } 098 099 private static String cleanIssuerPath(String issuer) { 100 if (issuer.endsWith("/")) { 101 return issuer.substring(0, issuer.length() - 1); 102 } 103 return issuer; 104 } 105 106 private static Builder getBuilder(String registrationId, String configuredProviderId, 107 Map<String, Provider> providers) { 108 String providerId = (configuredProviderId != null) ? configuredProviderId 109 : registrationId; 110 CommonOAuth2Provider provider = getCommonProvider(providerId); 111 if (provider == null && !providers.containsKey(providerId)) { 112 throw new IllegalStateException( 113 getErrorMessage(configuredProviderId, registrationId)); 114 } 115 Builder builder = (provider != null) ? provider.getBuilder(registrationId) 116 : ClientRegistration.withRegistrationId(registrationId); 117 if (providers.containsKey(providerId)) { 118 return getBuilder(builder, providers.get(providerId)); 119 } 120 return builder; 121 } 122 123 private static String getErrorMessage(String configuredProviderId, 124 String registrationId) { 125 return ((configuredProviderId != null) 126 ? "Unknown provider ID '" + configuredProviderId + "'" 127 : "Provider ID must be specified for client registration '" 128 + registrationId + "'"); 129 } 130 131 private static Builder getBuilder(Builder builder, Provider provider) { 132 PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); 133 map.from(provider::getAuthorizationUri).to(builder::authorizationUri); 134 map.from(provider::getTokenUri).to(builder::tokenUri); 135 map.from(provider::getUserInfoUri).to(builder::userInfoUri); 136 map.from(provider::getUserInfoAuthenticationMethod).as(AuthenticationMethod::new) 137 .to(builder::userInfoAuthenticationMethod); 138 map.from(provider::getJwkSetUri).to(builder::jwkSetUri); 139 map.from(provider::getUserNameAttribute).to(builder::userNameAttributeName); 140 return builder; 141 } 142 143 private static CommonOAuth2Provider getCommonProvider(String providerId) { 144 try { 145 return ApplicationConversionService.getSharedInstance().convert(providerId, 146 CommonOAuth2Provider.class); 147 } 148 catch (ConversionException ex) { 149 return null; 150 } 151 } 152 153}