Skip to content

Commit 5d33f0c

Browse files
backport/8.4: fix(common-authentication): Token not refreshed (#632)
* added final modifiers * created AuthenticationBuilder interface, moved build method there * moved noop log to constructor * refactored authentication and builders * better build next time before push * added jsonMapper to saas auth * formatter
1 parent 079e541 commit 5d33f0c

22 files changed

+255
-182
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,68 @@
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+
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
14+
15+
private final JwtConfig jwtConfig;
16+
private final Map<Product, JwtToken> tokens = new HashMap<>();
17+
18+
protected JwtAuthentication(JwtConfig jwtConfig) {
19+
this.jwtConfig = jwtConfig;
20+
}
21+
22+
public JwtConfig getJwtConfig() {
23+
return jwtConfig;
24+
}
25+
26+
@Override
27+
public final void resetToken(Product product) {
28+
tokens.remove(product);
29+
}
30+
31+
@Override
32+
public final Entry<String, String> getTokenHeader(Product product) {
33+
if (!tokens.containsKey(product) || !isValid(tokens.get(product))) {
34+
JwtToken newToken = generateToken(product, jwtConfig.getProduct(product));
35+
tokens.put(product, newToken);
36+
}
37+
return authHeader(tokens.get(product).getToken());
38+
}
39+
40+
protected abstract JwtToken generateToken(Product product, JwtCredential credential);
41+
42+
private Entry<String, String> authHeader(String token) {
43+
return new AbstractMap.SimpleEntry<>("Authorization", "Bearer " + token);
44+
}
45+
46+
private boolean isValid(JwtToken jwtToken) {
47+
// a token is only counted valid if it is only valid for at least 30 seconds
48+
return jwtToken.getExpiry().isAfter(LocalDateTime.now().minusSeconds(30));
49+
}
50+
51+
protected static class JwtToken {
52+
private final String token;
53+
private final LocalDateTime expiry;
54+
55+
public JwtToken(String token, LocalDateTime expiry) {
56+
this.token = token;
57+
this.expiry = expiry;
58+
}
59+
60+
public String getToken() {
61+
return token;
62+
}
63+
64+
public LocalDateTime getExpiry() {
65+
return expiry;
66+
}
67+
}
68+
}
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+
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
/** Contains mapping between products and their JWT credentials */
77
public class JwtConfig {
88

9-
private Map<Product, JwtCredential> map;
9+
private final Map<Product, JwtCredential> map;
1010

1111
public JwtConfig() {
1212
map = new HashMap<>();
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-47
Original file line numberDiff line numberDiff line change
@@ -4,69 +4,34 @@
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

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

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

2421
public static SelfManagedAuthenticationBuilder builder() {
2522
return new SelfManagedAuthenticationBuilder();
2623
}
2724

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-
4025
@Override
41-
public Authentication build() {
42-
return this;
26+
protected JwtToken generateToken(Product product, JwtCredential credential) {
27+
Tokens token = getIdentityToken(product, credential);
28+
return new JwtToken(
29+
token.getAccessToken(), LocalDateTime.now().plusSeconds(token.getExpiresIn()));
4330
}
4431

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) {
32+
private Tokens getIdentityToken(Product product, JwtCredential credential) {
6333
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);
34+
String audience = credential.getAudience();
35+
return identity.authentication().requestToken(audience);
7136
}
7237
}

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)