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);
+ }
+
+ }
+
+}