Repository: cxf Updated Branches: refs/heads/master cc5f43b74 -> d8cf47faa
Adding the missing files Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/d8cf47fa Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/d8cf47fa Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/d8cf47fa Branch: refs/heads/master Commit: d8cf47faa9409b9039ae6d991140a152282d3011 Parents: cc5f43b Author: Sergey Beryozkin <sberyoz...@talend.com> Authored: Fri Nov 14 09:52:15 2014 +0000 Committer: Sergey Beryozkin <sberyoz...@talend.com> Committed: Fri Nov 14 09:52:15 2014 +0000 ---------------------------------------------------------------------- .../oidc/rp/AbstractTokenValidator.java | 144 +++++++++++++++++++ .../security/oidc/rp/UserProfileValidator.java | 66 +++++++++ 2 files changed, 210 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/d8cf47fa/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 new file mode 100644 index 0000000..1553258 --- /dev/null +++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/AbstractTokenValidator.java @@ -0,0 +1,144 @@ +/** + * 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.concurrent.ConcurrentHashMap; + +import org.apache.cxf.jaxrs.client.WebClient; +import org.apache.cxf.rs.security.jose.jwe.JweDecryptionProvider; +import org.apache.cxf.rs.security.jose.jwe.JweJwtCompactConsumer; +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.jws.JwsJwtCompactConsumer; +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.JwtToken; + +public abstract class AbstractTokenValidator { + private JweDecryptionProvider jweDecryptor; + private JwsSignatureVerifier jwsVerifier; + private String issuerId; + private int issuedAtRange; + private WebClient jwkSetClient; + private ConcurrentHashMap<String, JsonWebKey> keyMap = new ConcurrentHashMap<String, JsonWebKey>(); + + protected JwtToken getJwtToken(String wrappedJwtToken, String clientId, String idTokenKid, + boolean jweOnly) { + if (wrappedJwtToken == null) { + throw new SecurityException("ID Token is missing"); + } + // Decrypt the token if needed + if (jweDecryptor != null) { + if (jweOnly) { + return new JweJwtCompactConsumer(wrappedJwtToken).decryptWith(jweDecryptor); + } + wrappedJwtToken = jweDecryptor.decrypt(wrappedJwtToken).getContentText(); + } else if (jweOnly) { + throw new SecurityException("Token can not be decrypted"); + } + + // validate token signature + return getTokenValidateSignature(wrappedJwtToken, idTokenKid); + } + + protected void validateJwtClaims(JwtClaims claims, String clientId, boolean validateClaimsAlways) { + // validate subject + if (claims.getSubject() == null) { + throw new SecurityException("Invalid subject"); + } + // validate audience + String aud = claims.getAudience(); + if (aud == null && validateClaimsAlways || aud != null && !clientId.equals(aud)) { + throw new SecurityException("Invalid audience"); + } + + // validate the provider + String issuer = claims.getIssuer(); + if (issuerId == null && validateClaimsAlways || issuerId != null && !issuerId.equals(issuer)) { + throw new SecurityException("Invalid provider"); + } + Long currentTimeInSecs = System.currentTimeMillis() / 1000; + Long expiryTimeInSecs = claims.getExpiryTime(); + if (expiryTimeInSecs == null && validateClaimsAlways + || expiryTimeInSecs != null && currentTimeInSecs > expiryTimeInSecs) { + throw new SecurityException("The token expired"); + } + Long issuedAtInSecs = claims.getIssuedAt(); + if (issuedAtInSecs == null && validateClaimsAlways + || issuedAtInSecs != null && (issuedAtInSecs > currentTimeInSecs || issuedAtRange > 0 + && issuedAtInSecs < currentTimeInSecs - issuedAtRange)) { + throw new SecurityException("Invalid issuedAt"); + } + + } + + protected JwtToken getTokenValidateSignature(String wrappedJwtToken, String idTokenKid) { + // read id_token into JwtToken + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(wrappedJwtToken); + JwtToken jwt = jwtConsumer.getJwtToken(); + if (jwsVerifier != null) { + return validateToken(jwtConsumer, jwt, jwsVerifier); + } + if (jwkSetClient == null) { + 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); + if (key == null) { + JsonWebKeys keys = jwkSetClient.get(JsonWebKeys.class); + key = keys.getKey(keyId); + keyMap.putAll(keys.getKeyIdMap()); + } + if (key == null) { + throw new SecurityException("JWK key with the key id: \"" + keyId + "\" is not available"); + } + return validateToken(jwtConsumer, jwt, JwsUtils.getSignatureVerifier(key)); + } + protected JwtToken validateToken(JwsJwtCompactConsumer consumer, JwtToken jwt, JwsSignatureVerifier jws) { + if (consumer.verifySignatureWith(jws)) { + throw new SecurityException("Invalid Signature"); + } + return jwt; + } + public void setJweDecryptor(JweDecryptionProvider jweDecryptor) { + this.jweDecryptor = jweDecryptor; + } + + public void setJweVerifier(JwsSignatureVerifier theJwsVerifier) { + this.jwsVerifier = theJwsVerifier; + } + + public void setIssuerId(String issuerId) { + this.issuerId = issuerId; + } + + public void setJwkSetClient(WebClient jwkSetClient) { + this.jwkSetClient = jwkSetClient; + } + + public void setIssuedAtRange(int issuedAtRange) { + this.issuedAtRange = issuedAtRange; + } + + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/d8cf47fa/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/UserProfileValidator.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/UserProfileValidator.java b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/UserProfileValidator.java new file mode 100644 index 0000000..d1f6ffe --- /dev/null +++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/UserProfileValidator.java @@ -0,0 +1,66 @@ +/** + * 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 org.apache.cxf.jaxrs.client.WebClient; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.rs.security.oidc.common.UserIdToken; +import org.apache.cxf.rs.security.oidc.common.UserProfile; + +public class UserProfileValidator extends AbstractTokenValidator { + private boolean encryptedOnly; + + public UserProfile getProfile(WebClient profileClient, UserIdToken idToken) { + return getProfile(profileClient, idToken, false); + } + public UserProfile getProfile(WebClient profileClient, UserIdToken idToken, boolean asJwt) { + if (asJwt) { + String jwt = profileClient.get(String.class); + return getProfileFromJwt(jwt, idToken); + } else { + UserProfile profile = profileClient.get(UserProfile.class); + validateUserProfile(profile, idToken); + return profile; + } + + } + public UserProfile getProfileFromJwt(String profileJwtToken, UserIdToken idToken) { + JwtToken jwt = getProfileJwtToken(profileJwtToken, idToken); + return getProfileFromJwt(jwt, idToken); + } + public UserProfile getProfileFromJwt(JwtToken jwt, UserIdToken idToken) { + UserProfile profile = new UserProfile(jwt.getClaims().asMap()); + validateUserProfile(profile, idToken); + return profile; + } + public JwtToken getProfileJwtToken(String profileJwtToken, UserIdToken idToken) { + return getJwtToken(profileJwtToken, idToken.getAudience(), (String)idToken.getProperty("kid"), encryptedOnly); + } + public void validateUserProfile(UserProfile profile, UserIdToken idToken) { + validateJwtClaims(profile, idToken.getAudience(), false); + // validate subject + if (!idToken.getSubject().equals(profile.getSubject())) { + throw new SecurityException("Invalid subject"); + } + } + public void setEncryptedOnly(boolean encryptedOnly) { + this.encryptedOnly = encryptedOnly; + } + +}