Skip to content

Commit b0283e5

Browse files
committed
fixed a bug where the jwt token is not refreshed
1 parent ddc4679 commit b0283e5

20 files changed

+250
-178
lines changed

camunda-sdk-java/java-common/src/main/java/io/camunda/common/auth/Authentication.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44

55
public interface Authentication {
66

7-
Authentication build();
8-
97
Map.Entry<String, String> getTokenHeader(Product product);
108

119
void resetToken(Product product);
10+
11+
interface AuthenticationBuilder {
12+
Authentication build();
13+
}
1214
}

camunda-sdk-java/java-common/src/main/java/io/camunda/common/auth/DefaultNoopAuthentication.java

+1-3
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,8 @@ public class DefaultNoopAuthentication implements Authentication {
1717
private final String errorMessage =
1818
"Unable to determine authentication. Please check your configuration";
1919

20-
@Override
21-
public Authentication build() {
20+
public DefaultNoopAuthentication() {
2221
LOG.error(errorMessage);
23-
return this;
2422
}
2523

2624
@Override
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,67 @@
11
package io.camunda.common.auth;
22

3-
/**
4-
* TODO: Figure out common functionality between SaaS and Self Managed that can be inserted here to
5-
* reduce code duplication. If not, remove this class
6-
*/
7-
public abstract class JwtAuthentication implements Authentication {}
3+
import java.lang.invoke.MethodHandles;
4+
import java.time.LocalDateTime;
5+
import java.util.AbstractMap;
6+
import java.util.HashMap;
7+
import java.util.Map;
8+
import java.util.Map.Entry;
9+
import org.slf4j.Logger;
10+
import org.slf4j.LoggerFactory;
11+
12+
public abstract class JwtAuthentication implements Authentication {
13+
14+
private final JwtConfig jwtConfig;
15+
private final Map<Product, JwtToken> tokens = new HashMap<>();
16+
17+
protected JwtAuthentication(JwtConfig jwtConfig) {
18+
this.jwtConfig = jwtConfig;
19+
}
20+
21+
public JwtConfig getJwtConfig() {
22+
return jwtConfig;
23+
}
24+
25+
@Override
26+
public final void resetToken(Product product) {
27+
tokens.remove(product);
28+
}
29+
30+
@Override
31+
public final Entry<String, String> getTokenHeader(Product product) {
32+
if (!tokens.containsKey(product) || !isValid(tokens.get(product))) {
33+
JwtToken newToken = generateToken(product, jwtConfig.getProduct(product));
34+
tokens.put(product, newToken);
35+
}
36+
return authHeader(tokens.get(product).getToken());
37+
}
38+
39+
protected abstract JwtToken generateToken(Product product, JwtCredential credential);
40+
41+
private Entry<String, String> authHeader(String token) {
42+
return new AbstractMap.SimpleEntry<>("Authorization", "Bearer " + token);
43+
}
44+
45+
private boolean isValid(JwtToken jwtToken) {
46+
// a token is only counted valid if it is only valid for at least 30 seconds
47+
return jwtToken.getExpiry().isAfter(LocalDateTime.now().minusSeconds(30));
48+
}
49+
50+
protected static class JwtToken {
51+
private final String token;
52+
private final LocalDateTime expiry;
53+
54+
public JwtToken(String token, LocalDateTime expiry) {
55+
this.token = token;
56+
this.expiry = expiry;
57+
}
58+
59+
public String getToken() {
60+
return token;
61+
}
62+
63+
public LocalDateTime getExpiry() {
64+
return expiry;
65+
}
66+
}
67+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package io.camunda.common.auth;
2+
3+
import io.camunda.common.auth.Authentication.AuthenticationBuilder;
4+
5+
public abstract class JwtAuthenticationBuilder<T extends JwtAuthenticationBuilder<?>>
6+
implements AuthenticationBuilder {
7+
private JwtConfig jwtConfig;
8+
9+
public final T withJwtConfig(JwtConfig jwtConfig) {
10+
this.jwtConfig = jwtConfig;
11+
return self();
12+
}
13+
14+
@Override
15+
public final Authentication build() {
16+
return build(jwtConfig);
17+
}
18+
19+
protected abstract T self();
20+
21+
protected abstract Authentication build(JwtConfig jwtConfig);
22+
}
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
package io.camunda.common.auth;
22

33
import io.camunda.common.json.JsonMapper;
4-
import io.camunda.common.json.SdkObjectMapper;
54
import java.lang.invoke.MethodHandles;
6-
import java.util.AbstractMap;
7-
import java.util.HashMap;
8-
import java.util.Map;
5+
import java.time.LocalDateTime;
96
import org.apache.hc.client5.http.classic.methods.HttpPost;
107
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
118
import org.apache.hc.client5.http.impl.classic.HttpClients;
@@ -17,53 +14,29 @@
1714
public class SaaSAuthentication extends JwtAuthentication {
1815

1916
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
20-
private JwtConfig jwtConfig;
21-
private Map<Product, String> tokens;
2217

23-
// TODO: have a single object mapper to be used all throughout the SDK, i.e.bean injection
24-
private JsonMapper jsonMapper = new SdkObjectMapper();
18+
private final JsonMapper jsonMapper;
2519

26-
public SaaSAuthentication() {
27-
tokens = new HashMap<>();
20+
public SaaSAuthentication(JwtConfig jwtConfig, JsonMapper jsonMapper) {
21+
super(jwtConfig);
22+
this.jsonMapper = jsonMapper;
2823
}
2924

3025
public static SaaSAuthenticationBuilder builder() {
3126
return new SaaSAuthenticationBuilder();
3227
}
3328

34-
public JwtConfig getJwtConfig() {
35-
return jwtConfig;
36-
}
37-
38-
public void setJwtConfig(JwtConfig jwtConfig) {
39-
this.jwtConfig = jwtConfig;
40-
}
41-
42-
@Override
43-
public Authentication build() {
44-
return this;
45-
}
46-
47-
@Override
48-
public void resetToken(Product product) {
49-
tokens.remove(product);
50-
}
51-
52-
private String retrieveToken(Product product, JwtCredential jwtCredential) {
29+
private TokenResponse retrieveToken(Product product, JwtCredential jwtCredential) {
5330
try (CloseableHttpClient client = HttpClients.createDefault()) {
5431
HttpPost request = buildRequest(jwtCredential);
55-
TokenResponse tokenResponse =
56-
client.execute(
57-
request,
58-
response ->
59-
jsonMapper.fromJson(
60-
EntityUtils.toString(response.getEntity()), TokenResponse.class));
61-
tokens.put(product, tokenResponse.getAccessToken());
32+
return client.execute(
33+
request,
34+
response ->
35+
jsonMapper.fromJson(EntityUtils.toString(response.getEntity()), TokenResponse.class));
6236
} catch (Exception e) {
6337
LOG.error("Authenticating for " + product + " failed due to " + e);
6438
throw new RuntimeException("Unable to authenticate", e);
6539
}
66-
return tokens.get(product);
6740
}
6841

6942
private HttpPost buildRequest(JwtCredential jwtCredential) {
@@ -79,14 +52,10 @@ private HttpPost buildRequest(JwtCredential jwtCredential) {
7952
}
8053

8154
@Override
82-
public Map.Entry<String, String> getTokenHeader(Product product) {
83-
String token;
84-
if (tokens.containsKey(product)) {
85-
token = tokens.get(product);
86-
} else {
87-
JwtCredential jwtCredential = jwtConfig.getProduct(product);
88-
token = retrieveToken(product, jwtCredential);
89-
}
90-
return new AbstractMap.SimpleEntry<>("Authorization", "Bearer " + token);
55+
protected JwtToken generateToken(Product product, JwtCredential credential) {
56+
TokenResponse tokenResponse = retrieveToken(product, credential);
57+
return new JwtToken(
58+
tokenResponse.getAccessToken(),
59+
LocalDateTime.now().plusSeconds(tokenResponse.getExpiresIn()));
9160
}
9261
}
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
package io.camunda.common.auth;
22

3-
public class SaaSAuthenticationBuilder {
3+
import io.camunda.common.json.JsonMapper;
44

5-
SaaSAuthentication saaSAuthentication;
5+
public class SaaSAuthenticationBuilder extends JwtAuthenticationBuilder<SaaSAuthenticationBuilder> {
6+
private JsonMapper jsonMapper;
67

7-
SaaSAuthenticationBuilder() {
8-
saaSAuthentication = new SaaSAuthentication();
8+
public SaaSAuthenticationBuilder withJsonMapper(JsonMapper jsonMapper) {
9+
this.jsonMapper = jsonMapper;
10+
return this;
911
}
1012

11-
public SaaSAuthenticationBuilder jwtConfig(JwtConfig jwtConfig) {
12-
saaSAuthentication.setJwtConfig(jwtConfig);
13+
@Override
14+
protected SaaSAuthenticationBuilder self() {
1315
return this;
1416
}
1517

16-
public Authentication build() {
17-
return saaSAuthentication.build();
18+
@Override
19+
protected SaaSAuthentication build(JwtConfig jwtConfig) {
20+
return new SaaSAuthentication(jwtConfig, jsonMapper);
1821
}
1922
}

camunda-sdk-java/java-common/src/main/java/io/camunda/common/auth/SelfManagedAuthentication.java

+12-48
Original file line numberDiff line numberDiff line change
@@ -4,69 +4,33 @@
44
import io.camunda.identity.sdk.Identity;
55
import io.camunda.identity.sdk.authentication.Tokens;
66
import java.lang.invoke.MethodHandles;
7-
import java.util.AbstractMap;
8-
import java.util.HashMap;
9-
import java.util.Map;
7+
import java.time.LocalDateTime;
108
import org.slf4j.Logger;
119
import org.slf4j.LoggerFactory;
1210

1311
public class SelfManagedAuthentication extends JwtAuthentication {
1412

15-
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
16-
private JwtConfig jwtConfig;
17-
private IdentityConfig identityConfig;
18-
private Map<Product, String> tokens;
13+
private final IdentityConfig identityConfig;
1914

20-
public SelfManagedAuthentication() {
21-
tokens = new HashMap<>();
15+
public SelfManagedAuthentication(JwtConfig jwtConfig, IdentityConfig identityConfig) {
16+
super(jwtConfig);
17+
this.identityConfig = identityConfig;
2218
}
2319

2420
public static SelfManagedAuthenticationBuilder builder() {
2521
return new SelfManagedAuthenticationBuilder();
2622
}
2723

28-
public JwtConfig getJwtConfig() {
29-
return jwtConfig;
30-
}
31-
32-
public void setJwtConfig(JwtConfig jwtConfig) {
33-
this.jwtConfig = jwtConfig;
34-
}
35-
36-
public void setIdentityConfig(IdentityConfig identityConfig) {
37-
this.identityConfig = identityConfig;
38-
}
39-
4024
@Override
41-
public Authentication build() {
42-
return this;
25+
protected JwtToken generateToken(Product product, JwtCredential credential) {
26+
Tokens token = getIdentityToken(product, credential);
27+
return new JwtToken(
28+
token.getAccessToken(), LocalDateTime.now().plusSeconds(token.getExpiresIn()));
4329
}
4430

45-
@Override
46-
public void resetToken(Product product) {
47-
tokens.remove(product);
48-
}
49-
50-
@Override
51-
public Map.Entry<String, String> getTokenHeader(Product product) {
52-
String token;
53-
if (tokens.containsKey(product)) {
54-
token = tokens.get(product);
55-
} else {
56-
token = getIdentityToken(product);
57-
saveToken(product, token);
58-
}
59-
return new AbstractMap.SimpleEntry<>("Authorization", "Bearer " + token);
60-
}
61-
62-
private String getIdentityToken(Product product) {
31+
private Tokens getIdentityToken(Product product, JwtCredential credential) {
6332
Identity identity = identityConfig.get(product).getIdentity();
64-
String audience = jwtConfig.getProduct(product).getAudience();
65-
Tokens identityTokens = identity.authentication().requestToken(audience);
66-
return identityTokens.getAccessToken();
67-
}
68-
69-
private void saveToken(Product product, String token) {
70-
tokens.put(product, token);
33+
String audience = credential.getAudience();
34+
return identity.authentication().requestToken(audience);
7135
}
7236
}

camunda-sdk-java/java-common/src/main/java/io/camunda/common/auth/SelfManagedAuthenticationBuilder.java

+10-13
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,22 @@
22

33
import io.camunda.common.auth.identity.IdentityConfig;
44

5-
public class SelfManagedAuthenticationBuilder {
5+
public class SelfManagedAuthenticationBuilder
6+
extends JwtAuthenticationBuilder<SelfManagedAuthenticationBuilder> {
7+
private IdentityConfig identityConfig;
68

7-
SelfManagedAuthentication selfManagedAuthentication;
8-
9-
SelfManagedAuthenticationBuilder() {
10-
selfManagedAuthentication = new SelfManagedAuthentication();
11-
}
12-
13-
public SelfManagedAuthenticationBuilder jwtConfig(JwtConfig jwtConfig) {
14-
selfManagedAuthentication.setJwtConfig(jwtConfig);
9+
public SelfManagedAuthenticationBuilder withIdentityConfig(IdentityConfig identityConfig) {
10+
this.identityConfig = identityConfig;
1511
return this;
1612
}
1713

18-
public SelfManagedAuthenticationBuilder identityConfig(IdentityConfig identityConfig) {
19-
selfManagedAuthentication.setIdentityConfig(identityConfig);
14+
@Override
15+
protected SelfManagedAuthenticationBuilder self() {
2016
return this;
2117
}
2218

23-
public Authentication build() {
24-
return selfManagedAuthentication.build();
19+
@Override
20+
protected Authentication build(JwtConfig jwtConfig) {
21+
return new SelfManagedAuthentication(jwtConfig, identityConfig);
2522
}
2623
}

0 commit comments

Comments
 (0)