added signed authentication assertion support to client, closes #558
parent
b67121f0cd
commit
3629ff5e2f
|
@ -16,12 +16,14 @@
|
|||
******************************************************************************/
|
||||
package org.mitre.openid.connect.client;
|
||||
|
||||
import static org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod.SECRET_BASIC;
|
||||
import static org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URI;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
@ -35,7 +37,9 @@ import javax.servlet.http.HttpSession;
|
|||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.impl.client.SystemDefaultHttpClient;
|
||||
import org.mitre.jwt.signer.service.JwtSigningAndValidationService;
|
||||
import org.mitre.jwt.signer.service.impl.DefaultJwtSigningAndValidationService;
|
||||
import org.mitre.jwt.signer.service.impl.JWKSetCacheService;
|
||||
import org.mitre.oauth2.model.ClientDetailsEntity;
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
import org.mitre.openid.connect.client.model.IssuerServiceResponse;
|
||||
import org.mitre.openid.connect.client.service.AuthRequestOptionsService;
|
||||
|
@ -61,10 +65,19 @@ import org.springframework.web.client.HttpClientErrorException;
|
|||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.nimbusds.jose.JWSAlgorithm;
|
||||
import com.nimbusds.jose.JWSHeader;
|
||||
import com.nimbusds.jose.jwk.JWK;
|
||||
import com.nimbusds.jose.jwk.OctetSequenceKey;
|
||||
import com.nimbusds.jose.jwk.Use;
|
||||
import com.nimbusds.jose.util.Base64;
|
||||
import com.nimbusds.jose.util.Base64URL;
|
||||
import com.nimbusds.jwt.JWTClaimsSet;
|
||||
import com.nimbusds.jwt.ReadOnlyJWTClaimsSet;
|
||||
import com.nimbusds.jwt.SignedJWT;
|
||||
|
||||
|
@ -90,6 +103,9 @@ public class OIDCAuthenticationFilter extends AbstractAuthenticationProcessingFi
|
|||
|
||||
@Autowired
|
||||
private JWKSetCacheService validationServices;
|
||||
|
||||
@Autowired(required=false)
|
||||
private JwtSigningAndValidationService authenticationSignerService;
|
||||
|
||||
// modular services to build out client filter
|
||||
private ServerConfigurationService servers;
|
||||
|
@ -291,11 +307,61 @@ public class OIDCAuthenticationFilter extends AbstractAuthenticationProcessingFi
|
|||
return httpRequest;
|
||||
}
|
||||
};
|
||||
} else { //Alternatively use form based auth
|
||||
} else {
|
||||
// we're not doing basic auth, figure out what other flavor we have
|
||||
restTemplate = new RestTemplate(factory);
|
||||
|
||||
form.add("client_id", clientConfig.getClientId());
|
||||
form.add("client_secret", clientConfig.getClientSecret());
|
||||
if (SECRET_JWT.equals(clientConfig.getTokenEndpointAuthMethod()) || PRIVATE_KEY.equals(clientConfig.getTokenEndpointAuthMethod())) {
|
||||
// do a symmetric secret signed JWT for auth
|
||||
|
||||
|
||||
JwtSigningAndValidationService signer = null;
|
||||
JWSAlgorithm alg = clientConfig.getTokenEndpointAuthSigningAlg();
|
||||
|
||||
if (SECRET_JWT.equals(clientConfig.getTokenEndpointAuthMethod()) &&
|
||||
(alg.equals(JWSAlgorithm.HS256)
|
||||
|| alg.equals(JWSAlgorithm.HS384)
|
||||
|| alg.equals(JWSAlgorithm.HS512))) {
|
||||
|
||||
// generate one based on client secret
|
||||
signer = getSymmetricValidtor(clientConfig.getClient());
|
||||
|
||||
} else if (PRIVATE_KEY.equals(clientConfig.getTokenEndpointAuthMethod())) {
|
||||
|
||||
// needs to be wired in to the bean
|
||||
signer = authenticationSignerService;
|
||||
}
|
||||
|
||||
if (signer == null) {
|
||||
throw new AuthenticationServiceException("Couldn't find required signer service for use with private key auth.");
|
||||
}
|
||||
|
||||
JWTClaimsSet claimsSet = new JWTClaimsSet();
|
||||
|
||||
claimsSet.setIssuer(clientConfig.getClientId());
|
||||
claimsSet.setSubject(clientConfig.getClientId());
|
||||
claimsSet.setAudience(Lists.newArrayList(serverConfig.getIssuer()));
|
||||
|
||||
// TODO: make this configurable
|
||||
Date exp = new Date(System.currentTimeMillis() + (60 * 1000)); // auth good for 60 seconds
|
||||
claimsSet.setExpirationTime(exp);
|
||||
|
||||
Date now = new Date(System.currentTimeMillis());
|
||||
claimsSet.setIssueTime(now);
|
||||
claimsSet.setNotBeforeTime(now);
|
||||
|
||||
SignedJWT jwt = new SignedJWT(new JWSHeader(alg), claimsSet);
|
||||
|
||||
signer.signJwt(jwt, alg);
|
||||
|
||||
form.add("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer");
|
||||
form.add("client_assertion", jwt.serialize());
|
||||
} else {
|
||||
//Alternatively use form based auth
|
||||
form.add("client_id", clientConfig.getClientId());
|
||||
form.add("client_secret", clientConfig.getClientSecret());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
logger.debug("tokenEndpointURI = " + serverConfig.getTokenEndpointUri());
|
||||
|
@ -678,4 +744,41 @@ public class OIDCAuthenticationFilter extends AbstractAuthenticationProcessingFi
|
|||
this.authOptions = authOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a symmetric signing and validation service for the given client
|
||||
*
|
||||
* @param client
|
||||
* @return
|
||||
*/
|
||||
private JwtSigningAndValidationService getSymmetricValidtor(ClientDetailsEntity client) {
|
||||
|
||||
// TODO: cache
|
||||
|
||||
if (client == null) {
|
||||
logger.error("Couldn't create symmetric validator for null client");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Strings.isNullOrEmpty(client.getClientSecret())) {
|
||||
logger.error("Couldn't create symmetric validator for client " + client.getClientId() + " without a client secret");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
JWK jwk = new OctetSequenceKey(Base64URL.encode(client.getClientSecret()), Use.SIGNATURE, null, client.getClientId(), null, null, null);
|
||||
Map<String, JWK> keys = ImmutableMap.of(client.getClientId(), jwk);
|
||||
JwtSigningAndValidationService service = new DefaultJwtSigningAndValidationService(keys);
|
||||
|
||||
return service;
|
||||
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
logger.error("Couldn't create symmetric validator for client " + client.getClientId(), e);
|
||||
} catch (InvalidKeySpecException e) {
|
||||
logger.error("Couldn't create symmetric validator for client " + client.getClientId(), e);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue