diff --git a/gms/src/main/java/it/inaf/ia2/gms/authn/GetTokenDataService.java b/gms/src/main/java/it/inaf/ia2/gms/authn/GetTokenDataService.java new file mode 100644 index 0000000000000000000000000000000000000000..3843b7a68a30e2c308e1146e04a84bef6ad118c6 --- /dev/null +++ b/gms/src/main/java/it/inaf/ia2/gms/authn/GetTokenDataService.java @@ -0,0 +1,70 @@ +package it.inaf.ia2.gms.authn; + +import java.util.Map; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.token.AccessTokenConverter; +import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +/** + * Retrieve token data from the user info / check token endpoint using the + * returned access token. + */ +public class GetTokenDataService implements ResourceServerTokenServices { + + private final RestOperations restTemplate = new RestTemplate(); + + private String checkTokenEndpointUrl; + + private String clientId; + + private AccessTokenConverter tokenConverter; + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public void setCheckTokenEndpointUrl(String checkTokenEndpointUrl) { + this.checkTokenEndpointUrl = checkTokenEndpointUrl; + } + + public void setAccessTokenConverter(AccessTokenConverter accessTokenConverter) { + this.tokenConverter = accessTokenConverter; + } + + @Override + public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException { + + MultiValueMap<String, String> formData = new LinkedMultiValueMap<>(); + formData.add("client_id", clientId); + + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", "Bearer " + accessToken); + Map<String, Object> map = postForMap(checkTokenEndpointUrl, formData, headers); + + return this.tokenConverter.extractAuthentication(map); + } + + private Map<String, Object> postForMap(String path, MultiValueMap<String, String> formData, HttpHeaders headers) { + if (headers.getContentType() == null) { + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + } + return restTemplate.exchange(path, HttpMethod.POST, + new HttpEntity<>(formData, headers), Map.class).getBody(); + } + + @Override + public OAuth2AccessToken readAccessToken(String accessToken) { + throw new UnsupportedOperationException("Not supported: read access token"); + } +} diff --git a/gms/src/main/java/it/inaf/ia2/gms/authn/OAuth2Config.java b/gms/src/main/java/it/inaf/ia2/gms/authn/OAuth2Config.java index 53e7dc276d0773ed2fe4008f061dc2e5f001c361..756deb6785a6b8227f3974282994f3566e51e635 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/authn/OAuth2Config.java +++ b/gms/src/main/java/it/inaf/ia2/gms/authn/OAuth2Config.java @@ -14,7 +14,7 @@ import org.springframework.security.oauth2.config.annotation.web.configuration.A import org.springframework.security.oauth2.provider.ClientDetailsService; import org.springframework.security.oauth2.provider.client.InMemoryClientDetailsService; import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter; -import org.springframework.security.oauth2.provider.token.RemoteTokenServices; +import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; import org.springframework.security.oauth2.provider.token.store.jwk.JwkTokenStore; import org.springframework.web.client.RestTemplate; @@ -31,12 +31,9 @@ public class OAuth2Config extends AuthorizationServerEndpointsConfiguration { @Value("${security.oauth2.client.client-id}") private String clientId; - @Value("${security.oauth2.client.client-secret}") - private String clientSecret; - @Bean - public RemoteTokenServices resourceServerTokenServices(JwkTokenStore jwkTokenStore) { - RemoteTokenServices tokenService = new RemoteTokenServices(); + public ResourceServerTokenServices resourceServerTokenServices(JwkTokenStore jwkTokenStore) { + GetTokenDataService tokenService = new GetTokenDataService(); DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter(); accessTokenConverter.setUserTokenConverter(new CustomIdTokenConverter(jwkTokenStore)); @@ -44,7 +41,6 @@ public class OAuth2Config extends AuthorizationServerEndpointsConfiguration { tokenService.setCheckTokenEndpointUrl(checkTokenEndpointUrl); tokenService.setClientId(clientId); - tokenService.setClientSecret(clientSecret); return tokenService; } diff --git a/gms/src/main/java/it/inaf/ia2/gms/rap/RapClient.java b/gms/src/main/java/it/inaf/ia2/gms/rap/RapClient.java index d73ab74db97d31dba85a2b83de18bac31844d707..48a465c2afb7d1c773afb77447386893d209d852 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/rap/RapClient.java +++ b/gms/src/main/java/it/inaf/ia2/gms/rap/RapClient.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Function; +import org.apache.commons.codec.binary.Base64; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.ParameterizedTypeReference; @@ -40,6 +41,10 @@ public class RapClient { @Value("${security.oauth2.client.scope}") private String scope; + /* Use basic auth instead of JWT when asking for users */ + @Value("${rap.ws.basic-auth}") + private boolean basicAuth; + private final SessionData sessionData; private final RestTemplate rapRestTemplate; @@ -99,7 +104,13 @@ public class RapClient { HttpHeaders headers = new HttpHeaders(); headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); - headers.add("Authorization", "Bearer " + sessionData.getAccessToken()); + if (basicAuth) { + String auth = clientId + ":" + clientSecret; + String encodedAuth = Base64.encodeBase64String(auth.getBytes()); + headers.add("Authorization", "Basic " + encodedAuth); + } else { + headers.add("Authorization", "Bearer " + sessionData.getAccessToken()); + } return new HttpEntity<>(body, headers); } diff --git a/gms/src/main/resources/application.properties b/gms/src/main/resources/application.properties index 1bb740b6c30e8a89172d700379d7fccaf1514431..a4dec9aa458982bcbf9c04922ca172023598d1d7 100644 --- a/gms/src/main/resources/application.properties +++ b/gms/src/main/resources/application.properties @@ -11,12 +11,14 @@ security.oauth2.resource.jwk.key-set-uri=http://localhost/rap-ia2/auth/oidc/jwks logging.level.org.springframework.security=DEBUG logging.level.org.springframework.jdbc=TRACE +logging.level.org.springframework.web=TRACE spring.datasource.url=jdbc:postgresql://localhost:5432/postgres spring.datasource.username=gms spring.datasource.password=gms rap.ws-url=http://localhost/rap-ia2/ws +rap.ws.basic-auth=false # For development only: spring.profiles.active=dev