Return-Path: X-Original-To: apmail-cxf-commits-archive@www.apache.org Delivered-To: apmail-cxf-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 7F10D18D9A for ; Fri, 5 Feb 2016 14:21:31 +0000 (UTC) Received: (qmail 91967 invoked by uid 500); 5 Feb 2016 14:21:31 -0000 Delivered-To: apmail-cxf-commits-archive@cxf.apache.org Received: (qmail 91904 invoked by uid 500); 5 Feb 2016 14:21:31 -0000 Mailing-List: contact commits-help@cxf.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cxf.apache.org Delivered-To: mailing list commits@cxf.apache.org Received: (qmail 91894 invoked by uid 99); 5 Feb 2016 14:21:31 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 05 Feb 2016 14:21:31 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 37B05E0063; Fri, 5 Feb 2016 14:21:31 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: sergeyb@apache.org To: commits@cxf.apache.org Message-Id: <97d5b3e036484895a92565a86778f392@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: cxf git commit: Adding the renamed resources Date: Fri, 5 Feb 2016 14:21:31 +0000 (UTC) Repository: cxf Updated Branches: refs/heads/3.1.x-fixes 89b7bb172 -> c3399966e Adding the renamed resources Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/c3399966 Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/c3399966 Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/c3399966 Branch: refs/heads/3.1.x-fixes Commit: c3399966e4060837fd17511d604a26c8d12dad7c Parents: 89b7bb1 Author: Sergey Beryozkin Authored: Fri Feb 5 14:20:40 2016 +0000 Committer: Sergey Beryozkin Committed: Fri Feb 5 14:21:19 2016 +0000 ---------------------------------------------------------------------- .../rs/security/jose/jwt/JoseJwtConsumer.java | 107 +++++++++++ .../rs/security/jose/jwt/JoseJwtProducer.java | 91 +++++++++ .../oauth2/provider/OAuthJoseJwtConsumer.java | 60 ++++++ .../oauth2/provider/OAuthJoseJwtProducer.java | 71 +++++++ .../provider/OAuthServerJoseJwtProducer.java | 65 +++++++ .../security/oidc/rp/OidcClaimsValidator.java | 192 +++++++++++++++++++ 6 files changed, 586 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/c3399966/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JoseJwtConsumer.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JoseJwtConsumer.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JoseJwtConsumer.java new file mode 100644 index 0000000..35a6eee --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JoseJwtConsumer.java @@ -0,0 +1,107 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.cxf.rs.security.jose.jwt; + +import org.apache.cxf.rs.security.jose.common.AbstractJoseConsumer; +import org.apache.cxf.rs.security.jose.jwe.JweDecryptionOutput; +import org.apache.cxf.rs.security.jose.jwe.JweDecryptionProvider; +import org.apache.cxf.rs.security.jose.jwe.JweHeaders; +import org.apache.cxf.rs.security.jose.jwe.JweJwtCompactConsumer; +import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer; +import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier; + +public class JoseJwtConsumer extends AbstractJoseConsumer { + private boolean jwsRequired = true; + private boolean jweRequired; + + public JwtToken getJwtToken(String wrappedJwtToken) { + return getJwtToken(wrappedJwtToken, null, null); + } + public JwtToken getJwtToken(String wrappedJwtToken, + JweDecryptionProvider theDecryptor, + JwsSignatureVerifier theSigVerifier) { + if (!isJwsRequired() && !isJweRequired()) { + throw new JwtException("Unable to process JWT"); + } + + JweHeaders jweHeaders = new JweHeaders(); + if (isJweRequired()) { + JweJwtCompactConsumer jwtConsumer = new JweJwtCompactConsumer(wrappedJwtToken); + + if (theDecryptor == null) { + theDecryptor = getInitializedDecryptionProvider(jwtConsumer.getHeaders()); + } + if (theDecryptor == null) { + throw new JwtException("Unable to decrypt JWT"); + } + + if (!isJwsRequired()) { + return jwtConsumer.decryptWith(theDecryptor); + } + + JweDecryptionOutput decOutput = theDecryptor.decrypt(wrappedJwtToken); + wrappedJwtToken = decOutput.getContentText(); + jweHeaders = decOutput.getHeaders(); + } + + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(wrappedJwtToken); + JwtToken jwt = jwtConsumer.getJwtToken(); + // Store the encryption headers as well + jwt = new JwtToken(jwt.getJwsHeaders(), jweHeaders, jwt.getClaims()); + + if (isJwsRequired()) { + if (theSigVerifier == null) { + theSigVerifier = getInitializedSignatureVerifier(jwt); + } + if (theSigVerifier == null) { + throw new JwtException("Unable to validate JWT"); + } + + if (!jwtConsumer.verifySignatureWith(theSigVerifier)) { + throw new JwtException("Invalid Signature"); + } + } + + validateToken(jwt); + return jwt; + } + + protected JwsSignatureVerifier getInitializedSignatureVerifier(JwtToken jwt) { + return super.getInitializedSignatureVerifier(jwt.getJwsHeaders()); + } + + protected void validateToken(JwtToken jwt) { + } + public boolean isJwsRequired() { + return jwsRequired; + } + + public void setJwsRequired(boolean jwsRequired) { + this.jwsRequired = jwsRequired; + } + + public boolean isJweRequired() { + return jweRequired; + } + + public void setJweRequired(boolean jweRequired) { + this.jweRequired = jweRequired; + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/c3399966/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JoseJwtProducer.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JoseJwtProducer.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JoseJwtProducer.java new file mode 100644 index 0000000..c729cbe --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JoseJwtProducer.java @@ -0,0 +1,91 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.cxf.rs.security.jose.jwt; + +import org.apache.cxf.common.util.StringUtils; +import org.apache.cxf.rs.security.jose.common.AbstractJoseProducer; +import org.apache.cxf.rs.security.jose.jwe.JweEncryptionProvider; +import org.apache.cxf.rs.security.jose.jwe.JweJwtCompactProducer; +import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactProducer; +import org.apache.cxf.rs.security.jose.jws.JwsSignatureProvider; + +public class JoseJwtProducer extends AbstractJoseProducer { + private boolean jwsRequired = true; + private boolean jweRequired; + + public String processJwt(JwtToken jwt) { + return processJwt(jwt, null, null); + } + public String processJwt(JwtToken jwt, + JweEncryptionProvider theEncProvider, + JwsSignatureProvider theSigProvider) { + if (!isJwsRequired() && !isJweRequired()) { + throw new JwtException("Unable to secure JWT"); + } + String data = null; + + if (isJweRequired() && theEncProvider == null) { + theEncProvider = getInitializedEncryptionProvider(jwt.getJweHeaders()); + if (theEncProvider == null) { + throw new JwtException("Unable to encrypt JWT"); + } + } + + if (isJwsRequired()) { + JwsJwtCompactProducer jws = new JwsJwtCompactProducer(jwt); + if (jws.isPlainText()) { + data = jws.getSignedEncodedJws(); + } else { + if (theSigProvider == null) { + theSigProvider = getInitializedSignatureProvider(jwt.getJwsHeaders()); + } + + if (theSigProvider == null) { + throw new JwtException("Unable to sign JWT"); + } + + data = jws.signWith(theSigProvider); + } + if (theEncProvider != null) { + data = theEncProvider.encrypt(StringUtils.toBytesUTF8(data), null); + } + } else { + JweJwtCompactProducer jwe = new JweJwtCompactProducer(jwt); + data = jwe.encryptWith(theEncProvider); + } + return data; + } + + public boolean isJwsRequired() { + return jwsRequired; + } + + public void setJwsRequired(boolean jwsRequired) { + this.jwsRequired = jwsRequired; + } + + public boolean isJweRequired() { + return jweRequired; + } + + public void setJweRequired(boolean jweRequired) { + this.jweRequired = jweRequired; + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/c3399966/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/OAuthJoseJwtConsumer.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/OAuthJoseJwtConsumer.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/OAuthJoseJwtConsumer.java new file mode 100644 index 0000000..a6e4541 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/OAuthJoseJwtConsumer.java @@ -0,0 +1,60 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.cxf.rs.security.oauth2.provider; + +import org.apache.cxf.common.util.StringUtils; +import org.apache.cxf.rs.security.jose.jwe.JweDecryptionProvider; +import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier; +import org.apache.cxf.rs.security.jose.jwt.JoseJwtConsumer; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.rs.security.oauth2.utils.OAuthUtils; + +public class OAuthJoseJwtConsumer extends JoseJwtConsumer { + + private boolean decryptWithClientSecret; + private boolean verifyWithClientSecret; + + public JwtToken getJwtToken(String wrappedJwtToken, String clientSecret) { + return getJwtToken(wrappedJwtToken, + getInitializedDecryptionProvider(clientSecret), + getInitializedSignatureVerifier(clientSecret)); + } + + protected JwsSignatureVerifier getInitializedSignatureVerifier(String clientSecret) { + if (verifyWithClientSecret && !StringUtils.isEmpty(clientSecret)) { + return OAuthUtils.getClientSecretSignatureVerifier(clientSecret); + } else { + return null; + } + } + protected JweDecryptionProvider getInitializedDecryptionProvider(String clientSecret) { + if (decryptWithClientSecret && !StringUtils.isEmpty(clientSecret)) { + return OAuthUtils.getClientSecretDecryptionProvider(clientSecret); + } else { + return null; + } + } + + public void setDecryptWithClientSecret(boolean decryptWithClientSecret) { + this.decryptWithClientSecret = verifyWithClientSecret; + } + public void setVerifyWithClientSecret(boolean verifyWithClientSecret) { + this.verifyWithClientSecret = verifyWithClientSecret; + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/c3399966/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/OAuthJoseJwtProducer.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/OAuthJoseJwtProducer.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/OAuthJoseJwtProducer.java new file mode 100644 index 0000000..506e826 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/OAuthJoseJwtProducer.java @@ -0,0 +1,71 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.cxf.rs.security.oauth2.provider; + +import org.apache.cxf.common.util.StringUtils; +import org.apache.cxf.rs.security.jose.jwe.JweEncryptionProvider; +import org.apache.cxf.rs.security.jose.jws.JwsSignatureProvider; +import org.apache.cxf.rs.security.jose.jwt.JoseJwtProducer; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.rs.security.oauth2.utils.OAuthUtils; + +public class OAuthJoseJwtProducer extends JoseJwtProducer { + private boolean encryptWithClientSecret; + private boolean signWithClientSecret; + + public String processJwt(JwtToken jwt, String clientSecret) { + return processJwt(jwt, + getInitializedEncryptionProvider(clientSecret), + getInitializedSignatureProvider(clientSecret)); + } + + protected JwsSignatureProvider getInitializedSignatureProvider(String clientSecret) { + if (signWithClientSecret && !StringUtils.isEmpty(clientSecret)) { + return OAuthUtils.getClientSecretSignatureProvider(clientSecret); + } else { + return null; + } + } + protected JweEncryptionProvider getInitializedEncryptionProvider(String clientSecret) { + if (encryptWithClientSecret && !StringUtils.isEmpty(clientSecret)) { + return OAuthUtils.getClientSecretEncryptionProvider(clientSecret); + } else { + return null; + } + } + + public void setEncryptWithClientSecret(boolean encryptWithClientSecret) { + if (signWithClientSecret) { + throw new SecurityException(); + } + this.encryptWithClientSecret = encryptWithClientSecret; + } + public void setSignWithClientSecret(boolean signWithClientSecret) { + if (encryptWithClientSecret) { + throw new SecurityException(); + } + this.signWithClientSecret = signWithClientSecret; + } + public boolean isSignWithClientSecret() { + return signWithClientSecret; + } + public boolean isEncryptWithClientSecret() { + return encryptWithClientSecret; + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/c3399966/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/OAuthServerJoseJwtProducer.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/OAuthServerJoseJwtProducer.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/OAuthServerJoseJwtProducer.java new file mode 100644 index 0000000..24e6a16 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/OAuthServerJoseJwtProducer.java @@ -0,0 +1,65 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.cxf.rs.security.oauth2.provider; + +import java.security.cert.X509Certificate; +import java.security.interfaces.RSAPublicKey; + +import org.apache.cxf.rs.security.jose.jwa.ContentAlgorithm; +import org.apache.cxf.rs.security.jose.jwa.KeyAlgorithm; +import org.apache.cxf.rs.security.jose.jwe.JweEncryptionProvider; +import org.apache.cxf.rs.security.jose.jwe.JweUtils; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.rs.security.oauth2.common.Client; +import org.apache.cxf.rt.security.crypto.CryptoUtils; + +public class OAuthServerJoseJwtProducer extends OAuthJoseJwtProducer { + private boolean encryptWithClientCertificates; + + public String processJwt(JwtToken jwt, Client client) { + return processJwt(jwt, + getInitializedEncryptionProvider(client), + getInitializedSignatureProvider(client.getClientSecret())); + } + + protected JweEncryptionProvider getInitializedEncryptionProvider(Client c) { + JweEncryptionProvider theEncryptionProvider = null; + if (encryptWithClientCertificates) { + X509Certificate cert = + (X509Certificate)CryptoUtils.decodeCertificate(c.getApplicationCertificates().get(0)); + theEncryptionProvider = JweUtils.createJweEncryptionProvider((RSAPublicKey)cert.getPublicKey(), + KeyAlgorithm.RSA_OAEP, + ContentAlgorithm.A128GCM, + null); + } + if (theEncryptionProvider == null) { + theEncryptionProvider = super.getInitializedEncryptionProvider(c.getClientSecret()); + } + return theEncryptionProvider; + + } + + public void setEncryptWithClientCertificates(boolean encryptWithClientCertificates) { + if (isEncryptWithClientSecret()) { + throw new SecurityException(); + } + this.encryptWithClientCertificates = encryptWithClientCertificates; + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/c3399966/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcClaimsValidator.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcClaimsValidator.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcClaimsValidator.java new file mode 100644 index 0000000..31a3111 --- /dev/null +++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcClaimsValidator.java @@ -0,0 +1,192 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.cxf.rs.security.oidc.rp; + +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.cxf.common.util.StringUtils; +import org.apache.cxf.jaxrs.client.WebClient; +import org.apache.cxf.rs.security.jose.jwk.JsonWebKey; +import org.apache.cxf.rs.security.jose.jwk.JsonWebKeys; +import org.apache.cxf.rs.security.jose.jwk.JwkUtils; +import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier; +import org.apache.cxf.rs.security.jose.jws.JwsUtils; +import org.apache.cxf.rs.security.jose.jwt.JwtClaims; +import org.apache.cxf.rs.security.jose.jwt.JwtException; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.rs.security.jose.jwt.JwtUtils; +import org.apache.cxf.rs.security.oauth2.provider.OAuthJoseJwtConsumer; +import org.apache.cxf.rs.security.oauth2.provider.OAuthServiceException; +import org.apache.cxf.rs.security.oidc.common.IdToken; + +public class OidcClaimsValidator extends OAuthJoseJwtConsumer { + private static final String SELF_ISSUED_ISSUER = "https://self-issued.me"; + private String issuerId; + private int clockOffset; + private int ttl; + private WebClient jwkSetClient; + private boolean supportSelfIssuedProvider; + private boolean strictTimeValidation; + private ConcurrentHashMap keyMap = new ConcurrentHashMap(); + + /** + * Validate core JWT claims + * @param claims the claims + * @param clientId OAuth2 client id + * @param validateClaimsAlways if set to true then enforce that the claims + * to be validated must be set + */ + public void validateJwtClaims(JwtClaims claims, String clientId, boolean validateClaimsAlways) { + // validate the issuer + String issuer = claims.getIssuer(); + if (issuer == null && validateClaimsAlways) { + throw new OAuthServiceException("Invalid issuer"); + } + if (supportSelfIssuedProvider && issuerId == null + && issuer != null && SELF_ISSUED_ISSUER.equals(issuer)) { + validateSelfIssuedProvider(claims, clientId, validateClaimsAlways); + } else { + if (issuer != null && !issuer.equals(issuerId)) { + throw new OAuthServiceException("Invalid issuer"); + } + // validate subject + if (claims.getSubject() == null) { + throw new OAuthServiceException("Invalid subject"); + } + + // validate authorized party + String authorizedParty = (String)claims.getClaim(IdToken.AZP_CLAIM); + if (authorizedParty != null && !authorizedParty.equals(clientId)) { + throw new OAuthServiceException("Invalid authorized party"); + } + // validate audience + List audiences = claims.getAudiences(); + if (StringUtils.isEmpty(audiences) && validateClaimsAlways + || !StringUtils.isEmpty(audiences) && !audiences.contains(clientId)) { + throw new OAuthServiceException("Invalid audience"); + } + + // If strict time validation: if no issuedTime claim is set then an expiresAt claim must be set + // Otherwise: validate only if expiresAt claim is set + boolean expiredRequired = + validateClaimsAlways || strictTimeValidation && claims.getIssuedAt() == null; + try { + JwtUtils.validateJwtExpiry(claims, clockOffset, expiredRequired); + } catch (JwtException ex) { + throw new OAuthServiceException("ID Token has expired", ex); + } + + // If strict time validation: If no expiresAt claim is set then an issuedAt claim must be set + // Otherwise: validate only if issuedAt claim is set + boolean issuedAtRequired = + validateClaimsAlways || strictTimeValidation && claims.getExpiryTime() == null; + try { + JwtUtils.validateJwtIssuedAt(claims, ttl, clockOffset, issuedAtRequired); + } catch (JwtException ex) { + throw new OAuthServiceException("Invalid issuedAt claim", ex); + } + if (strictTimeValidation) { + try { + JwtUtils.validateJwtNotBefore(claims, clockOffset, strictTimeValidation); + } catch (JwtException ex) { + throw new OAuthServiceException("ID Token can not be used yet", ex); + } + } + } + } + + private void validateSelfIssuedProvider(JwtClaims claims, String clientId, boolean validateClaimsAlways) { + } + + public void setIssuerId(String issuerId) { + this.issuerId = issuerId; + } + + public void setJwkSetClient(WebClient jwkSetClient) { + this.jwkSetClient = jwkSetClient; + } + + @Override + protected JwsSignatureVerifier getInitializedSignatureVerifier(JwtToken jwt) { + JsonWebKey key = null; + if (supportSelfIssuedProvider && SELF_ISSUED_ISSUER.equals(jwt.getClaim("issuer"))) { + String publicKeyJson = (String)jwt.getClaim("sub_jwk"); + if (publicKeyJson != null) { + JsonWebKey publicKey = JwkUtils.readJwkKey(publicKeyJson); + String thumbprint = JwkUtils.getThumbprint(publicKey); + if (thumbprint.equals(jwt.getClaim("sub"))) { + key = publicKey; + } + } + if (key == null) { + throw new SecurityException("Self-issued JWK key is invalid or not available"); + } + } else { + String keyId = jwt.getJwsHeaders().getKeyId(); + key = keyId != null ? keyMap.get(keyId) : null; + if (key == null && jwkSetClient != null) { + JsonWebKeys keys = jwkSetClient.get(JsonWebKeys.class); + if (keyId != null) { + key = keys.getKey(keyId); + } else if (keys.getKeys().size() == 1) { + key = keys.getKeys().get(0); + } + //jwkSetClient returns the most up-to-date keys + keyMap.clear(); + keyMap.putAll(keys.getKeyIdMap()); + } + } + JwsSignatureVerifier theJwsVerifier = null; + if (key != null) { + theJwsVerifier = JwsUtils.getSignatureVerifier(key); + } else { + theJwsVerifier = super.getInitializedSignatureVerifier(jwt.getJwsHeaders()); + } + if (theJwsVerifier == null) { + throw new SecurityException("JWS Verifier is not available"); + } + + return theJwsVerifier; + } + + public void setSupportSelfIssuedProvider(boolean supportSelfIssuedProvider) { + this.supportSelfIssuedProvider = supportSelfIssuedProvider; + } + + public int getClockOffset() { + return clockOffset; + } + + public void setClockOffset(int clockOffset) { + this.clockOffset = clockOffset; + } + + public void setStrictTimeValidation(boolean strictTimeValidation) { + this.strictTimeValidation = strictTimeValidation; + } + + public int getTtl() { + return ttl; + } + + public void setTtl(int ttl) { + this.ttl = ttl; + } +}