cxf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From serg...@apache.org
Subject cxf git commit: [CXF-5607] Adding some more OIDC utility code
Date Wed, 12 Nov 2014 17:29:34 GMT
Repository: cxf
Updated Branches:
  refs/heads/master f675e3158 -> 4ff4d39b0


[CXF-5607] Adding some more OIDC utility code


Project: http://git-wip-us.apache.org/repos/asf/cxf/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/4ff4d39b
Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/4ff4d39b
Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/4ff4d39b

Branch: refs/heads/master
Commit: 4ff4d39b076281ac5ef193b72a77db9d5d7755f3
Parents: f675e31
Author: Sergey Beryozkin <sberyozkin@talend.com>
Authored: Wed Nov 12 17:29:13 2014 +0000
Committer: Sergey Beryozkin <sberyozkin@talend.com>
Committed: Wed Nov 12 17:29:13 2014 +0000

----------------------------------------------------------------------
 .../cxf/jaxrs/provider/json/JsonMapObject.java  |   8 +
 .../cxf/rs/security/jose/jwt/JwtToken.java      |   6 +
 .../oauth2/filters/OAuthRequestFilter.java      |  26 ++-
 .../rs/security/oidc/common/UserAddress.java    |  55 ++++++
 .../rs/security/oidc/common/UserIdToken.java    |  61 ++++++
 .../rs/security/oidc/common/UserProfile.java    |  71 ++++++-
 .../rs/security/oidc/rp/IdTokenValidator.java   | 121 ------------
 .../cxf/rs/security/oidc/rp/OidcUtils.java      |  15 +-
 .../cxf/rs/security/oidc/rp/TokenValidator.java | 190 +++++++++++++++++++
 9 files changed, 423 insertions(+), 130 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf/blob/4ff4d39b/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/json/JsonMapObject.java
----------------------------------------------------------------------
diff --git a/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/json/JsonMapObject.java
b/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/json/JsonMapObject.java
index 3001833..cfc98f2 100644
--- a/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/json/JsonMapObject.java
+++ b/rt/rs/extensions/providers/src/main/java/org/apache/cxf/jaxrs/provider/json/JsonMapObject.java
@@ -69,6 +69,14 @@ public class JsonMapObject {
             return null;
         }
     }
+    public Boolean getBooleanProperty(String name) {
+        Object value = getProperty(name);
+        if (value != null) {
+            return value instanceof Boolean ? (Boolean)value : Boolean.parseBoolean(value.toString());
+        } else {
+            return null;
+        }
+    }
     
     public int hashCode() { 
         return values.hashCode();

http://git-wip-us.apache.org/repos/asf/cxf/blob/4ff4d39b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtToken.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtToken.java
b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtToken.java
index e22e0ef..6a55854 100644
--- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtToken.java
+++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtToken.java
@@ -35,6 +35,12 @@ public class JwtToken {
     public JwtClaims getClaims() {
         return claims;
     }
+    public Object getHeader(String name) {
+        return headers.getHeader(name);
+    }
+    public Object getClaim(String name) {
+        return claims.getClaim(name);
+    }
     public int hashCode() { 
         return headers.hashCode() + 37 * claims.hashCode();
     }

http://git-wip-us.apache.org/repos/asf/cxf/blob/4ff4d39b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java
b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java
index 8f3f5d8..fb63639 100644
--- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java
+++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java
@@ -20,6 +20,7 @@ package org.apache.cxf.rs.security.oauth2.filters;
 
 import java.security.Principal;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.logging.Logger;
 
@@ -64,6 +65,8 @@ public class OAuthRequestFilter extends AbstractAccessTokenValidator
     private boolean useUserSubject;
     private boolean audienceIsEndpointAddress;
     private boolean checkFormData;
+    private List<String> requiredScopes = Collections.emptyList();
+    private boolean allPermissionsMatch;
     
     public void filter(ContainerRequestContext context) {
         validateRequest(JAXRSUtils.getCurrentMessage());
@@ -93,12 +96,15 @@ public class OAuthRequestFilter extends AbstractAccessTokenValidator
         for (OAuthPermission perm : permissions) {
             boolean uriOK = checkRequestURI(req, perm.getUris());
             boolean verbOK = checkHttpVerb(req, perm.getHttpVerbs());
-            if (uriOK && verbOK) {
+            boolean scopeOk = checkScopeProperty(perm.getPermission()); 
+            if (uriOK && verbOK && scopeOk) {
                 matchingPermissions.add(perm);
             }
         }
         
-        if (permissions.size() > 0 && matchingPermissions.isEmpty()) {
+        if (permissions.size() > 0 && matchingPermissions.isEmpty() 
+            || allPermissionsMatch && (matchingPermissions.size() != permissions.size())
+            || !requiredScopes.isEmpty() && requiredScopes.size() != matchingPermissions.size())
{
             String message = "Client has no valid permissions";
             LOG.warning(message);
             throw new WebApplicationException(403);
@@ -150,7 +156,13 @@ public class OAuthRequestFilter extends AbstractAccessTokenValidator
         }
         return foundValidScope;
     }
-    
+    protected boolean checkScopeProperty(String scope) {
+        if (!requiredScopes.isEmpty()) {
+            return requiredScopes.contains(scope);
+        } else {
+            return true;
+        }
+    }
     public void setUseUserSubject(boolean useUserSubject) {
         this.useUserSubject = useUserSubject;
     }
@@ -239,5 +251,13 @@ public class OAuthRequestFilter extends AbstractAccessTokenValidator
         AuthorizationUtils.throwAuthorizationFailure(supportedSchemes, realm);
         return null;
     }
+
+    public void setRequiredScopes(List<String> requiredScopes) {
+        this.requiredScopes = requiredScopes;
+    }
+
+    public void setAllPermissionsMatch(boolean allPermissionsMatch) {
+        this.allPermissionsMatch = allPermissionsMatch;
+    }
     
 }

http://git-wip-us.apache.org/repos/asf/cxf/blob/4ff4d39b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/common/UserAddress.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/common/UserAddress.java
b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/common/UserAddress.java
new file mode 100644
index 0000000..46c4d96
--- /dev/null
+++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/common/UserAddress.java
@@ -0,0 +1,55 @@
+/**
+ * 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.common;
+
+import java.util.Map;
+
+import org.apache.cxf.jaxrs.provider.json.JsonMapObject;
+
+public class UserAddress extends JsonMapObject {
+    public static final String STREET = "street_address";
+    public static final String LOCALITY = "locality";
+    public static final String COUNTRY = "country";
+    
+    public UserAddress() {
+    }
+    
+    public UserAddress(Map<String, Object> claims) {
+        super(claims);
+    }
+    
+    public void setStreet(String name) {
+        setProperty(STREET, name);
+    }
+    public String getStreet() {
+        return (String)getProperty(STREET);
+    }
+    public void setLocality(String name) {
+        setProperty(LOCALITY, name);
+    }
+    public String getLocality() {
+        return (String)getProperty(LOCALITY);
+    }
+    public void setCountry(String name) {
+        setProperty(COUNTRY, name);
+    }
+    public String getCountry() {
+        return (String)getProperty(COUNTRY);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/4ff4d39b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/common/UserIdToken.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/common/UserIdToken.java
b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/common/UserIdToken.java
new file mode 100644
index 0000000..7db7991
--- /dev/null
+++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/common/UserIdToken.java
@@ -0,0 +1,61 @@
+/**
+ * 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.common;
+
+import java.util.Map;
+
+import org.apache.cxf.rs.security.jose.jwt.JwtClaims;
+
+public class UserIdToken extends JwtClaims {
+    public static final String AUTH_TIME_CLAIM = "auth_time";
+    public static final String NONCE_CLAIM = "nonce";
+    public static final String ACR_CLAIM = "acr";
+    public static final String AZP_CLAIM = "azp";
+    
+    public UserIdToken() {
+    }
+    
+    public UserIdToken(Map<String, Object> claims) {
+        super(claims);
+    }
+    public void setAuthenticationTime(Long time) {
+        setProperty(AUTH_TIME_CLAIM, time);
+    }
+    public Long getAuthenticationTime() {
+        return getLongProperty(AUTH_TIME_CLAIM);
+    }
+    public void setNonce(String nonce) {
+        setProperty(NONCE_CLAIM, nonce);
+    }
+    public String getNonce() {
+        return (String)getProperty(NONCE_CLAIM);
+    }
+    public void setAuthenticationContextRef(String ref) {
+        setProperty(ACR_CLAIM, ref);
+    }
+    public String getAuthenticationContextRef() {
+        return (String)getProperty(ACR_CLAIM);
+    }
+    public void setAuthorizedParty(String azp) {
+        setProperty(AZP_CLAIM, azp);
+    }
+    public String getAuthorizedParty() {
+        return (String)getProperty(AZP_CLAIM);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/4ff4d39b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/common/UserProfile.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/common/UserProfile.java
b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/common/UserProfile.java
index 9f519cf..944c399 100644
--- a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/common/UserProfile.java
+++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/common/UserProfile.java
@@ -18,8 +18,75 @@
  */
 package org.apache.cxf.rs.security.oidc.common;
 
-import org.apache.cxf.jaxrs.provider.json.JsonMapObject;
+import java.util.Map;
 
-public class UserProfile extends JsonMapObject {
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.rs.security.jose.jwt.JwtClaims;
 
+public class UserProfile extends JwtClaims {
+    public static final String NAME_CLAIM = "name";
+    public static final String PROFILE_CLAIM = "profile";
+    public static final String EMAIL_CLAIM = "email";
+    public static final String EMAIL_VERIFIED_CLAIM = "email_verified";
+    public static final String BIRTHDATE_CLAIM = "birthdate";
+    public static final String PHONE_CLAIM = "phone_number";
+    public static final String ADDRESS_CLAIM = "address";
+    public UserProfile() {
+    }
+    
+    public UserProfile(Map<String, Object> claims) {
+        super(claims);
+    }
+    
+    public void setName(String name) {
+        setProperty(NAME_CLAIM, name);
+    }
+    public String getName() {
+        return (String)getProperty(NAME_CLAIM);
+    }
+    public void setProfile(String name) {
+        setProperty(PROFILE_CLAIM, name);
+    }
+    public String getProfile() {
+        return (String)getProperty(PROFILE_CLAIM);
+    }
+    public void setEmail(String name) {
+        setProperty(EMAIL_CLAIM, name);
+    }
+    public String getEmail() {
+        return (String)getProperty(EMAIL_CLAIM);
+    }
+    public void setEmailVerified(Boolean verified) {
+        setProperty(EMAIL_VERIFIED_CLAIM, verified);
+    }
+    public Boolean getEmailVerified() {
+        return getBooleanProperty(EMAIL_VERIFIED_CLAIM);
+    }
+    public void setBirthDate(String date) {
+        setProperty(BIRTHDATE_CLAIM, date);
+    }
+    public String getBirthdate() {
+        return (String)getProperty(BIRTHDATE_CLAIM);
+    }
+    public String getPhoneNumber() {
+        return (String)getProperty(PHONE_CLAIM);
+    }
+    public void setPhoneNumber(String name) {
+        setProperty(PHONE_CLAIM, name);
+    }
+    public UserAddress getUserAddress() {
+        Object value = getProperty(ADDRESS_CLAIM);
+        if (value instanceof UserAddress) {
+            return (UserAddress)value;
+        } else if (value instanceof Map) {
+            Map<String, Object> map = CastUtils.cast((Map<?, ?>)value); 
+            return new UserAddress(map);
+        } else {
+            return null;
+        }
+    }
+    public void setUserAddressNumber(UserAddress address) {
+        setProperty(ADDRESS_CLAIM, address);
+    }
+    
 }

http://git-wip-us.apache.org/repos/asf/cxf/blob/4ff4d39b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/IdTokenValidator.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/IdTokenValidator.java
b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/IdTokenValidator.java
deleted file mode 100644
index eb16698..0000000
--- a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/IdTokenValidator.java
+++ /dev/null
@@ -1,121 +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;
-
-import org.apache.cxf.jaxrs.client.WebClient;
-import org.apache.cxf.rs.security.jose.jwe.JweDecryptionProvider;
-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.JwtToken;
-import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken;
-
-public class IdTokenValidator {
-    private JweDecryptionProvider jweDecryptor;
-    private JwsSignatureVerifier jwsVerifier;
-    private String issuerId;
-    private int issuedAtRange;
-
-    private WebClient jwkSetClient;
-
-    public JwtToken validateIdToken(ClientAccessToken at, String clientId) {
-        String idToken = at.getParameters().get("id_token");
-        if (idToken == null) {
-            throw new SecurityException("ID Token is missing");
-        }
-        // Decrypt the token if needed
-        if (jweDecryptor != null) {
-            idToken = new String(jweDecryptor.decrypt(idToken).getContentText());
-        }
-
-        // read id_token into JwtToken
-        JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(idToken);
-        JwtToken jwt = jwtConsumer.getJwtToken();
-        System.out.println("JWT claims" + jwtConsumer.getDecodedJsonToken().getClaimsJson());
-        // validate token signature
-        JwsSignatureVerifier theJwsVerifier = loadJwkSignatureVerifier(jwt);
-        if (!jwtConsumer.verifySignatureWith(theJwsVerifier)) {
-            throw new SecurityException("ID Token signature verification failed");
-        }
-
-        // validate audience
-        if (!clientId.equals(jwt.getClaims().getAudience())) {
-            throw new SecurityException("Invalid audience");
-        }
-
-        // validate the provider
-        if (!issuerId.equals(jwt.getClaims().getIssuer())) {
-            throw new SecurityException("Invalid provider");
-        }
-        long currentTimeInSecs = System.currentTimeMillis() / 1000;
-        long expiryTimeInSecs = jwt.getClaims().getExpiryTime();
-        if (currentTimeInSecs > expiryTimeInSecs) {
-            throw new SecurityException("The token expired");
-        }
-        long issuedAtInSecs = jwt.getClaims().getIssuedAt();
-        if (issuedAtInSecs > currentTimeInSecs || issuedAtRange > 0
-            && issuedAtInSecs < currentTimeInSecs - issuedAtRange) {
-            throw new SecurityException("Invalid issuedAt");
-        }
-        // validate at_hash
-        OidcUtils.validateAccessTokenHash(at, jwt);
-        return jwt;
-    }
-
-    private JwsSignatureVerifier loadJwkSignatureVerifier(JwtToken jwt) {
-        if (jwsVerifier != null) {
-            return jwsVerifier;
-        }
-        if (jwkSetClient == null) {
-            throw new SecurityException("Provider Jwk Set Client is not available");
-        }
-        String keyId = jwt.getHeaders().getKeyId();
-        if (keyId == null) {
-            throw new SecurityException("Provider JWK key id is null");
-        }
-        JsonWebKeys keys = jwkSetClient.get(JsonWebKeys.class);
-        JsonWebKey key = keys.getKey(keyId);
-        if (key == null) {
-            throw new SecurityException("JWK key with the key id: \"" + keyId + "\" is not
available");
-        }
-        return JwsUtils.getSignatureVerifier(key);
-    }
-
-    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/4ff4d39b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcUtils.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcUtils.java
b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcUtils.java
index 0a2e71d..10ece56 100644
--- a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcUtils.java
+++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcUtils.java
@@ -30,18 +30,25 @@ public final class OidcUtils {
     private OidcUtils() {
         
     }
-    
     public static void validateAccessTokenHash(ClientAccessToken at, JwtToken jwt) {
+        validateAccessTokenHash(at, jwt, true);
+    }
+    public static void validateAccessTokenHash(ClientAccessToken at, JwtToken jwt, boolean
required) {
         validateHash(at.getTokenKey(),
                      (String)jwt.getClaims().getClaim("at_hash"),
-                     jwt.getHeaders().getAlgorithm());
+                     jwt.getHeaders().getAlgorithm(),
+                     required);
     }
     public static void validateCodeHash(String code, JwtToken jwt) {
+        validateCodeHash(code, jwt, true);
+    }
+    public static void validateCodeHash(String code, JwtToken jwt, boolean required) {
         validateHash(code,
                      (String)jwt.getClaims().getClaim("c_hash"),
-                     jwt.getHeaders().getAlgorithm());
+                     jwt.getHeaders().getAlgorithm(),
+                     required);
     }
-    private static void validateHash(String value, String theHash, String joseAlgo) {
+    private static void validateHash(String value, String theHash, String joseAlgo, boolean
required) {
         String hash = calculateHash(value, joseAlgo);
         if (!hash.equals(theHash)) {
             throw new SecurityException("Invalid hash");

http://git-wip-us.apache.org/repos/asf/cxf/blob/4ff4d39b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/TokenValidator.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/TokenValidator.java
b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/TokenValidator.java
new file mode 100644
index 0000000..483059e
--- /dev/null
+++ b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/TokenValidator.java
@@ -0,0 +1,190 @@
+/**
+ * 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.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;
+import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken;
+import org.apache.cxf.rs.security.oidc.common.UserIdToken;
+import org.apache.cxf.rs.security.oidc.common.UserProfile;
+
+public class TokenValidator {
+    private JweDecryptionProvider jweDecryptor;
+    private JwsSignatureVerifier jwsVerifier;
+    private String issuerId;
+    private int issuedAtRange;
+    private boolean requireAtHash = true;
+    private WebClient jwkSetClient;
+    private ConcurrentHashMap<String, JsonWebKey> keyMap = new ConcurrentHashMap<String,
JsonWebKey>(); 
+    
+    public UserIdToken getIdTokenFromJwt(ClientAccessToken at, String clientId) {
+        JwtToken jwt = getIdJwtToken(at, clientId);
+        return getIdTokenFromJwt(jwt, clientId);
+    }
+    public UserIdToken getIdTokenFromJwt(JwtToken jwt, String clientId) {
+        //TODO: do the extra validation if needed
+        return new UserIdToken(jwt.getClaims().asMap());
+    }
+    public JwtToken getIdJwtToken(ClientAccessToken at, String clientId) {
+        String idJwtToken = at.getParameters().get("id_token");
+        JwtToken jwt = getJwtToken(idJwtToken, clientId);
+        validateJwtClaims(jwt.getClaims(), clientId, true);
+        OidcUtils.validateAccessTokenHash(at, jwt, requireAtHash);
+        return jwt;
+    }
+    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());
+    }
+    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 JwtToken getJwtToken(String wrappedJwtToken, String clientId) {
+        if (wrappedJwtToken == null) {
+            throw new SecurityException("ID Token is missing");
+        }
+        // Decrypt the token if needed
+        if (jweDecryptor != null) {
+            wrappedJwtToken = jweDecryptor.decrypt(wrappedJwtToken).getContentText();
+        }
+
+        // read id_token into JwtToken
+        JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(wrappedJwtToken);
+        JwtToken jwt = jwtConsumer.getJwtToken();
+        // validate token signature
+        JwsSignatureVerifier theJwsVerifier = loadJwkSignatureVerifier(jwt);
+        if (!jwtConsumer.verifySignatureWith(theJwsVerifier)) {
+            throw new SecurityException("ID Token signature verification failed");
+        }
+        return jwt;
+    }
+    
+    private 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");
+        }
+        
+    }
+    
+    private JwsSignatureVerifier loadJwkSignatureVerifier(JwtToken jwt) {
+        if (jwsVerifier != null) {
+            return jwsVerifier;
+        }
+        if (jwkSetClient == null) {
+            throw new SecurityException("Provider Jwk Set Client is not available");
+        }
+        String keyId = jwt.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.putIfAbsent(keyId, key);
+        }
+        if (key == null) {
+            throw new SecurityException("JWK key with the key id: \"" + keyId + "\" is not
available");
+        }
+        return JwsUtils.getSignatureVerifier(key);
+    }
+
+    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;
+    }
+
+    public void setRequireAtHash(boolean requireAtHash) {
+        this.requireAtHash = requireAtHash;
+    }
+}


Mime
View raw message