You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@guacamole.apache.org by vn...@apache.org on 2017/09/27 02:18:05 UTC

[13/29] incubator-guacamole-client git commit: GUACAMOLE-210: Validate the JWT using jose.4.j.

GUACAMOLE-210: Validate the JWT using jose.4.j.


Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/d27ba444
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/d27ba444
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/d27ba444

Branch: refs/heads/master
Commit: d27ba44439e702964cb668886ccbc35f740b38e8
Parents: fdc0313
Author: Michael Jumper <mj...@apache.org>
Authored: Sun Jun 12 23:03:47 2016 -0700
Committer: Michael Jumper <mj...@apache.org>
Committed: Mon Sep 25 13:06:43 2017 -0700

----------------------------------------------------------------------
 extensions/guacamole-auth-openid/pom.xml        |   7 ++
 .../oauth/AuthenticationProviderService.java    |  13 ++-
 .../OAuthAuthenticationProviderModule.java      |   2 +
 .../auth/oauth/conf/ConfigurationService.java   |  52 +++++++++-
 .../oauth/conf/OAuthGuacamoleProperties.java    |  35 +++++++
 .../oauth/token/TokenValidationService.java     | 102 +++++++++++++++++++
 6 files changed, 207 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d27ba444/extensions/guacamole-auth-openid/pom.xml
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-openid/pom.xml b/extensions/guacamole-auth-openid/pom.xml
index 60691e2..fa819c8 100644
--- a/extensions/guacamole-auth-openid/pom.xml
+++ b/extensions/guacamole-auth-openid/pom.xml
@@ -86,6 +86,13 @@
             <scope>provided</scope>
         </dependency>
 
+        <!-- Java implementation of JOSE (jose.4.j) -->
+        <dependency>
+            <groupId>org.bitbucket.b_c</groupId>
+            <artifactId>jose4j</artifactId>
+            <version>0.5.1</version>
+        </dependency>
+
         <!-- Guice -->
         <dependency>
             <groupId>com.google.inject</groupId>

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d27ba444/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/AuthenticationProviderService.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/AuthenticationProviderService.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/AuthenticationProviderService.java
index 0aac968..d89f087 100644
--- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/AuthenticationProviderService.java
+++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/AuthenticationProviderService.java
@@ -23,9 +23,10 @@ import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.util.Arrays;
 import javax.servlet.http.HttpServletRequest;
-import org.apache.guacamole.auth.oauth.user.AuthenticatedUser;
 import org.apache.guacamole.auth.oauth.conf.ConfigurationService;
 import org.apache.guacamole.auth.oauth.form.OAuthTokenField;
+import org.apache.guacamole.auth.oauth.token.TokenValidationService;
+import org.apache.guacamole.auth.oauth.user.AuthenticatedUser;
 import org.apache.guacamole.GuacamoleException;
 import org.apache.guacamole.form.Field;
 import org.apache.guacamole.net.auth.Credentials;
@@ -52,6 +53,12 @@ public class AuthenticationProviderService {
     private ConfigurationService confService;
 
     /**
+     * Service for validating received ID tokens.
+     */
+    @Inject
+    private TokenValidationService tokenService;
+
+    /**
      * Provider for AuthenticatedUser objects.
      */
     @Inject
@@ -82,12 +89,12 @@ public class AuthenticationProviderService {
         if (request != null)
             token = request.getParameter(OAuthTokenField.PARAMETER_NAME);
 
-        // TODO: Actually validate received token
+        // If token provided, validate and produce authenticated user
         if (token != null) {
 
             // Create corresponding authenticated user
             AuthenticatedUser authenticatedUser = authenticatedUserProvider.get();
-            authenticatedUser.init("STUB", credentials);
+            authenticatedUser.init(tokenService.processUsername(token), credentials);
             return authenticatedUser;
 
         }

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d27ba444/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthAuthenticationProviderModule.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthAuthenticationProviderModule.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthAuthenticationProviderModule.java
index 202e6a2..f838063 100644
--- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthAuthenticationProviderModule.java
+++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/OAuthAuthenticationProviderModule.java
@@ -21,6 +21,7 @@ package org.apache.guacamole.auth.oauth;
 
 import com.google.inject.AbstractModule;
 import org.apache.guacamole.auth.oauth.conf.ConfigurationService;
+import org.apache.guacamole.auth.oauth.token.TokenValidationService;
 import org.apache.guacamole.GuacamoleException;
 import org.apache.guacamole.environment.Environment;
 import org.apache.guacamole.environment.LocalEnvironment;
@@ -73,6 +74,7 @@ public class OAuthAuthenticationProviderModule extends AbstractModule {
 
         // Bind OAuth-specific services
         bind(ConfigurationService.class);
+        bind(TokenValidationService.class);
 
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d27ba444/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/conf/ConfigurationService.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/conf/ConfigurationService.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/conf/ConfigurationService.java
index 9debab7..1304d58 100644
--- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/conf/ConfigurationService.java
+++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/conf/ConfigurationService.java
@@ -79,11 +79,61 @@ public class ConfigurationService {
      *     as configured with guacamole.properties.
      *
      * @throws GuacamoleException
-     *     If guacamole.properties cannot be parsed, or if the client secret
+     *     If guacamole.properties cannot be parsed, or if the redirect URI
      *     property is missing.
      */
     public String getRedirectURI() throws GuacamoleException {
         return environment.getRequiredProperty(OAuthGuacamoleProperties.OAUTH_REDIRECT_URI);
     }
 
+    /**
+     * Returns the issuer to expect for all received ID tokens, as configured
+     * with guacamole.properties.
+     *
+     * @return
+     *     The issuer to expect for all received ID tokens, as configured with
+     *     guacamole.properties.
+     *
+     * @throws GuacamoleException
+     *     If guacamole.properties cannot be parsed, or if the issuer property
+     *     is missing.
+     */
+    public String getIssuer() throws GuacamoleException {
+        return environment.getRequiredProperty(OAuthGuacamoleProperties.OAUTH_ISSUER);
+    }
+
+    /**
+     * Returns the endpoint (URI) of the JWKS service which defines how
+     * received ID tokens (JWTs) shall be validated, as configured with
+     * guacamole.properties.
+     *
+     * @return
+     *     The endpoint (URI) of the JWKS service which defines how received ID
+     *     tokens (JWTs) shall be validated, as configured with
+     *     guacamole.properties.
+     *
+     * @throws GuacamoleException
+     *     If guacamole.properties cannot be parsed, or if the JWKS endpoint
+     *     property is missing.
+     */
+    public String getJWKSEndpoint() throws GuacamoleException {
+        return environment.getRequiredProperty(OAuthGuacamoleProperties.OAUTH_JWKS_ENDPOINT);
+    }
+
+    /**
+     * Returns the claim type which contains the authenticated user's username
+     * within any valid JWT, as configured with guacamole.properties.
+     *
+     * @return
+     *     The claim type which contains the authenticated user's username
+     *     within any valid JWT, as configured with guacamole.properties.
+     *
+     * @throws GuacamoleException
+     *     If guacamole.properties cannot be parsed, or if the username claim
+     *     type property is missing.
+     */
+    public String getUsernameClaimType() throws GuacamoleException {
+        return environment.getRequiredProperty(OAuthGuacamoleProperties.OAUTH_USERNAME_CLAIM_TYPE);
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d27ba444/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/conf/OAuthGuacamoleProperties.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/conf/OAuthGuacamoleProperties.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/conf/OAuthGuacamoleProperties.java
index 34952fe..cfb4eb3 100644
--- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/conf/OAuthGuacamoleProperties.java
+++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/conf/OAuthGuacamoleProperties.java
@@ -45,6 +45,41 @@ public class OAuthGuacamoleProperties {
     };
 
     /**
+     * The endpoint (URI) of the JWKS service which defines how received ID
+     * tokens (JWTs) shall be validated.
+     */
+    public static final StringGuacamoleProperty OAUTH_JWKS_ENDPOINT =
+            new StringGuacamoleProperty() {
+
+        @Override
+        public String getName() { return "oauth-jwks-endpoint"; }
+
+    };
+
+    /**
+     * The issuer to expect for all received ID tokens.
+     */
+    public static final StringGuacamoleProperty OAUTH_ISSUER =
+            new StringGuacamoleProperty() {
+
+        @Override
+        public String getName() { return "oauth-issuer"; }
+
+    };
+
+    /**
+     * The claim type which contains the authenticated user's username within
+     * any valid JWT.
+     */
+    public static final StringGuacamoleProperty OAUTH_USERNAME_CLAIM_TYPE =
+            new StringGuacamoleProperty() {
+
+        @Override
+        public String getName() { return "oauth-username-claim-type"; }
+
+    };
+
+    /**
      * OAuth client ID which should be submitted to the OAuth service when
      * necessary. This value is typically provided by the OAuth service when
      * OAuth credentials are generated for your application.

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/d27ba444/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenValidationService.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenValidationService.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenValidationService.java
new file mode 100644
index 0000000..a61f7ce
--- /dev/null
+++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/oauth/token/TokenValidationService.java
@@ -0,0 +1,102 @@
+/*
+ * 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.guacamole.auth.oauth.token;
+
+import com.google.inject.Inject;
+import org.apache.guacamole.auth.oauth.conf.ConfigurationService;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleSecurityException;
+import org.apache.guacamole.GuacamoleServerException;
+import org.jose4j.jwk.HttpsJwks;
+import org.jose4j.jwt.JwtClaims;
+import org.jose4j.jwt.MalformedClaimException;
+import org.jose4j.jwt.consumer.InvalidJwtException;
+import org.jose4j.jwt.consumer.JwtConsumer;
+import org.jose4j.jwt.consumer.JwtConsumerBuilder;
+import org.jose4j.keys.resolvers.HttpsJwksVerificationKeyResolver;
+
+/**
+ * Service for validating ID tokens forwarded to us by the client, verifying
+ * that they did indeed come from the OAuth service.
+ */
+public class TokenValidationService {
+
+    @Inject
+    private ConfigurationService confService;
+
+    /**
+     * Validates and parses the given ID token, returning the username contained
+     * therein, as defined by the username claim type given in
+     * guacamole.properties. If the username claim type is missing or the ID
+     * token is invalid, an exception is thrown instead.
+     *
+     * @param token
+     *     The ID token to validate and parse.
+     *
+     * @return
+     *     The username contained within the given ID token.
+     *
+     * @throws GuacamoleException
+     *     If the ID token is not valid, the username claim type is missing, or
+     *     guacamole.properties could not be parsed.
+     */
+    public String processUsername(String token) throws GuacamoleException {
+
+        // Validating the token requires a JWKS key resolver
+        HttpsJwks jwks = new HttpsJwks(confService.getJWKSEndpoint());
+        HttpsJwksVerificationKeyResolver resolver = new HttpsJwksVerificationKeyResolver(jwks);
+
+        // Create JWT consumer for validating received token
+        JwtConsumer jwtConsumer = new JwtConsumerBuilder()
+                .setRequireExpirationTime()
+                .setMaxFutureValidityInMinutes(300)
+                .setAllowedClockSkewInSeconds(30)
+                .setRequireSubject()
+                .setExpectedIssuer(confService.getIssuer())
+                .setExpectedAudience(confService.getClientID())
+                .setVerificationKeyResolver(resolver)
+                .build();
+
+        try {
+
+            // Validate JWT
+            JwtClaims claims = jwtConsumer.processToClaims(token);
+
+            // Pull username from claims
+            String username = claims.getStringClaimValue(confService.getUsernameClaimType());
+            if (username == null)
+                throw new GuacamoleSecurityException("Username missing from token");
+
+            // Username successfully retrieved from the JWT
+            return username;
+
+        }
+
+        // Rethrow any failures to validate/parse the JWT
+        catch (InvalidJwtException e) {
+            throw new GuacamoleSecurityException("Invalid ID token.", e);
+        }
+        catch (MalformedClaimException e) {
+            throw new GuacamoleServerException("Unable to parse JWT claims.", e);
+        }
+
+    }
+
+}