diff --git a/fhir-parent/pom.xml b/fhir-parent/pom.xml
index b3262e358c6..e8375f452c7 100644
--- a/fhir-parent/pom.xml
+++ b/fhir-parent/pom.xml
@@ -415,7 +415,8 @@
com.auth0
java-jwt
- 3.13.0
+ 3.18.1
+ test
org.antlr
diff --git a/fhir-smart/pom.xml b/fhir-smart/pom.xml
index 51a1cd38148..e4b2fae1002 100644
--- a/fhir-smart/pom.xml
+++ b/fhir-smart/pom.xml
@@ -41,6 +41,7 @@
${project.groupId}
fhir-examples
+ test
${project.groupId}
@@ -57,6 +58,7 @@
com.auth0
java-jwt
+ test
org.testng
diff --git a/fhir-smart/src/main/java/com/ibm/fhir/smart/AuthzPolicyEnforcementPersistenceInterceptor.java b/fhir-smart/src/main/java/com/ibm/fhir/smart/AuthzPolicyEnforcementPersistenceInterceptor.java
index 619b755ed62..10a314e9616 100644
--- a/fhir-smart/src/main/java/com/ibm/fhir/smart/AuthzPolicyEnforcementPersistenceInterceptor.java
+++ b/fhir-smart/src/main/java/com/ibm/fhir/smart/AuthzPolicyEnforcementPersistenceInterceptor.java
@@ -18,9 +18,6 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import com.auth0.jwt.JWT;
-import com.auth0.jwt.interfaces.Claim;
-import com.auth0.jwt.interfaces.DecodedJWT;
import com.ibm.fhir.config.FHIRRequestContext;
import com.ibm.fhir.model.resource.Bundle;
import com.ibm.fhir.model.resource.Provenance;
@@ -53,6 +50,8 @@
import com.ibm.fhir.search.util.ReferenceUtil;
import com.ibm.fhir.search.util.ReferenceValue;
import com.ibm.fhir.search.util.SearchUtil;
+import com.ibm.fhir.smart.JWT.Claim;
+import com.ibm.fhir.smart.JWT.DecodedJWT;
import com.ibm.fhir.smart.Scope.ContextType;
import com.ibm.fhir.smart.Scope.Permission;
@@ -622,7 +621,7 @@ private List getScopesFromToken(DecodedJWT jwt) throws FHIRPersistenceInt
scopeStrings = Arrays.asList(claim.asString().split("\\s+"));
} else {
log.fine("Found scope claim was expected to be a string but is not; processing as a list");
- scopeStrings = claim.asList(String.class);
+ scopeStrings = claim.asList();
}
return scopeStrings.stream()
@@ -641,7 +640,7 @@ private Set getPatientIdFromToken(DecodedJWT jwt) throws FHIRPersistence
String patientId = claim.asString();
if (patientId == null) {
- return new HashSet<>(claim.asList(String.class));
+ return new HashSet<>(claim.asList());
}
return Stream.of(patientId.split(" "))
diff --git a/fhir-smart/src/main/java/com/ibm/fhir/smart/JWT.java b/fhir-smart/src/main/java/com/ibm/fhir/smart/JWT.java
new file mode 100644
index 00000000000..ccb7b6b25a4
--- /dev/null
+++ b/fhir-smart/src/main/java/com/ibm/fhir/smart/JWT.java
@@ -0,0 +1,99 @@
+/*
+ * (C) Copyright IBM Corp. 2021
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+package com.ibm.fhir.smart;
+
+import java.io.StringReader;
+import java.util.Base64;
+import java.util.Base64.Decoder;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import jakarta.json.Json;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonReaderFactory;
+import jakarta.json.JsonString;
+import jakarta.json.JsonValue;
+import jakarta.json.JsonValue.ValueType;
+
+/**
+ * A minimal alternative to the com.auth0:java-jwt library.
+ * This flavor mimics the java-jwt API but implement only the [very small] subset of functionality that is
+ * actually needed by the fhir-smart policy enforcement.
+ */
+public class JWT {
+ private static final JsonReaderFactory JSON_READER_FACTORY = Json.createReaderFactory(null);
+ private static final Decoder decoder = Base64.getDecoder();
+
+ public static DecodedJWT decode(String jwt) {
+ String[] parts = jwt.split("\\.");
+ if (parts.length < 2 || parts.length > 3 || (parts.length == 2 && !jwt.endsWith("."))) {
+ throw new IllegalArgumentException("Invalid JWT: expected 3 parts but found " + parts.length);
+ }
+
+ return new DecodedJWT(
+ new String(decoder.decode(parts[0])),
+ new String(decoder.decode(parts[1])),
+ parts.length == 2 ? null : new String(decoder.decode(parts[2]).toString())
+ );
+ }
+
+ public static class DecodedJWT {
+ final JsonObject header;
+ final JsonObject data;
+ final JsonObject signature;
+
+ private DecodedJWT(String header, String data, String signature) {
+ this.header = JSON_READER_FACTORY.createReader(new StringReader(header)).readObject();
+ this.data = JSON_READER_FACTORY.createReader(new StringReader(data)).readObject();
+ this.signature = signature == null ? null :
+ JSON_READER_FACTORY.createReader(new StringReader(signature)).readObject();
+ }
+
+ public Claim getClaim(String name) {
+ return new Claim(data.get(name));
+ }
+ }
+
+ public static class Claim {
+ final JsonValue value;
+
+ private Claim(JsonValue value) {
+ this.value = value;
+ }
+
+ public String asString() {
+ if (value == null) return null;
+
+ switch (value.getValueType()) {
+ case STRING:
+ return ((JsonString) value).getString();
+ case TRUE:
+ case FALSE:
+ case NUMBER:
+ case NULL:
+ case OBJECT:
+ case ARRAY:
+ default:
+ return value.toString();
+ }
+ }
+
+ public List asList() {
+ List list = value.asJsonArray().getValuesAs(JsonString.class).stream()
+ .map(s -> s.getString())
+ .collect(Collectors.toList());
+ return Collections.unmodifiableList(list);
+ }
+
+ public boolean isNull() {
+ if (value == null || value.getValueType() == ValueType.NULL) {
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/fhir-smart/src/test/java/com/ibm/fhir/smart/test/JWTTest.java b/fhir-smart/src/test/java/com/ibm/fhir/smart/test/JWTTest.java
new file mode 100644
index 00000000000..bf8066af3a5
--- /dev/null
+++ b/fhir-smart/src/test/java/com/ibm/fhir/smart/test/JWTTest.java
@@ -0,0 +1,39 @@
+/*
+ * (C) Copyright IBM Corp. 2021
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+package com.ibm.fhir.smart.test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.testng.annotations.Test;
+
+import com.auth0.jwt.algorithms.Algorithm;
+import com.ibm.fhir.smart.JWT;
+import com.ibm.fhir.smart.JWT.DecodedJWT;
+
+/**
+ *
+ */
+public class JWTTest {
+ private String testString = "value";
+ private List testList = Arrays.asList(new String[] {"value1", "value2"});
+
+ private String jwt = com.auth0.jwt.JWT.create()
+ .withClaim("string", testString)
+ .withClaim("array", testList)
+ .sign(Algorithm.none());
+
+ @Test
+ public void test() {
+ DecodedJWT decodedJwt = JWT.decode(jwt);
+ assertEquals(decodedJwt.getClaim("string").asString(), testString);
+ assertEquals(decodedJwt.getClaim("array").asList(), testList);
+ assertTrue(decodedJwt.getClaim("bogus").isNull());
+ }
+}