From 7b30a4506601f72d2b99c323e4981abb0c509f48 Mon Sep 17 00:00:00 2001 From: Christophe Date: Wed, 3 Aug 2022 16:04:00 +0200 Subject: [PATCH] check jwt token expiration and refresh token if necessary --- README.md | 2 +- build.gradle | 2 +- .../camunda/operate/CamundaOperateClient.java | 16 ++++++++ .../operate/auth/JwtAuthentication.java | 41 +++++++++++++++++++ .../auth/LocalIdentityAuthentication.java | 6 +-- .../operate/auth/SaasAuthentication.java | 5 +-- 6 files changed, 63 insertions(+), 9 deletions(-) create mode 100644 src/main/java/io/camunda/operate/auth/JwtAuthentication.java diff --git a/README.md b/README.md index ec98c38..a536097 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ You can import it to your maven or gradle project as a dependency io.camunda camunda-operate-client-java - 1.1.0 + 1.2.0 ``` diff --git a/build.gradle b/build.gradle index 83381ec..ba2bb15 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ plugins { } group = 'io.camunda' -version = '1.1.0' +version = '1.2.0' sourceCompatibility = '8' repositories { diff --git a/src/main/java/io/camunda/operate/CamundaOperateClient.java b/src/main/java/io/camunda/operate/CamundaOperateClient.java index 40e90e3..39a6c18 100644 --- a/src/main/java/io/camunda/operate/CamundaOperateClient.java +++ b/src/main/java/io/camunda/operate/CamundaOperateClient.java @@ -37,9 +37,13 @@ public class CamundaOperateClient { + private AuthInterface authentication; + private String operateUrl; private Header authHeader; + + private int tokenExpiration; public ProcessDefinition getProcessDefinition(Long key) throws OperateException { return get(key, ProcessDefinition.class); @@ -161,6 +165,7 @@ private T get(Long key, Class resultType) throws OperateException { } protected String executeQuery(ClassicHttpRequest httpRequest) throws OperateException { + reconnectEventually(); try (CloseableHttpClient httpClient = HttpClients.createDefault()) { try (CloseableHttpResponse response = httpClient.execute(httpRequest)) { return new String(Java8Utils.readAllBytes(response.getEntity().getContent()), StandardCharsets.UTF_8); @@ -186,6 +191,16 @@ public void setAuthHeader(Header authHeader) { this.authHeader = authHeader; } + public void setTokenExpiration(int tokenExpiration) { + this.tokenExpiration = tokenExpiration; + } + + private void reconnectEventually() throws OperateException { + if (this.tokenExpiration>0 && this.tokenExpiration<(System.currentTimeMillis()/1000-30)) { + authentication.authenticate(this); + } + } + public static class Builder { private AuthInterface authentication; @@ -220,6 +235,7 @@ public CamundaOperateClient build() throws OperateException { } else { client = new CamundaOperateClient(); } + client.authentication = authentication; client.operateUrl = operateUrl; authentication.authenticate(client); return client; diff --git a/src/main/java/io/camunda/operate/auth/JwtAuthentication.java b/src/main/java/io/camunda/operate/auth/JwtAuthentication.java new file mode 100644 index 0000000..b6e3fa4 --- /dev/null +++ b/src/main/java/io/camunda/operate/auth/JwtAuthentication.java @@ -0,0 +1,41 @@ +package io.camunda.operate.auth; + +import java.util.Base64; + +import org.apache.hc.core5.http.message.BasicHeader; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.camunda.operate.CamundaOperateClient; +import io.camunda.operate.exception.OperateException; + +public abstract class JwtAuthentication implements AuthInterface { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + private static final Base64.Decoder DECODER = Base64.getUrlDecoder(); + + public int getExpiration(String token) throws OperateException { + try { + String[] chunks = token.split("\\."); + + + String payload = new String(DECODER.decode(chunks[1])); + JsonNode jsonPayload = MAPPER.readValue(payload, JsonNode.class); + JsonNode exp = jsonPayload.get("exp"); + if (exp==null) { + return 0; + } else { + return exp.asInt(); + } + } catch (JsonProcessingException e) { + throw new OperateException("Token is not readable", e); + } + } + + public void setToken(CamundaOperateClient client, String token) throws OperateException { + client.setAuthHeader(new BasicHeader("Authorization", "Bearer " + token)); + client.setTokenExpiration(getExpiration(token)); + } +} diff --git a/src/main/java/io/camunda/operate/auth/LocalIdentityAuthentication.java b/src/main/java/io/camunda/operate/auth/LocalIdentityAuthentication.java index a91f60a..0a89237 100644 --- a/src/main/java/io/camunda/operate/auth/LocalIdentityAuthentication.java +++ b/src/main/java/io/camunda/operate/auth/LocalIdentityAuthentication.java @@ -9,15 +9,13 @@ import java.net.URLEncoder; import java.nio.charset.StandardCharsets; -import org.apache.hc.core5.http.message.BasicHeader; - import com.fasterxml.jackson.databind.JsonNode; import io.camunda.operate.CamundaOperateClient; import io.camunda.operate.exception.OperateException; import io.camunda.operate.util.JsonUtils; -public class LocalIdentityAuthentication implements AuthInterface { +public class LocalIdentityAuthentication extends JwtAuthentication { private String clientId; private String clientSecret; @@ -83,7 +81,7 @@ public void authenticate(CamundaOperateClient client) throws OperateException { } JsonNode responseBody = JsonUtils.toJsonNode(response.toString()); String token = responseBody.get("access_token").asText(); - client.setAuthHeader(new BasicHeader("Authorization", "Bearer " + token)); + setToken(client, token); } } else { throw new OperateException("Error "+conn.getResponseCode()+" obtaining access token : "+conn.getResponseMessage()); diff --git a/src/main/java/io/camunda/operate/auth/SaasAuthentication.java b/src/main/java/io/camunda/operate/auth/SaasAuthentication.java index 4615cf5..0f5a0b6 100644 --- a/src/main/java/io/camunda/operate/auth/SaasAuthentication.java +++ b/src/main/java/io/camunda/operate/auth/SaasAuthentication.java @@ -7,7 +7,6 @@ import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.core5.http.io.entity.StringEntity; -import org.apache.hc.core5.http.message.BasicHeader; import com.fasterxml.jackson.databind.JsonNode; @@ -15,7 +14,7 @@ import io.camunda.operate.exception.OperateException; import io.camunda.operate.util.JsonUtils; -public class SaasAuthentication implements AuthInterface { +public class SaasAuthentication extends JwtAuthentication { private String clientId; private String clientSecret; @@ -40,7 +39,7 @@ public void authenticate(CamundaOperateClient client) throws OperateException { JsonNode responseBody = JsonUtils.toJsonNode(response.getEntity().getContent()); String token = responseBody.get("access_token").asText(); - client.setAuthHeader(new BasicHeader("Authorization", "Bearer " + token)); + setToken(client, token); } } catch (IOException e) { throw new OperateException(e);