refactored token introspection packages to support different client credentials at different services
parent
127507246e
commit
2201206f0e
|
@ -16,13 +16,25 @@
|
|||
******************************************************************************/
|
||||
package org.mitre.oauth2.introspectingfilter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.mitre.oauth2.introspectingfilter.service.IntrospectionAuthorityGranter;
|
||||
import org.mitre.oauth2.introspectingfilter.service.IntrospectionConfigurationService;
|
||||
import org.mitre.oauth2.introspectingfilter.service.impl.SimpleIntrospectionAuthorityGranter;
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
import org.mitre.openid.connect.client.service.ClientConfigurationService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.client.ClientHttpRequest;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
|
@ -39,6 +51,9 @@ import org.springframework.web.client.RestTemplate;
|
|||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.nimbusds.jose.util.Base64;
|
||||
|
||||
import static org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod.SECRET_BASIC;
|
||||
|
||||
/**
|
||||
* This ResourceServerTokenServices implementation introspects incoming tokens at a
|
||||
|
@ -48,12 +63,14 @@ import com.google.gson.JsonParser;
|
|||
*
|
||||
*/
|
||||
public class IntrospectingTokenService implements ResourceServerTokenServices {
|
||||
|
||||
private String clientId;
|
||||
private String clientSecret;
|
||||
private IntrospectionUrlProvider introspectionUrlProvider;
|
||||
|
||||
private ClientConfigurationService clientService;
|
||||
private IntrospectionConfigurationService introspectionConfigurationService;
|
||||
private IntrospectionAuthorityGranter introspectionAuthorityGranter = new SimpleIntrospectionAuthorityGranter();
|
||||
|
||||
private DefaultHttpClient httpClient = new DefaultHttpClient();
|
||||
private HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
|
||||
|
||||
// Inner class to store in the hash map
|
||||
private class TokenCacheObject {
|
||||
OAuth2AccessToken token;
|
||||
|
@ -66,35 +83,20 @@ public class IntrospectingTokenService implements ResourceServerTokenServices {
|
|||
}
|
||||
|
||||
private Map<String, TokenCacheObject> authCache = new HashMap<String, TokenCacheObject>();
|
||||
private static Logger logger = LoggerFactory.getLogger(IntrospectingTokenService.class);
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getClientSecret() {
|
||||
return clientSecret;
|
||||
}
|
||||
|
||||
public void setClientSecret(String clientSecret) {
|
||||
this.clientSecret = clientSecret;
|
||||
/**
|
||||
* @return the introspectionConfigurationService
|
||||
*/
|
||||
public IntrospectionConfigurationService getIntrospectionConfigurationService() {
|
||||
return introspectionConfigurationService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the introspectionUrlProvider
|
||||
* @param introspectionConfigurationService the introspectionConfigurationService to set
|
||||
*/
|
||||
public IntrospectionUrlProvider getIntrospectionUrlProvider() {
|
||||
return introspectionUrlProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param introspectionUrlProvider the introspectionUrlProvider to set
|
||||
*/
|
||||
public void setIntrospectionUrlProvider(IntrospectionUrlProvider introspectionUrlProvider) {
|
||||
this.introspectionUrlProvider = introspectionUrlProvider;
|
||||
public void setIntrospectionConfigurationService(IntrospectionConfigurationService introspectionUrlProvider) {
|
||||
this.introspectionConfigurationService = introspectionUrlProvider;
|
||||
}
|
||||
|
||||
// Check if there is a token and authentication in the cache
|
||||
|
@ -113,7 +115,7 @@ public class IntrospectingTokenService implements ResourceServerTokenServices {
|
|||
}
|
||||
|
||||
private OAuth2Request createStoredRequest(final JsonObject token) {
|
||||
clientId = token.get("client_id").getAsString();
|
||||
String clientId = token.get("client_id").getAsString();
|
||||
Set<String> scopes = new HashSet<String>();
|
||||
for (JsonElement e : token.get("scope").getAsJsonArray()) {
|
||||
scopes.add(e.getAsString());
|
||||
|
@ -140,22 +142,50 @@ public class IntrospectingTokenService implements ResourceServerTokenServices {
|
|||
private boolean parseToken(String accessToken) {
|
||||
|
||||
// find out which URL to ask
|
||||
String introspectionUrl = introspectionUrlProvider.getIntrospectionUrl(accessToken);
|
||||
|
||||
String introspectionUrl;
|
||||
RegisteredClient client;
|
||||
try {
|
||||
introspectionUrl = introspectionConfigurationService.getIntrospectionUrl(accessToken);
|
||||
client = introspectionConfigurationService.getClientConfiguration(accessToken);
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.error("Unable to load introspection URL or client configuration", e);
|
||||
return false;
|
||||
}
|
||||
// Use the SpringFramework RestTemplate to send the request to the
|
||||
// endpoint
|
||||
String validatedToken = null;
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
RestTemplate restTemplate;
|
||||
MultiValueMap<String, String> form = new LinkedMultiValueMap<String, String>();
|
||||
|
||||
final String clientId = client.getClientId();
|
||||
final String clientSecret = client.getClientSecret();
|
||||
|
||||
if (SECRET_BASIC.equals(client.getTokenEndpointAuthMethod())){
|
||||
// use BASIC auth if configured to do so
|
||||
restTemplate = new RestTemplate(factory) {
|
||||
|
||||
@Override
|
||||
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", clientId, clientSecret)) ));
|
||||
return httpRequest;
|
||||
}
|
||||
};
|
||||
} else { //Alternatively use form based auth
|
||||
restTemplate = new RestTemplate(factory);
|
||||
|
||||
form.add("client_id", clientId);
|
||||
form.add("client_secret", clientSecret);
|
||||
}
|
||||
|
||||
form.add("token", accessToken);
|
||||
form.add("client_id", this.clientId);
|
||||
form.add("client_secret", this.clientSecret);
|
||||
|
||||
try {
|
||||
validatedToken = restTemplate.postForObject(introspectionUrl, form, String.class);
|
||||
} catch (RestClientException rce) {
|
||||
// TODO: LOG THIS!?
|
||||
LoggerFactory.getLogger(IntrospectingTokenService.class).error("validateToken", rce);
|
||||
logger.error("validateToken", rce);
|
||||
}
|
||||
if (validatedToken != null) {
|
||||
// parse the json
|
||||
|
@ -168,11 +198,13 @@ public class IntrospectingTokenService implements ResourceServerTokenServices {
|
|||
|
||||
if (tokenResponse.get("error") != null) {
|
||||
// report an error?
|
||||
logger.error("Got an error back: " + tokenResponse.get("error") + ", " + tokenResponse.get("error_description"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tokenResponse.get("active").getAsBoolean()) {
|
||||
// non-valid token
|
||||
logger.info("Server returned non-active token");
|
||||
return false;
|
||||
}
|
||||
// create an OAuth2Authentication
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2013 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.introspectingfilter;
|
||||
|
||||
import java.text.ParseException;
|
||||
|
||||
import org.mitre.openid.connect.client.service.ServerConfigurationService;
|
||||
import org.mitre.openid.connect.config.ServerConfiguration;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.nimbusds.jwt.JWT;
|
||||
import com.nimbusds.jwt.JWTParser;
|
||||
|
||||
/**
|
||||
*
|
||||
* Parses the incoming accesstoken as a JWT and determines the issuer based on
|
||||
* the "iss" field inside the JWT. Uses the ServerConfigurationService to determine
|
||||
* the introspection URL for that issuer.
|
||||
*
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class JWTParsingIntrospectionUrlProvider implements IntrospectionUrlProvider {
|
||||
|
||||
private ServerConfigurationService serverConfigurationService;
|
||||
|
||||
/**
|
||||
* @return the serverConfigurationService
|
||||
*/
|
||||
public ServerConfigurationService getServerConfigurationService() {
|
||||
return serverConfigurationService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param serverConfigurationService the serverConfigurationService to set
|
||||
*/
|
||||
public void setServerConfigurationService(ServerConfigurationService serverConfigurationService) {
|
||||
this.serverConfigurationService = serverConfigurationService;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.oauth2.introspectingfilter.IntrospectionUrlProvider#getIntrospectionUrl(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public String getIntrospectionUrl(String accessToken) {
|
||||
|
||||
try {
|
||||
JWT jwt = JWTParser.parse(accessToken);
|
||||
|
||||
String issuer = jwt.getJWTClaimsSet().getIssuer();
|
||||
if (!Strings.isNullOrEmpty(issuer)) {
|
||||
|
||||
|
||||
|
||||
ServerConfiguration server = serverConfigurationService.getServerConfiguration(issuer);
|
||||
if (server != null) {
|
||||
if (!Strings.isNullOrEmpty(server.getIntrospectionEndpointUri())) {
|
||||
return server.getIntrospectionEndpointUri();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Server does not have Introspection Endpoint defined");
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Could not find server configuration for issuer " + issuer);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("No issuer claim found in JWT");
|
||||
}
|
||||
|
||||
} catch (ParseException e) {
|
||||
throw new IllegalArgumentException("Unable to parse JWT", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -17,7 +17,7 @@
|
|||
/**
|
||||
*
|
||||
*/
|
||||
package org.mitre.oauth2.introspectingfilter;
|
||||
package org.mitre.oauth2.introspectingfilter.service;
|
||||
|
||||
import java.util.List;
|
||||
|
|
@ -17,13 +17,15 @@
|
|||
/**
|
||||
*
|
||||
*/
|
||||
package org.mitre.oauth2.introspectingfilter;
|
||||
package org.mitre.oauth2.introspectingfilter.service;
|
||||
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public interface IntrospectionUrlProvider {
|
||||
public interface IntrospectionConfigurationService {
|
||||
|
||||
/**
|
||||
* Get the introspection URL based on the access token.
|
||||
|
@ -32,4 +34,13 @@ public interface IntrospectionUrlProvider {
|
|||
*/
|
||||
public String getIntrospectionUrl(String accessToken);
|
||||
|
||||
|
||||
/**
|
||||
* Get the client configuration to use to connect to the
|
||||
* introspection endpoint. In particular, this cares about
|
||||
* the clientId, clientSecret, and tokenEndpointAuthMethod
|
||||
* fields.
|
||||
*/
|
||||
public RegisteredClient getClientConfiguration(String accessToken);
|
||||
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2013 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.introspectingfilter.service.impl;
|
||||
|
||||
import java.text.ParseException;
|
||||
|
||||
import org.mitre.oauth2.introspectingfilter.service.IntrospectionConfigurationService;
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
import org.mitre.openid.connect.client.service.ClientConfigurationService;
|
||||
import org.mitre.openid.connect.client.service.ServerConfigurationService;
|
||||
import org.mitre.openid.connect.config.ServerConfiguration;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.nimbusds.jwt.JWT;
|
||||
import com.nimbusds.jwt.JWTParser;
|
||||
|
||||
/**
|
||||
*
|
||||
* Parses the incoming accesstoken as a JWT and determines the issuer based on
|
||||
* the "iss" field inside the JWT. Uses the ServerConfigurationService to determine
|
||||
* the introspection URL for that issuer.
|
||||
*
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class JWTParsingIntrospectionConfigurationService implements IntrospectionConfigurationService {
|
||||
|
||||
private ServerConfigurationService serverConfigurationService;
|
||||
private ClientConfigurationService clientConfigurationService;
|
||||
|
||||
/**
|
||||
* @return the serverConfigurationService
|
||||
*/
|
||||
public ServerConfigurationService getServerConfigurationService() {
|
||||
return serverConfigurationService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param serverConfigurationService the serverConfigurationService to set
|
||||
*/
|
||||
public void setServerConfigurationService(ServerConfigurationService serverConfigurationService) {
|
||||
this.serverConfigurationService = serverConfigurationService;
|
||||
}
|
||||
|
||||
|
||||
private String getIssuer(String accessToken) {
|
||||
try {
|
||||
JWT jwt = JWTParser.parse(accessToken);
|
||||
|
||||
String issuer = jwt.getJWTClaimsSet().getIssuer();
|
||||
|
||||
return issuer;
|
||||
|
||||
} catch (ParseException e) {
|
||||
throw new IllegalArgumentException("Unable to parse JWT", e);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.oauth2.introspectingfilter.IntrospectionConfigurationService#getIntrospectionUrl(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public String getIntrospectionUrl(String accessToken) {
|
||||
String issuer = getIssuer(accessToken);
|
||||
if (!Strings.isNullOrEmpty(issuer)) {
|
||||
ServerConfiguration server = serverConfigurationService.getServerConfiguration(issuer);
|
||||
if (server != null) {
|
||||
if (!Strings.isNullOrEmpty(server.getIntrospectionEndpointUri())) {
|
||||
return server.getIntrospectionEndpointUri();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Server does not have Introspection Endpoint defined");
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Could not find server configuration for issuer " + issuer);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("No issuer claim found in JWT");
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.oauth2.introspectingfilter.service.IntrospectionConfigurationService#getClientConfiguration(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public RegisteredClient getClientConfiguration(String accessToken) {
|
||||
|
||||
String issuer = getIssuer(accessToken);
|
||||
if (!Strings.isNullOrEmpty(issuer)) {
|
||||
ServerConfiguration server = serverConfigurationService.getServerConfiguration(issuer);
|
||||
if (server != null) {
|
||||
RegisteredClient client = clientConfigurationService.getClientConfiguration(server);
|
||||
if (client != null) {
|
||||
return client;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Could not find client configuration for issuer " + issuer);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Could not find server configuration for issuer " + issuer);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("No issuer claim found in JWT");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -17,10 +17,11 @@
|
|||
/**
|
||||
*
|
||||
*/
|
||||
package org.mitre.oauth2.introspectingfilter;
|
||||
package org.mitre.oauth2.introspectingfilter.service.impl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.mitre.oauth2.introspectingfilter.service.IntrospectionAuthorityGranter;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
|
|
@ -17,19 +17,37 @@
|
|||
/**
|
||||
*
|
||||
*/
|
||||
package org.mitre.oauth2.introspectingfilter;
|
||||
package org.mitre.oauth2.introspectingfilter.service.impl;
|
||||
|
||||
import org.mitre.oauth2.introspectingfilter.service.IntrospectionConfigurationService;
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
|
||||
/**
|
||||
*
|
||||
* Always provides the (configured) IntrospectionURL regardless of token. Useful for talking to
|
||||
* a single, trusted authorization server.
|
||||
* Always provides the (configured) IntrospectionURL and RegisteredClient regardless
|
||||
* of token. Useful for talking to a single, trusted authorization server.
|
||||
*
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class StaticIntrospectionUrlProvider implements IntrospectionUrlProvider {
|
||||
public class StaticIntrospectionConfigurationService implements IntrospectionConfigurationService {
|
||||
|
||||
private String introspectionUrl;
|
||||
private RegisteredClient clientConfiguration;
|
||||
|
||||
/**
|
||||
* @return the clientConfiguration
|
||||
*/
|
||||
public RegisteredClient getClientConfiguration() {
|
||||
return clientConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clientConfiguration the clientConfiguration to set
|
||||
*/
|
||||
public void setClientConfiguration(RegisteredClient client) {
|
||||
this.clientConfiguration = client;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the introspectionUrl
|
||||
|
@ -46,11 +64,19 @@ public class StaticIntrospectionUrlProvider implements IntrospectionUrlProvider
|
|||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.oauth2.introspectingfilter.IntrospectionUrlProvider#getIntrospectionUrl(java.lang.String)
|
||||
* @see org.mitre.oauth2.introspectingfilter.IntrospectionConfigurationService#getIntrospectionUrl(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public String getIntrospectionUrl(String accessToken) {
|
||||
return getIntrospectionUrl();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.oauth2.introspectingfilter.service.IntrospectionConfigurationService#getClientConfiguration(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public RegisteredClient getClientConfiguration(String accessToken) {
|
||||
return getClientConfiguration();
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue