From 9114085cd0d438ee92106763ef20aec99a6ea54f Mon Sep 17 00:00:00 2001
From: Lee Surprenant <lmsurpre@us.ibm.com>
Date: Tue, 5 Oct 2021 11:17:06 -0400
Subject: [PATCH 1/2] Remove runtime dependency on the auth0 java-jwt lib

We use such a small portion of this library that I was always on the
fence about whether we should even bother with it.
Recently, a user hit some issues with conflicting jackson dependency
versions and so I decided to see what fhir-smart might look like without
this dependency.

Signed-off-by: Lee Surprenant <lmsurpre@us.ibm.com>
---
 fhir-parent/pom.xml                           |  2 +-
 fhir-smart/pom.xml                            |  2 +
 ...licyEnforcementPersistenceInterceptor.java |  9 +-
 .../src/main/java/com/ibm/fhir/smart/JWT.java | 99 +++++++++++++++++++
 .../java/com/ibm/fhir/smart/test/JWTTest.java | 39 ++++++++
 5 files changed, 145 insertions(+), 6 deletions(-)
 create mode 100644 fhir-smart/src/main/java/com/ibm/fhir/smart/JWT.java
 create mode 100644 fhir-smart/src/test/java/com/ibm/fhir/smart/test/JWTTest.java

diff --git a/fhir-parent/pom.xml b/fhir-parent/pom.xml
index b3262e358c6..ff577f89181 100644
--- a/fhir-parent/pom.xml
+++ b/fhir-parent/pom.xml
@@ -415,7 +415,7 @@
             <dependency>
                 <groupId>com.auth0</groupId>
                 <artifactId>java-jwt</artifactId>
-                <version>3.13.0</version>
+                <version>3.18.1</version>
             </dependency>
             <dependency>
                 <groupId>org.antlr</groupId>
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 @@
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>fhir-examples</artifactId>
+            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
@@ -57,6 +58,7 @@
         <dependency>
             <groupId>com.auth0</groupId>
             <artifactId>java-jwt</artifactId>
+            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.testng</groupId>
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<Scope> 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<String> 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<String> asList() {
+            List<String> 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<String> 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());
+    }
+}

From fb09408dbda659e82ffbb303417ea0aa5619e3ff Mon Sep 17 00:00:00 2001
From: Lee Surprenant <lmsurpre@us.ibm.com>
Date: Wed, 6 Oct 2021 12:00:32 -0400
Subject: [PATCH 2/2] Update fhir-parent/pom.xml

Signed-off-by: Lee Surprenant <lmsurpre@us.ibm.com>
---
 fhir-parent/pom.xml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/fhir-parent/pom.xml b/fhir-parent/pom.xml
index ff577f89181..e8375f452c7 100644
--- a/fhir-parent/pom.xml
+++ b/fhir-parent/pom.xml
@@ -416,6 +416,7 @@
                 <groupId>com.auth0</groupId>
                 <artifactId>java-jwt</artifactId>
                 <version>3.18.1</version>
+                <scope>test</scope>
             </dependency>
             <dependency>
                 <groupId>org.antlr</groupId>
