diff --git a/edu.harvard.i2b2.pm/src/edu/harvard/i2b2/pm/delegate/ServicesHandler.java b/edu.harvard.i2b2.pm/src/edu/harvard/i2b2/pm/delegate/ServicesHandler.java index 79408c19..79562763 100755 --- a/edu.harvard.i2b2.pm/src/edu/harvard/i2b2/pm/delegate/ServicesHandler.java +++ b/edu.harvard.i2b2.pm/src/edu/harvard/i2b2/pm/delegate/ServicesHandler.java @@ -164,6 +164,8 @@ private UserType validateSuppliedPassword (String username, String password, Has classname = "edu.harvard.i2b2.pm.util.SecurityAuthenticationNTLM"; else if (param.get("authentication_method").equals("LDAP")) classname = "edu.harvard.i2b2.pm.util.SecurityAuthenticationLDAP"; + else if (param.get("authentication_method").equals("OIDC")) + classname = "edu.harvard.i2b2.pm.util.SecurityAuthenticationOIDC"; ClassLoader classLoader = ServicesHandler.class.getClassLoader(); diff --git a/edu.harvard.i2b2.pm/src/edu/harvard/i2b2/pm/util/SecurityAuthenticationOIDC.java b/edu.harvard.i2b2.pm/src/edu/harvard/i2b2/pm/util/SecurityAuthenticationOIDC.java new file mode 100755 index 00000000..9fa77f67 --- /dev/null +++ b/edu.harvard.i2b2.pm/src/edu/harvard/i2b2/pm/util/SecurityAuthenticationOIDC.java @@ -0,0 +1,98 @@ +/** + * Copyright 2017 - 2018 The Hyve B.V. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package edu.harvard.i2b2.pm.util; + +import com.auth0.jwk.*; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.interfaces.DecodedJWT; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.net.MalformedURLException; +import java.net.URL; +import java.security.interfaces.RSAPublicKey; +import java.util.Hashtable; + +/* + * OpenID Connect authentication for i2b2 v1.7.10 (might work with other versions, not tested). + * Supports JWT verification signed with RS256 and keys retrieved via JWKS. + * Token is passed through the password field of the XML. + * + * The parameters are listed below with their possible values in (): + * authentication_method - (OIDC) + * oidc_jwks_uri - () URI of JWKS to retrieve the public signing keys + * oidc_client_id - () client ID of this i2b2 instance + * oidc_user_field - () claim field containing the username + * oidc_token_issuer - () token issuer + * + * Some JWT fields are checked: https://openid.net/specs/openid-connect-core-1_0.html#ImplicitIDTValidation + * - audience must match the client ID + * - username must match i2b2 username + * - nonce check is made by the caller + */ +public class SecurityAuthenticationOIDC implements SecurityAuthentication { + + private static Log log = LogFactory.getLog(SecurityAuthenticationOIDC.class); + + private static JwkProvider jwkProvider = null; + private static String jwksUri = ""; + private static JwkProvider getJwkProvider(String pJwksUri) throws MalformedURLException { + if (!jwksUri.equals(pJwksUri) || jwkProvider == null) { + jwksUri = pJwksUri; + jwkProvider = new GuavaCachedJwkProvider(new UrlJwkProvider(new URL(jwksUri))); + } + return jwkProvider; + } + + /** + * @param username username provided via the i2b2 API headers + * @param tokenString JWT provided via the i2b2 API headers password field + * @param params i2b2 parameters containing OIDC configuration + * + * @return true if authentication valid + * @throws Exception if authentication not valid or any kind of error + */ + @Override + public boolean validateUser(String username, String tokenString, Hashtable params) throws Exception { + + try { + + String pJwksUri = (String) params.get("oidc_jwks_uri"), + pClientId = (String) params.get("oidc_client_id"), + pUserField = (String) params.get("oidc_user_field"), + pTokenIssuer = (String) params.get("oidc_token_issuer"); + + log.debug("validateUser() with jwks URI:" + pJwksUri); + + DecodedJWT jwt = com.auth0.jwt.JWT.decode(tokenString); + Jwk jwk = getJwkProvider(pJwksUri).get(jwt.getKeyId()); + RSAPublicKey signingPubKey = (RSAPublicKey) jwk.getPublicKey(); + + // verif algorithm and signing key + if (signingPubKey == null || !jwk.getAlgorithm().equals("RS256")) { + throw new Exception("Rejected authentication: Problematic public key = " + signingPubKey + " or algo = " + jwk.getAlgorithm()); + } + + // token validation + com.auth0.jwt.JWT + .require(Algorithm.RSA256(signingPubKey, null)) + .withIssuer(pTokenIssuer) // check issuer + .withAudience(pClientId) // check audience matches + .withClaim(pUserField, username) // check username matches i2b2 internal username + .build() + .verify(tokenString); // check time validity and signature + + } catch (Exception e) { + log.warn("Failed authentication: " + e.getMessage()); + throw new Exception("Token is invalid, please request a new one: " + e.getMessage(), e); + } + + log.debug("validateUser() validation successful"); + return true; + } +} diff --git a/edu.harvard.i2b2.server-common/build.xml b/edu.harvard.i2b2.server-common/build.xml index 69d4375c..b897268d 100755 --- a/edu.harvard.i2b2.server-common/build.xml +++ b/edu.harvard.i2b2.server-common/build.xml @@ -145,6 +145,7 @@ + diff --git a/edu.harvard.i2b2.server-common/lib/axis2-1.6.1/commons-codec-1.3.jar b/edu.harvard.i2b2.server-common/lib/axis2-1.6.1/commons-codec-1.3.jar deleted file mode 100755 index 957b6752..00000000 Binary files a/edu.harvard.i2b2.server-common/lib/axis2-1.6.1/commons-codec-1.3.jar and /dev/null differ diff --git a/edu.harvard.i2b2.server-common/lib/commons/commons-codec-1.11.jar b/edu.harvard.i2b2.server-common/lib/commons/commons-codec-1.11.jar new file mode 100644 index 00000000..22451206 Binary files /dev/null and b/edu.harvard.i2b2.server-common/lib/commons/commons-codec-1.11.jar differ diff --git a/edu.harvard.i2b2.server-common/lib/commons/commons-codec-1.3.jar b/edu.harvard.i2b2.server-common/lib/commons/commons-codec-1.3.jar deleted file mode 100755 index 957b6752..00000000 Binary files a/edu.harvard.i2b2.server-common/lib/commons/commons-codec-1.3.jar and /dev/null differ diff --git a/edu.harvard.i2b2.server-common/lib/commons/commons-io-2.6.jar b/edu.harvard.i2b2.server-common/lib/commons/commons-io-2.6.jar new file mode 100644 index 00000000..00556b11 Binary files /dev/null and b/edu.harvard.i2b2.server-common/lib/commons/commons-io-2.6.jar differ diff --git a/edu.harvard.i2b2.server-common/lib/oidc/animal-sniffer-annotations-1.16.jar b/edu.harvard.i2b2.server-common/lib/oidc/animal-sniffer-annotations-1.16.jar new file mode 100644 index 00000000..df878fa1 Binary files /dev/null and b/edu.harvard.i2b2.server-common/lib/oidc/animal-sniffer-annotations-1.16.jar differ diff --git a/edu.harvard.i2b2.server-common/lib/oidc/checker-compat-qual-2.5.0.jar b/edu.harvard.i2b2.server-common/lib/oidc/checker-compat-qual-2.5.0.jar new file mode 100644 index 00000000..f3b5a8bf Binary files /dev/null and b/edu.harvard.i2b2.server-common/lib/oidc/checker-compat-qual-2.5.0.jar differ diff --git a/edu.harvard.i2b2.server-common/lib/oidc/error_prone_annotations-2.3.1.jar b/edu.harvard.i2b2.server-common/lib/oidc/error_prone_annotations-2.3.1.jar new file mode 100644 index 00000000..8a0efa37 Binary files /dev/null and b/edu.harvard.i2b2.server-common/lib/oidc/error_prone_annotations-2.3.1.jar differ diff --git a/edu.harvard.i2b2.server-common/lib/oidc/guava-24.1-jre.jar b/edu.harvard.i2b2.server-common/lib/oidc/guava-24.1-jre.jar new file mode 100644 index 00000000..5f2c693d Binary files /dev/null and b/edu.harvard.i2b2.server-common/lib/oidc/guava-24.1-jre.jar differ diff --git a/edu.harvard.i2b2.server-common/lib/oidc/j2objc-annotations-1.3.jar b/edu.harvard.i2b2.server-common/lib/oidc/j2objc-annotations-1.3.jar new file mode 100644 index 00000000..a429c721 Binary files /dev/null and b/edu.harvard.i2b2.server-common/lib/oidc/j2objc-annotations-1.3.jar differ diff --git a/edu.harvard.i2b2.server-common/lib/oidc/jackson-annotations-2.9.6.jar b/edu.harvard.i2b2.server-common/lib/oidc/jackson-annotations-2.9.6.jar new file mode 100644 index 00000000..51412a57 Binary files /dev/null and b/edu.harvard.i2b2.server-common/lib/oidc/jackson-annotations-2.9.6.jar differ diff --git a/edu.harvard.i2b2.server-common/lib/oidc/jackson-core-2.9.6.jar b/edu.harvard.i2b2.server-common/lib/oidc/jackson-core-2.9.6.jar new file mode 100644 index 00000000..09e7dd20 Binary files /dev/null and b/edu.harvard.i2b2.server-common/lib/oidc/jackson-core-2.9.6.jar differ diff --git a/edu.harvard.i2b2.server-common/lib/oidc/jackson-databind-2.9.6.jar b/edu.harvard.i2b2.server-common/lib/oidc/jackson-databind-2.9.6.jar new file mode 100644 index 00000000..e8eb6586 Binary files /dev/null and b/edu.harvard.i2b2.server-common/lib/oidc/jackson-databind-2.9.6.jar differ diff --git a/edu.harvard.i2b2.server-common/lib/oidc/java-jwt-3.4.0.jar b/edu.harvard.i2b2.server-common/lib/oidc/java-jwt-3.4.0.jar new file mode 100644 index 00000000..4785c3f8 Binary files /dev/null and b/edu.harvard.i2b2.server-common/lib/oidc/java-jwt-3.4.0.jar differ diff --git a/edu.harvard.i2b2.server-common/lib/oidc/jsr305-3.0.2.jar b/edu.harvard.i2b2.server-common/lib/oidc/jsr305-3.0.2.jar new file mode 100644 index 00000000..59222d9c Binary files /dev/null and b/edu.harvard.i2b2.server-common/lib/oidc/jsr305-3.0.2.jar differ diff --git a/edu.harvard.i2b2.server-common/lib/oidc/jwks-rsa-0.6.0.jar b/edu.harvard.i2b2.server-common/lib/oidc/jwks-rsa-0.6.0.jar new file mode 100644 index 00000000..35f3f667 Binary files /dev/null and b/edu.harvard.i2b2.server-common/lib/oidc/jwks-rsa-0.6.0.jar differ