converted bearer assertion framework to nimbus-jose

pull/306/merge
Justin Richer 2013-02-19 14:23:07 -05:00
parent 910a6cf1a0
commit c7d1b47b38
3 changed files with 46 additions and 44 deletions

View File

@ -5,10 +5,11 @@ package org.mitre.openid.connect.assertion;
import java.util.Collection; import java.util.Collection;
import org.mitre.jwt.model.Jwt;
import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import com.nimbusds.jwt.JWT;
/** /**
* @author jricher * @author jricher
* *
@ -16,16 +17,27 @@ import org.springframework.security.core.GrantedAuthority;
public class JwtBearerAssertionAuthenticationToken extends AbstractAuthenticationToken { public class JwtBearerAssertionAuthenticationToken extends AbstractAuthenticationToken {
private String clientId; private String clientId;
private Jwt jwt; private JWT jwt;
public JwtBearerAssertionAuthenticationToken(String clientId, Jwt jwt) { /**
* Create an unauthenticated token with the given client ID and jwt
* @param clientId
* @param jwt
*/
public JwtBearerAssertionAuthenticationToken(String clientId, JWT jwt) {
super(null); super(null);
this.clientId = clientId; this.clientId = clientId;
this.jwt = jwt; this.jwt = jwt;
setAuthenticated(false); setAuthenticated(false);
} }
public JwtBearerAssertionAuthenticationToken(String clientId, Jwt jwt, Collection<? extends GrantedAuthority> authorities) { /**
* Create an authenticated token with the given clientID, jwt, and authorities set
* @param clientId
* @param jwt
* @param authorities
*/
public JwtBearerAssertionAuthenticationToken(String clientId, JWT jwt, Collection<? extends GrantedAuthority> authorities) {
super(authorities); super(authorities);
this.clientId = clientId; this.clientId = clientId;
this.jwt = jwt; this.jwt = jwt;
@ -65,14 +77,14 @@ public class JwtBearerAssertionAuthenticationToken extends AbstractAuthenticatio
/** /**
* @return the jwt * @return the jwt
*/ */
public Jwt getJwt() { public JWT getJwt() {
return jwt; return jwt;
} }
/** /**
* @param jwt the jwt to set * @param jwt the jwt to set
*/ */
public void setJwt(Jwt jwt) { public void setJwt(JWT jwt) {
this.jwt = jwt; this.jwt = jwt;
} }

View File

@ -3,39 +3,25 @@
*/ */
package org.mitre.openid.connect.assertion; package org.mitre.openid.connect.assertion;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.interfaces.RSAPublicKey; import java.security.interfaces.RSAPublicKey;
import java.text.ParseException; import java.text.ParseException;
import java.util.Date; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.mitre.jwt.model.Jwt;
import org.mitre.jwt.model.JwtClaims;
import org.mitre.jwt.model.JwtHeader;
import org.mitre.jwt.signer.JwsAlgorithm;
import org.mitre.jwt.signer.JwtSigner;
import org.mitre.jwt.signer.impl.RsaSigner;
import org.mitre.jwt.signer.service.JwtSigningAndValidationService;
import org.mitre.jwt.signer.service.impl.DefaultJwtSigningAndValidationService;
import org.mitre.key.fetch.KeyFetcher; import org.mitre.key.fetch.KeyFetcher;
import org.mitre.oauth2.exception.ClientNotFoundException; import org.mitre.oauth2.exception.ClientNotFoundException;
import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.ClientDetailsEntity;
import org.mitre.oauth2.service.ClientDetailsEntityService; import org.mitre.oauth2.service.ClientDetailsEntityService;
import org.mitre.openid.connect.config.ConfigurationPropertiesBean; import org.mitre.openid.connect.config.ConfigurationPropertiesBean;
import org.mitre.openid.connect.config.OIDCServerConfiguration;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.core.userdetails.UsernameNotFoundException;
import com.nimbusds.jose.JOSEException; import com.nimbusds.jose.JOSEException;
@ -43,7 +29,7 @@ import com.nimbusds.jose.JWSObject;
import com.nimbusds.jose.JWSVerifier; import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.RSASSAVerifier; import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jwt.JWT; import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTParser; import com.nimbusds.jwt.ReadOnlyJWTClaimsSet;
/** /**
* @author jricher * @author jricher
@ -79,8 +65,8 @@ public class JwtBearerAuthenticationProvider implements AuthenticationProvider {
try { try {
ClientDetailsEntity client = clientService.loadClientByClientId(jwtAuth.getClientId()); ClientDetailsEntity client = clientService.loadClientByClientId(jwtAuth.getClientId());
Jwt jwt = jwtAuth.getJwt(); JWT jwt = jwtAuth.getJwt();
JwtClaims jwtClaims = jwt.getClaims(); ReadOnlyJWTClaimsSet jwtClaims = jwt.getJWTClaimsSet();
// check the signature with nimbus // check the signature with nimbus
JWSVerifier verifier = getVerifierForClient(client); JWSVerifier verifier = getVerifierForClient(client);
@ -90,20 +76,22 @@ public class JwtBearerAuthenticationProvider implements AuthenticationProvider {
} }
// check the issuer // check the issuer
if (jwtClaims.getIssuer() == null) { if (jwtClaims.getIssuerClaim() == null) {
throw new AuthenticationServiceException("Assertion Token Issuer is null"); throw new AuthenticationServiceException("Assertion Token Issuer is null");
} else if (!jwtClaims.getIssuer().equals(client.getClientId())){ } else if (!jwtClaims.getIssuerClaim().equals(client.getClientId())){
throw new AuthenticationServiceException("Issuers do not match, expected " + client.getClientId() + " got " + jwtClaims.getIssuer()); throw new AuthenticationServiceException("Issuers do not match, expected " + client.getClientId() + " got " + jwtClaims.getIssuerClaim());
} }
// check expiration // check expiration
if (jwtClaims.getExpiration() == null) { /*
* FIXME: re-institute date check for Nimbus
if (jwtClaims.getExpirationTimeClaim() == null) {
throw new AuthenticationServiceException("Assertion Token does not have required expiration claim"); throw new AuthenticationServiceException("Assertion Token does not have required expiration claim");
} else { } else {
// it's not null, see if it's expired // it's not null, see if it's expired
Date now = new Date(System.currentTimeMillis() - (timeSkewAllowance * 1000)); Date now = new Date(System.currentTimeMillis() - (timeSkewAllowance * 1000));
if (now.after(jwtClaims.getExpiration())) { if (now.after(jwtClaims.getExpirationTimeClaim())) {
throw new AuthenticationServiceException("Assertion Token is expired: " + jwtClaims.getExpiration()); throw new AuthenticationServiceException("Assertion Token is expired: " + jwtClaims.getExpirationTimeClaim());
} }
} }
@ -115,13 +103,6 @@ public class JwtBearerAuthenticationProvider implements AuthenticationProvider {
} }
} }
// check audience
if (jwtClaims.getAudience() == null) {
throw new AuthenticationServiceException("Assertion token audience is null");
} else if (!jwtClaims.getAudience().contains(config.getIssuer())) {
throw new AuthenticationServiceException("Audience does not match, expected " + config.getIssuer() + " got " + jwtClaims.getAudience());
}
// check issued at // check issued at
if (jwtClaims.getIssuedAt() != null) { if (jwtClaims.getIssuedAt() != null) {
// since it's not null, see if it was issued in the future // since it's not null, see if it was issued in the future
@ -130,7 +111,16 @@ public class JwtBearerAuthenticationProvider implements AuthenticationProvider {
throw new AuthenticationServiceException("Assertion Token was issued in the future: " + jwtClaims.getIssuedAt()); throw new AuthenticationServiceException("Assertion Token was issued in the future: " + jwtClaims.getIssuedAt());
} }
} }
*/
// check audience
if (jwtClaims.getAudienceClaim() == null) {
throw new AuthenticationServiceException("Assertion token audience is null");
} else if (!Arrays.asList(jwtClaims.getAudienceClaim()).contains(config.getIssuer())) { // FIXME: change back to list.contains() check after Nimbus update
throw new AuthenticationServiceException("Audience does not match, expected " + config.getIssuer() + " got " + jwtClaims.getAudienceClaim());
}
// IFF we managed to get all the way down here, the token is valid // IFF we managed to get all the way down here, the token is valid
return new JwtBearerAssertionAuthenticationToken(client.getClientId(), jwt, client.getAuthorities()); return new JwtBearerAssertionAuthenticationToken(client.getClientId(), jwt, client.getAuthorities());

View File

@ -4,20 +4,20 @@
package org.mitre.openid.connect.assertion; package org.mitre.openid.connect.assertion;
import java.io.IOException; import java.io.IOException;
import java.text.ParseException;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.mitre.jwt.model.Jwt;
import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter; import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTParser;
/** /**
* Filter to check client authentication via JWT Bearer assertions. * Filter to check client authentication via JWT Bearer assertions.
@ -48,14 +48,14 @@ public class JwtBearerClientAssertionTokenEndpointFilter extends ClientCredentia
String assertion = request.getParameter("client_assertion"); String assertion = request.getParameter("client_assertion");
try { try {
Jwt jwt = Jwt.parse(assertion); JWT jwt = JWTParser.parse(assertion);
String clientId = jwt.getClaims().getSubject(); String clientId = jwt.getJWTClaimsSet().getSubjectClaim();
Authentication authRequest = new JwtBearerAssertionAuthenticationToken(clientId, jwt); Authentication authRequest = new JwtBearerAssertionAuthenticationToken(clientId, jwt);
return this.getAuthenticationManager().authenticate(authRequest); return this.getAuthenticationManager().authenticate(authRequest);
} catch (IllegalArgumentException e) { } catch (ParseException e) {
throw new BadCredentialsException("Invalid JWT credential: " + assertion); throw new BadCredentialsException("Invalid JWT credential: " + assertion);
} }
} }