Skip to content

Commit

Permalink
[BACK-3227] Retrieve clinician identity using Practitioner FHIR Resource
Browse files Browse the repository at this point in the history
  • Loading branch information
toddkazakov committed Oct 24, 2024
1 parent 623b332 commit e7123a7
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 8 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ themes/base

# OSX files
.DS_Store

# Maven shade plugin
admin/dependency-reduced-pom.xml
32 changes: 32 additions & 0 deletions admin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@
</properties>

<dependencies>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-client</artifactId>
<version>7.4.5</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-r4</artifactId>
<version>7.4.5</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi</artifactId>
Expand Down Expand Up @@ -62,6 +72,28 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<artifactSet>
<includes>
<include>ca.uhn.hapi.fhir:*</include>
<include>io.opentelemetry:*</include>
<include>org.apache.commons:*</include>
</includes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.tidepool.keycloak.extensions.broker;

import ca.uhn.fhir.context.FhirContext;

public class FHIRContext {
// Singleton instance - the creation of this object is expensive
private static final FhirContext R4 = FhirContext.forR4();

private FHIRContext() {}

public static FhirContext getR4() {
return R4;
}
}
Original file line number Diff line number Diff line change
@@ -1,32 +1,92 @@
package org.tidepool.keycloak.extensions.broker;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.client.api.IClientInterceptor;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.interceptor.BearerTokenAuthInterceptor;
import org.hl7.fhir.r4.model.ContactPoint;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Practitioner;
import org.keycloak.broker.oidc.OIDCIdentityProvider;
import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
import org.keycloak.broker.oidc.OIDCIdentityProviderFactory;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.broker.provider.util.SimpleHttp;
import org.keycloak.models.KeycloakSession;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.JsonWebToken;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;

public class SMARTIdentityProvider extends OIDCIdentityProvider {

public static final String FHIR_R4 = "R4";

private static final String[] defaultForwardParameters = {"launch", "aud", "iss"};

private final String fhirVersion;
private final SMARTIdentityProviderConfig config;

public SMARTIdentityProvider(KeycloakSession session, SMARTIdentityProviderConfig config) {
super(session, discoverConfig(session, config.getIssuer()));
getConfig().setIssuer(config.getIssuer());
getConfig().setClientId(config.getClientId());
getConfig().setClientSecret(config.getClientSecret());
getConfig().setDefaultScope(config.getScopes());
getConfig().setAlias(config.getAlias());
getConfig().setForwardParameters(withDefaultForwardParameters(config.getForwardParameters()));
getConfig().setDisableUserInfoService(true);

this.config = config;
}

@Override
protected BrokeredIdentityContext extractIdentity(AccessTokenResponse tokenResponse, String accessToken, JsonWebToken idToken) throws IOException {
BrokeredIdentityContext identity = super.extractIdentity(tokenResponse, accessToken, idToken);

Practitioner practitioner = getPractitioner(idToken.getSubject(), accessToken);
for (ContactPoint c : practitioner.getTelecom()) {
if (c.getSystem() == ContactPoint.ContactPointSystem.EMAIL && c.getValue() != null && !c.getValue().isEmpty()) {
identity.setEmail(c.getValue());
break;
}
}

identity.setFirstName(practitioner.getNameFirstRep().getGivenAsSingleString());
identity.setLastName(practitioner.getNameFirstRep().getFamily());

return identity;
}

private Practitioner getPractitioner(String id, String accessToken) {
if (!FHIR_R4.equals(config.getFHIRVersion())) {
throw new IdentityBrokerException("Unsupported FHIR Version: " + config.getFHIRVersion());
}

FhirContext ctx = FHIRContext.getR4();
IClientInterceptor authInterceptor = new BearerTokenAuthInterceptor(accessToken);
IGenericClient client = ctx.newRestfulGenericClient(config.getIssuer());
client.registerInterceptor(authInterceptor);
Practitioner practitioner = client.read().resource(Practitioner.class).withId(id).execute();

IParser parser = ctx.newJsonParser();
String serialized = parser.encodeResourceToString(practitioner);
System.out.println(serialized);

return practitioner;
}

private Patient getPatient(String id, String accessToken) {
if (!FHIR_R4.equals(config.getFHIRVersion())) {
throw new IdentityBrokerException("Unsupported FHIR Version: " + config.getFHIRVersion());
}

fhirVersion = config.getFHIRVersion();
FhirContext ctx = FHIRContext.getR4();
IClientInterceptor authInterceptor = new BearerTokenAuthInterceptor(accessToken);
IGenericClient client = ctx.newRestfulGenericClient(config.getIssuer());
return client.read().resource(Patient.class).withId(id).execute();
}

private static String withDefaultForwardParameters(String params){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class SMARTIdentityProviderFactory extends AbstractIdentityProviderFactor

public static final String PROVIDER_ID = "smart";

public static final String[] SUPPORTED_FHIR_VERSIONS = {"R4"};
public static final String[] SUPPORTED_FHIR_VERSIONS = {SMARTIdentityProvider.FHIR_R4};

@Override
public String getName() {
Expand Down
8 changes: 4 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: "3"

volumes:
providers:
driver: local
Expand Down Expand Up @@ -48,6 +46,8 @@ services:
- ./tidepool-theme:/opt/keycloak/themes/tidepool
- providers:/opt/keycloak/providers
depends_on:
- providers
- postgres
providers:
condition: service_completed_successfully
postgres:
condition: service_started
command: start-dev

0 comments on commit e7123a7

Please sign in to comment.