Repository: cxf Updated Branches: refs/heads/master 7bf10fc3f -> 6cd954781
Optional support for using OAuth2 client secret or certs for JWS/JWE Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/6cd95478 Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/6cd95478 Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/6cd95478 Branch: refs/heads/master Commit: 6cd954781734df6645867ca5208a7032c103c1e4 Parents: 7bf10fc Author: Sergey Beryozkin <sberyoz...@talend.com> Authored: Fri Dec 5 14:20:23 2014 +0000 Committer: Sergey Beryozkin <sberyoz...@talend.com> Committed: Fri Dec 5 14:20:23 2014 +0000 ---------------------------------------------------------------------- .../cxf/rs/security/jose/jws/JwsUtils.java | 25 +++-- .../grants/code/JwtRequestCodeFilter.java | 47 +++++++- .../oauth2/grants/code/JwtRequestCodeGrant.java | 45 ++++++-- .../oidc/idp/AbstractJwsJweProducer.java | 97 +++++++++++++++++ .../oidc/idp/UserInfoCodeResponseFilter.java | 60 +++++++++++ .../rs/security/oidc/idp/UserInfoProvider.java | 31 ++++++ .../rs/security/oidc/idp/UserInfoService.java | 80 ++++++++++++++ .../oidc/rp/AbstractTokenValidator.java | 15 +-- .../oidc/rp/idp/UserInfoCodeResponseFilter.java | 83 -------------- .../security/oidc/rp/idp/UserInfoProvider.java | 31 ------ .../security/oidc/rp/idp/UserInfoService.java | 108 ------------------- 11 files changed, 374 insertions(+), 248 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/6cd95478/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java index aef782a..20058ad 100644 --- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java +++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java @@ -207,11 +207,11 @@ public final class JwsUtils { if (JwkUtils.JWK_KEY_STORE_TYPE.equals(props.get(KeyManagementUtils.RSSEC_KEY_STORE_TYPE))) { JsonWebKey jwk = JwkUtils.loadJsonWebKey(m, props, JsonWebKey.KEY_OPER_SIGN); if (jwk != null) { - rsaSignatureAlgo = getSignatureAlgo(m, props, jwk.getAlgorithm()); + rsaSignatureAlgo = getSignatureAlgo(m, props, jwk.getAlgorithm(), getDefaultKeyAlgo(jwk)); theSigProvider = JwsUtils.getSignatureProvider(jwk, rsaSignatureAlgo); } } else { - rsaSignatureAlgo = getSignatureAlgo(m, props, null); + rsaSignatureAlgo = getSignatureAlgo(m, props, null, null); RSAPrivateKey pk = (RSAPrivateKey)KeyManagementUtils.loadPrivateKey(m, props, JsonWebKey.KEY_OPER_SIGN); theSigProvider = getRSAKeySignatureProvider(pk, rsaSignatureAlgo); @@ -228,12 +228,12 @@ public final class JwsUtils { if (JwkUtils.JWK_KEY_STORE_TYPE.equals(props.get(KeyManagementUtils.RSSEC_KEY_STORE_TYPE))) { JsonWebKey jwk = JwkUtils.loadJsonWebKey(m, props, JsonWebKey.KEY_OPER_VERIFY); if (jwk != null) { - rsaSignatureAlgo = getSignatureAlgo(m, props, jwk.getAlgorithm()); + rsaSignatureAlgo = getSignatureAlgo(m, props, jwk.getAlgorithm(), getDefaultKeyAlgo(jwk)); theVerifier = JwsUtils.getSignatureVerifier(jwk, rsaSignatureAlgo); } } else { - rsaSignatureAlgo = getSignatureAlgo(m, props, null); + rsaSignatureAlgo = getSignatureAlgo(m, props, null, null); theVerifier = getRSAKeySignatureVerifier( (RSAPublicKey)KeyManagementUtils.loadPublicKey(m, props), rsaSignatureAlgo); } @@ -249,13 +249,24 @@ public final class JwsUtils { throw new SecurityException(ex); } } - private static String getSignatureAlgo(Message m, Properties props, String algo) { + private static String getSignatureAlgo(Message m, Properties props, String algo, String defaultAlgo) { if (algo == null) { - return KeyManagementUtils.getKeyAlgorithm(m, props, - JSON_WEB_SIGNATURE_ALGO_PROP, JoseConstants.RS_SHA_256_ALGO); + if (defaultAlgo == null) { + defaultAlgo = JoseConstants.RS_SHA_256_ALGO; + } + return KeyManagementUtils.getKeyAlgorithm(m, props, JSON_WEB_SIGNATURE_ALGO_PROP, defaultAlgo); } return algo; } + private static String getDefaultKeyAlgo(JsonWebKey jwk) { + if (JsonWebKey.KEY_TYPE_OCTET.equals(jwk.getKeyType())) { + return JoseConstants.HMAC_SHA_256_ALGO; + } else if (JsonWebKey.KEY_TYPE_ELLIPTIC.equals(jwk.getKeyType())) { + return JoseConstants.ES_SHA_256_ALGO; + } else { + return JoseConstants.RS_SHA_256_ALGO; + } + } private static JwsCompactConsumer verify(JwsSignatureVerifier v, String content) { JwsCompactConsumer jws = new JwsCompactConsumer(content); if (!jws.verifySignatureWith(v)) { http://git-wip-us.apache.org/repos/asf/cxf/blob/6cd95478/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/JwtRequestCodeFilter.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/JwtRequestCodeFilter.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/JwtRequestCodeFilter.java index 3e43cf9..73f0022 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/JwtRequestCodeFilter.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/JwtRequestCodeFilter.java @@ -18,11 +18,16 @@ */ package org.apache.cxf.rs.security.oauth2.grants.code; +import java.security.cert.X509Certificate; +import java.security.interfaces.RSAPublicKey; import java.util.Map; +import javax.crypto.SecretKey; import javax.ws.rs.core.MultivaluedMap; +import org.apache.cxf.common.util.crypto.CryptoUtils; import org.apache.cxf.jaxrs.impl.MetadataMap; +import org.apache.cxf.rs.security.jose.JoseConstants; import org.apache.cxf.rs.security.jose.jwe.JweDecryptionProvider; import org.apache.cxf.rs.security.jose.jwe.JweUtils; import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer; @@ -38,6 +43,9 @@ public class JwtRequestCodeFilter implements AuthorizationCodeRequestFilter { private static final String REQUEST_PARAM = "request"; private JweDecryptionProvider jweDecryptor; private JwsSignatureVerifier jwsVerifier; + private boolean verifyWithClientCertificates; + private boolean verifyWithClientSecret; + private boolean decryptWithClientSecret; private String issuer; @Override public MultivaluedMap<String, String> process(MultivaluedMap<String, String> params, @@ -45,11 +53,11 @@ public class JwtRequestCodeFilter implements AuthorizationCodeRequestFilter { Client client) { String requestToken = params.getFirst(REQUEST_PARAM); if (requestToken != null) { - JweDecryptionProvider theJweDecryptor = getInitializedDecryptionProvider(); + JweDecryptionProvider theJweDecryptor = getInitializedDecryptionProvider(client); if (theJweDecryptor != null) { requestToken = theJweDecryptor.decrypt(requestToken).getContentText(); } - JwsSignatureVerifier theSigVerifier = getInitializedSigVerifier(); + JwsSignatureVerifier theSigVerifier = getInitializedSigVerifier(client); JwsJwtCompactConsumer consumer = new JwsJwtCompactConsumer(requestToken); if (!consumer.verifySignatureWith(theSigVerifier)) { throw new SecurityException("Invalid Signature"); @@ -79,19 +87,50 @@ public class JwtRequestCodeFilter implements AuthorizationCodeRequestFilter { this.jwsVerifier = theJwsVerifier; } - protected JweDecryptionProvider getInitializedDecryptionProvider() { + protected JweDecryptionProvider getInitializedDecryptionProvider(Client c) { if (jweDecryptor != null) { return jweDecryptor; } + if (decryptWithClientSecret) { + SecretKey key = CryptoUtils.decodeSecretKey(c.getClientSecret()); + return JweUtils.getDirectKeyJweDecryption(key, JoseConstants.A128GCM_ALGO); + } return JweUtils.loadDecryptionProvider(false); } - protected JwsSignatureVerifier getInitializedSigVerifier() { + protected JwsSignatureVerifier getInitializedSigVerifier(Client c) { if (jwsVerifier != null) { return jwsVerifier; } + if (verifyWithClientSecret) { + byte[] hmac = CryptoUtils.decodeSequence(c.getClientSecret()); + return JwsUtils.getHmacSignatureVerifier(hmac, JoseConstants.HMAC_SHA_256_ALGO); + } else if (verifyWithClientCertificates) { + X509Certificate cert = + (X509Certificate)CryptoUtils.decodeCertificate(c.getApplicationCertificates().get(0)); + return JwsUtils.getRSAKeySignatureVerifier((RSAPublicKey)cert.getPublicKey(), + JoseConstants.RS_SHA_256_ALGO); + } return JwsUtils.loadSignatureVerifier(true); } public void setIssuer(String issuer) { this.issuer = issuer; } + public void setVerifyWithClientCertificates(boolean verifyWithClientCertificates) { + if (verifyWithClientSecret) { + throw new SecurityException(); + } + this.verifyWithClientCertificates = verifyWithClientCertificates; + } + public void setVerifyWithClientSecret(boolean verifyWithClientSecret) { + if (decryptWithClientSecret || verifyWithClientCertificates) { + throw new SecurityException(); + } + this.verifyWithClientSecret = verifyWithClientSecret; + } + public void setDecryptWithClientSecret(boolean decryptWithClientSecret) { + if (verifyWithClientSecret) { + throw new SecurityException(); + } + this.decryptWithClientSecret = decryptWithClientSecret; + } } http://git-wip-us.apache.org/repos/asf/cxf/blob/6cd95478/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/JwtRequestCodeGrant.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/JwtRequestCodeGrant.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/JwtRequestCodeGrant.java index 5b2f8b9..7bfea42 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/JwtRequestCodeGrant.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/JwtRequestCodeGrant.java @@ -20,11 +20,13 @@ package org.apache.cxf.rs.security.oauth2.grants.code; import java.net.URI; +import javax.crypto.SecretKey; import javax.ws.rs.core.MultivaluedMap; import org.apache.cxf.common.util.StringUtils; +import org.apache.cxf.common.util.crypto.CryptoUtils; import org.apache.cxf.jaxrs.impl.MetadataMap; -import org.apache.cxf.rs.security.jose.JoseHeaders; +import org.apache.cxf.rs.security.jose.JoseConstants; 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.jws.JwsJwtCompactProducer; @@ -42,6 +44,9 @@ public class JwtRequestCodeGrant extends AuthorizationCodeGrant { private static final long serialVersionUID = -3738825769770411453L; private JwsSignatureProvider sigProvider; private JweEncryptionProvider encryptionProvider; + private String clientSecret; + private boolean encryptWithClientSecret; + private boolean signWithClientSecret; // can be a client id private String issuer; public JwtRequestCodeGrant() { @@ -64,13 +69,16 @@ public class JwtRequestCodeGrant extends AuthorizationCodeGrant { this.sigProvider = signatureProvider; } - protected JwsSignatureProvider getInitializedSigProvider(JoseHeaders headers) { + protected JwsSignatureProvider getInitializedSigProvider() { if (sigProvider != null) { return sigProvider; } - JwsSignatureProvider theSigProvider = JwsUtils.loadSignatureProvider(true); - headers.setAlgorithm(theSigProvider.getAlgorithm()); - return theSigProvider; + if (signWithClientSecret) { + byte[] hmac = CryptoUtils.decodeSequence(clientSecret); + return JwsUtils.getHmacSignatureProvider(hmac, JoseConstants.HMAC_SHA_256_ALGO); + } else { + return JwsUtils.loadSignatureProvider(true); + } } public MultivaluedMap<String, String> toMap() { String request = getRequest(); @@ -87,8 +95,7 @@ public class JwtRequestCodeGrant extends AuthorizationCodeGrant { claims.setClaim(key, map.getFirst(key)); } JwsJwtCompactProducer producer = new JwsJwtCompactProducer(claims); - JoseHeaders headers = new JoseHeaders(); - JwsSignatureProvider theSigProvider = getInitializedSigProvider(headers); + JwsSignatureProvider theSigProvider = getInitializedSigProvider(); String request = producer.signWith(theSigProvider); JweEncryptionProvider theEncryptionProvider = getInitializedEncryptionProvider(); @@ -101,11 +108,31 @@ public class JwtRequestCodeGrant extends AuthorizationCodeGrant { if (encryptionProvider != null) { return encryptionProvider; } - return JweUtils.loadEncryptionProvider(false); + if (encryptWithClientSecret) { + SecretKey key = CryptoUtils.decodeSecretKey(clientSecret); + return JweUtils.getDirectKeyJweEncryption(key, JoseConstants.A128GCM_ALGO); + } else { + return JweUtils.loadEncryptionProvider(false); + } } public void setIssuer(String issuer) { this.issuer = issuer; } - + + public void setClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + } + 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; + } } http://git-wip-us.apache.org/repos/asf/cxf/blob/6cd95478/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/AbstractJwsJweProducer.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/AbstractJwsJweProducer.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/AbstractJwsJweProducer.java new file mode 100644 index 0000000..dfbf8b6 --- /dev/null +++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/AbstractJwsJweProducer.java @@ -0,0 +1,97 @@ +/** + * 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.idp; + +import java.security.cert.X509Certificate; +import java.security.interfaces.RSAPublicKey; + +import javax.crypto.SecretKey; + +import org.apache.cxf.common.util.crypto.CryptoUtils; +import org.apache.cxf.rs.security.jose.JoseConstants; +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.jws.JwsSignatureProvider; +import org.apache.cxf.rs.security.jose.jws.JwsUtils; +import org.apache.cxf.rs.security.oauth2.common.Client; + +public abstract class AbstractJwsJweProducer { + private JwsSignatureProvider sigProvider; + private JweEncryptionProvider encryptionProvider; + private boolean encryptWithClientCertificates; + private boolean encryptWithClientSecret; + private boolean signWithClientSecret; + public void setSignatureProvider(JwsSignatureProvider signatureProvider) { + this.sigProvider = signatureProvider; + } + + protected JwsSignatureProvider getInitializedSigProvider(Client c, boolean required) { + if (sigProvider != null) { + return sigProvider; + } + + if (signWithClientSecret) { + byte[] hmac = CryptoUtils.decodeSequence(c.getClientSecret()); + return JwsUtils.getHmacSignatureProvider(hmac, JoseConstants.HMAC_SHA_256_ALGO); + } else { + return JwsUtils.loadSignatureProvider(required); + } + } + protected JweEncryptionProvider getInitializedEncryptionProvider(Client c, boolean required) { + if (encryptionProvider != null) { + return encryptionProvider; + } + JweEncryptionProvider theEncryptionProvider = null; + if (encryptWithClientSecret) { + SecretKey key = CryptoUtils.decodeSecretKey(c.getClientSecret()); + theEncryptionProvider = JweUtils.getDirectKeyJweEncryption(key, JoseConstants.A128GCM_ALGO); + } else if (encryptWithClientCertificates) { + X509Certificate cert = + (X509Certificate)CryptoUtils.decodeCertificate(c.getApplicationCertificates().get(0)); + theEncryptionProvider = JweUtils.createJweEncryptionProvider((RSAPublicKey)cert.getPublicKey(), + JoseConstants.RSA_OAEP_ALGO, + JoseConstants.A128GCM_ALGO, + null); + } + if (theEncryptionProvider == null) { + theEncryptionProvider = JweUtils.loadEncryptionProvider(required); + } + return theEncryptionProvider; + + } + + public void setEncryptWithClientCertificates(boolean encryptWithClientCertificates) { + if (encryptWithClientSecret) { + throw new SecurityException(); + } + this.encryptWithClientCertificates = encryptWithClientCertificates; + } + public void setEncryptWithClientSecret(boolean encryptWithClientSecret) { + if (signWithClientSecret || encryptWithClientCertificates) { + throw new SecurityException(); + } + this.encryptWithClientSecret = encryptWithClientSecret; + } + public void setSignWithClientSecret(boolean signWithClientSecret) { + if (encryptWithClientSecret) { + throw new SecurityException(); + } + this.signWithClientSecret = signWithClientSecret; + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/6cd95478/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/UserInfoCodeResponseFilter.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/UserInfoCodeResponseFilter.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/UserInfoCodeResponseFilter.java new file mode 100644 index 0000000..e0b5a3e --- /dev/null +++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/UserInfoCodeResponseFilter.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.oidc.idp; + +import org.apache.cxf.common.util.StringUtils; +import org.apache.cxf.rs.security.jose.jwe.JweEncryptionProvider; +import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactProducer; +import org.apache.cxf.rs.security.jose.jws.JwsSignatureProvider; +import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken; +import org.apache.cxf.rs.security.oauth2.common.ServerAccessToken; +import org.apache.cxf.rs.security.oauth2.provider.AccessTokenResponseFilter; +import org.apache.cxf.rs.security.oidc.common.UserToken; +import org.apache.cxf.rs.security.oidc.utils.OidcUtils; + +public class UserInfoCodeResponseFilter extends AbstractJwsJweProducer implements AccessTokenResponseFilter { + private UserInfoProvider userInfoProvider; + private String issuer; + @Override + public void process(ClientAccessToken ct, ServerAccessToken st) { + UserToken token = + userInfoProvider.getUserToken(st.getClient().getClientId(), st.getSubject(), st.getScopes()); + token.setIssuer(issuer); + token.setAudience(st.getClient().getClientId()); + + JwsJwtCompactProducer producer = new JwsJwtCompactProducer(token); + JwsSignatureProvider theSigProvider = getInitializedSigProvider(st.getClient(), true); + String idToken = producer.signWith(theSigProvider); + + JweEncryptionProvider theEncryptionProvider = getInitializedEncryptionProvider(st.getClient(), false); + if (theEncryptionProvider != null) { + idToken = theEncryptionProvider.encrypt(StringUtils.toBytesUTF8(idToken), null); + } + ct.getParameters().put(OidcUtils.ID_TOKEN, idToken); + + } + + public void setIssuer(String issuer) { + this.issuer = issuer; + } + public void setUserInfoProvider(UserInfoProvider userInfoProvider) { + this.userInfoProvider = userInfoProvider; + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/6cd95478/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/UserInfoProvider.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/UserInfoProvider.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/UserInfoProvider.java new file mode 100644 index 0000000..0fa9c22 --- /dev/null +++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/UserInfoProvider.java @@ -0,0 +1,31 @@ +/** + * 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.idp; + +import java.util.List; + +import org.apache.cxf.rs.security.oauth2.common.OAuthPermission; +import org.apache.cxf.rs.security.oauth2.common.UserSubject; +import org.apache.cxf.rs.security.oidc.common.UserInfo; +import org.apache.cxf.rs.security.oidc.common.UserToken; + +public interface UserInfoProvider { + UserToken getUserToken(String clientId, UserSubject authenticatedUser, List<OAuthPermission> scopes); + UserInfo getUserInfo(String clientId, UserSubject authenticatedUser, List<OAuthPermission> scopes); +} http://git-wip-us.apache.org/repos/asf/cxf/blob/6cd95478/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/UserInfoService.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/UserInfoService.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/UserInfoService.java new file mode 100644 index 0000000..7896678 --- /dev/null +++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/UserInfoService.java @@ -0,0 +1,80 @@ +/** + * 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.idp; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.Response; + +import org.apache.cxf.common.util.StringUtils; +import org.apache.cxf.jaxrs.ext.MessageContext; +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; +import org.apache.cxf.rs.security.oauth2.common.OAuthContext; +import org.apache.cxf.rs.security.oauth2.utils.OAuthContextUtils; +import org.apache.cxf.rs.security.oidc.common.UserInfo; + +@Path("/userinfo") +public class UserInfoService extends AbstractJwsJweProducer { + private UserInfoProvider userInfoProvider; + private String issuer; + + @Context + private MessageContext mc; + @GET + @Produces({"application/json", "application/jwt" }) + public Response getUserInfo() { + OAuthContext oauth = OAuthContextUtils.getContext(mc); + UserInfo userInfo = + userInfoProvider.getUserInfo(oauth.getClientId(), oauth.getSubject(), oauth.getPermissions()); + if (userInfo != null) { + userInfo.setIssuer(issuer); + } + userInfo.setAudience(oauth.getClientId()); + + Object responseEntity = userInfo; + + JwsJwtCompactProducer producer = new JwsJwtCompactProducer(userInfo); + JwsSignatureProvider theSigProvider = getInitializedSigProvider(null, false); + JweEncryptionProvider theEncryptionProvider = getInitializedEncryptionProvider(null, false); + if (theSigProvider != null) { + String userInfoString = producer.signWith(theSigProvider); + if (theEncryptionProvider != null) { + userInfoString = theEncryptionProvider.encrypt(StringUtils.toBytesUTF8(userInfoString), null); + } + responseEntity = userInfoString; + } else if (theEncryptionProvider != null) { + JweJwtCompactProducer jwe = new JweJwtCompactProducer(userInfo); + responseEntity = jwe.encryptWith(theEncryptionProvider); + } + return Response.ok(responseEntity).build(); + + } + + public void setIssuer(String issuer) { + this.issuer = issuer; + } + public void setUserInfoProvider(UserInfoProvider userInfoProvider) { + this.userInfoProvider = userInfoProvider; + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/6cd95478/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/AbstractTokenValidator.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/AbstractTokenValidator.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/AbstractTokenValidator.java index 9c26804..69c54a4 100644 --- a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/AbstractTokenValidator.java +++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/AbstractTokenValidator.java @@ -41,7 +41,9 @@ public abstract class AbstractTokenValidator { private WebClient jwkSetClient; private ConcurrentHashMap<String, JsonWebKey> keyMap = new ConcurrentHashMap<String, JsonWebKey>(); - protected JwtToken getJwtToken(String wrappedJwtToken, String clientId, String idTokenKid, + protected JwtToken getJwtToken(String wrappedJwtToken, + String clientId, + String idTokenKid, boolean jweOnly) { if (wrappedJwtToken == null) { throw new SecurityException("ID Token is missing"); @@ -89,13 +91,14 @@ public abstract class AbstractTokenValidator { throw new SecurityException("Provider Jwk Set Client is not available"); } String keyId = idTokenKid != null ? idTokenKid : jwtConsumer.getJwtToken().getHeaders().getKeyId(); - if (keyId == null) { - throw new SecurityException("Provider JWK key id is null"); - } - JsonWebKey key = keyMap.get(keyId); + JsonWebKey key = keyId != null ? keyMap.get(keyId) : null; if (key == null) { JsonWebKeys keys = jwkSetClient.get(JsonWebKeys.class); - key = keys.getKey(keyId); + if (keyId != null) { + key = keys.getKey(keyId); + } else if (keys.getKeys().size() == 1) { + key = keys.getKeys().get(0); + } keyMap.putAll(keys.getKeyIdMap()); } if (key == null) { http://git-wip-us.apache.org/repos/asf/cxf/blob/6cd95478/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/idp/UserInfoCodeResponseFilter.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/idp/UserInfoCodeResponseFilter.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/idp/UserInfoCodeResponseFilter.java deleted file mode 100644 index 0a1375a..0000000 --- a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/idp/UserInfoCodeResponseFilter.java +++ /dev/null @@ -1,83 +0,0 @@ -/** - * 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.idp; - -import org.apache.cxf.common.util.StringUtils; -import org.apache.cxf.rs.security.jose.JoseHeaders; -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.jws.JwsJwtCompactProducer; -import org.apache.cxf.rs.security.jose.jws.JwsSignatureProvider; -import org.apache.cxf.rs.security.jose.jws.JwsUtils; -import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken; -import org.apache.cxf.rs.security.oauth2.common.ServerAccessToken; -import org.apache.cxf.rs.security.oauth2.provider.AccessTokenResponseFilter; -import org.apache.cxf.rs.security.oidc.common.UserToken; -import org.apache.cxf.rs.security.oidc.utils.OidcUtils; - -public class UserInfoCodeResponseFilter implements AccessTokenResponseFilter { - private JwsSignatureProvider sigProvider; - private JweEncryptionProvider encryptionProvider; - private UserInfoProvider userInfoProvider; - private String issuer; - @Override - public void process(ClientAccessToken ct, ServerAccessToken st) { - UserToken token = - userInfoProvider.getUserToken(st.getClient().getClientId(), st.getSubject(), st.getScopes()); - token.setIssuer(issuer); - token.setAudience(st.getClient().getClientId()); - - JwsJwtCompactProducer producer = new JwsJwtCompactProducer(token); - JoseHeaders headers = new JoseHeaders(); - JwsSignatureProvider theSigProvider = getInitializedSigProvider(headers); - String idToken = producer.signWith(theSigProvider); - - JweEncryptionProvider theEncryptionProvider = getInitializedEncryptionProvider(); - if (theEncryptionProvider != null) { - idToken = theEncryptionProvider.encrypt(StringUtils.toBytesUTF8(idToken), null); - } - ct.getParameters().put(OidcUtils.ID_TOKEN, idToken); - - } - public void setSignatureProvider(JwsSignatureProvider signatureProvider) { - this.sigProvider = signatureProvider; - } - - protected JwsSignatureProvider getInitializedSigProvider(JoseHeaders headers) { - if (sigProvider != null) { - return sigProvider; - } - JwsSignatureProvider theSigProvider = JwsUtils.loadSignatureProvider(true); - headers.setAlgorithm(theSigProvider.getAlgorithm()); - return theSigProvider; - } - protected JweEncryptionProvider getInitializedEncryptionProvider() { - if (encryptionProvider != null) { - return encryptionProvider; - } - return JweUtils.loadEncryptionProvider(false); - } - - public void setIssuer(String issuer) { - this.issuer = issuer; - } - public void setUserInfoProvider(UserInfoProvider userInfoProvider) { - this.userInfoProvider = userInfoProvider; - } -} http://git-wip-us.apache.org/repos/asf/cxf/blob/6cd95478/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/idp/UserInfoProvider.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/idp/UserInfoProvider.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/idp/UserInfoProvider.java deleted file mode 100644 index db58214..0000000 --- a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/idp/UserInfoProvider.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * 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.idp; - -import java.util.List; - -import org.apache.cxf.rs.security.oauth2.common.OAuthPermission; -import org.apache.cxf.rs.security.oauth2.common.UserSubject; -import org.apache.cxf.rs.security.oidc.common.UserInfo; -import org.apache.cxf.rs.security.oidc.common.UserToken; - -public interface UserInfoProvider { - UserToken getUserToken(String clientId, UserSubject authenticatedUser, List<OAuthPermission> scopes); - UserInfo getUserInfo(String clientId, UserSubject authenticatedUser, List<OAuthPermission> scopes); -} http://git-wip-us.apache.org/repos/asf/cxf/blob/6cd95478/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/idp/UserInfoService.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/idp/UserInfoService.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/idp/UserInfoService.java deleted file mode 100644 index dbe20f9..0000000 --- a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/idp/UserInfoService.java +++ /dev/null @@ -1,108 +0,0 @@ -/** - * 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.idp; - -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.Response; - -import org.apache.cxf.common.util.StringUtils; -import org.apache.cxf.jaxrs.ext.MessageContext; -import org.apache.cxf.rs.security.jose.JoseHeaders; -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.jwe.JweUtils; -import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactProducer; -import org.apache.cxf.rs.security.jose.jws.JwsSignatureProvider; -import org.apache.cxf.rs.security.jose.jws.JwsUtils; -import org.apache.cxf.rs.security.oauth2.common.OAuthContext; -import org.apache.cxf.rs.security.oauth2.utils.OAuthContextUtils; -import org.apache.cxf.rs.security.oidc.common.UserInfo; - -@Path("/userinfo") -public class UserInfoService { - // TODO: review if it makes sense to do JWE and JWS at the out filter level instead - private JwsSignatureProvider sigProvider; - private JweEncryptionProvider encryptionProvider; - private UserInfoProvider userInfoProvider; - private String issuer; - - @Context - private MessageContext mc; - @GET - @Produces({"application/json", "application/jwt" }) - public Response getUserInfo() { - OAuthContext oauth = OAuthContextUtils.getContext(mc); - UserInfo userInfo = - userInfoProvider.getUserInfo(oauth.getClientId(), oauth.getSubject(), oauth.getPermissions()); - if (userInfo != null) { - userInfo.setIssuer(issuer); - } - userInfo.setAudience(oauth.getClientId()); - - Object responseEntity = userInfo; - - JwsJwtCompactProducer producer = new JwsJwtCompactProducer(userInfo); - JoseHeaders headers = new JoseHeaders(); - JwsSignatureProvider theSigProvider = getInitializedSigProvider(headers); - JweEncryptionProvider theEncryptionProvider = getInitializedEncryptionProvider(); - if (theSigProvider != null) { - String userInfoString = producer.signWith(theSigProvider); - if (theEncryptionProvider != null) { - userInfoString = theEncryptionProvider.encrypt(StringUtils.toBytesUTF8(userInfoString), null); - } - responseEntity = userInfoString; - } else if (theEncryptionProvider != null) { - JweJwtCompactProducer jwe = new JweJwtCompactProducer(userInfo); - responseEntity = jwe.encryptWith(theEncryptionProvider); - } - return Response.ok(responseEntity).build(); - - } - public void setSignatureProvider(JwsSignatureProvider signatureProvider) { - this.sigProvider = signatureProvider; - } - - protected JwsSignatureProvider getInitializedSigProvider(JoseHeaders headers) { - if (sigProvider != null) { - return sigProvider; - } - JwsSignatureProvider theSigProvider = JwsUtils.loadSignatureProvider(false); - headers.setAlgorithm(theSigProvider.getAlgorithm()); - return theSigProvider; - } - protected JweEncryptionProvider getInitializedEncryptionProvider() { - if (encryptionProvider != null) { - return encryptionProvider; - } - return JweUtils.loadEncryptionProvider(false); - } - - public void setIssuer(String issuer) { - this.issuer = issuer; - } - public UserInfoProvider getUserInfoProvider() { - return userInfoProvider; - } - public void setUserInfoProvider(UserInfoProvider userInfoProvider) { - this.userInfoProvider = userInfoProvider; - } -}