Skip to content

Commit d6d1011

Browse files
committed
Merge branch 'develop'
2 parents cf17485 + 7b355a0 commit d6d1011

File tree

2 files changed

+40
-34
lines changed

2 files changed

+40
-34
lines changed

pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
<modelVersion>4.0.0</modelVersion>
1717
<artifactId>http-server</artifactId>
18-
<version>0.3.18</version>
18+
<version>0.3.19</version>
1919
<name>HttpServer</name>
2020
<packaging>jar</packaging>
2121

src/main/java/info/unterrainer/commons/httpserver/accessmanager/HttpAccessManager.java

+39-33
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,29 @@
11
package info.unterrainer.commons.httpserver.accessmanager;
22

33
import java.io.IOException;
4-
import java.io.UncheckedIOException;
4+
import java.math.BigInteger;
55
import java.net.URI;
6-
import java.net.URISyntaxException;
76
import java.net.http.HttpClient;
87
import java.net.http.HttpRequest;
9-
import java.net.http.HttpResponse.BodyHandlers;
8+
import java.net.http.HttpResponse;
9+
import java.security.KeyFactory;
1010
import java.security.PublicKey;
11+
import java.security.spec.RSAPublicKeySpec;
12+
import java.util.Base64;
1113
import java.util.HashSet;
1214
import java.util.Set;
1315

1416
import org.eclipse.jetty.http.HttpHeader;
1517
import org.keycloak.TokenVerifier;
1618
import org.keycloak.common.VerificationException;
1719
import org.keycloak.representations.AccessToken;
18-
import org.keycloak.representations.idm.PublishedRealmRepresentation;
1920

21+
import com.fasterxml.jackson.databind.JsonNode;
2022
import com.fasterxml.jackson.databind.ObjectMapper;
2123

2224
import info.unterrainer.commons.httpserver.HttpServer;
2325
import info.unterrainer.commons.httpserver.enums.Attribute;
2426
import info.unterrainer.commons.httpserver.exceptions.ForbiddenException;
25-
import info.unterrainer.commons.httpserver.exceptions.GatewayTimeoutException;
2627
import info.unterrainer.commons.httpserver.exceptions.UnauthorizedException;
2728
import info.unterrainer.commons.httpserver.jsons.UserDataJson;
2829
import io.javalin.core.security.AccessManager;
@@ -56,6 +57,35 @@ public void manage(final Handler handler, final Context ctx, final Set<Role> per
5657
handler.handle(ctx);
5758
}
5859

60+
private PublicKey fetchPublicKey(String jwksUrl) throws Exception {
61+
ObjectMapper objectMapper = new ObjectMapper();
62+
HttpClient client = HttpClient.newHttpClient();
63+
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(jwksUrl)).GET().build();
64+
65+
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
66+
67+
if (response.statusCode() >= 300) {
68+
throw new IOException("Failed to fetch JWKS: HTTP " + response.statusCode());
69+
}
70+
71+
JsonNode jwks = objectMapper.readTree(response.body());
72+
// Just take the first key for now.
73+
JsonNode key = jwks.get("keys").get(0);
74+
75+
String modulusBase64 = key.get("n").asText();
76+
String exponentBase64 = key.get("e").asText();
77+
78+
byte[] modulusBytes = Base64.getUrlDecoder().decode(modulusBase64);
79+
byte[] exponentBytes = Base64.getUrlDecoder().decode(exponentBase64);
80+
81+
BigInteger modulus = new BigInteger(1, modulusBytes);
82+
BigInteger exponent = new BigInteger(1, exponentBytes);
83+
84+
RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, exponent);
85+
KeyFactory factory = KeyFactory.getInstance("RSA");
86+
return factory.generatePublic(spec);
87+
}
88+
5989
private void initPublicKey() {
6090
if (publicKey != null)
6191
return;
@@ -66,36 +96,12 @@ private void initPublicKey() {
6696
if (!realm.startsWith("/"))
6797
realm = "/" + realm;
6898

69-
authUrl = host + "realms" + realm;
99+
authUrl = host + "realms" + realm + "/protocol/openid-connect/certs";
70100
try {
71101
log.info("Getting public key from: [{}]", authUrl);
72-
HttpClient client = HttpClient.newHttpClient();
73-
HttpRequest request = HttpRequest.newBuilder().uri(new URI(authUrl)).build();
74-
ObjectMapper objectMapper = new ObjectMapper();
75-
76-
client.sendAsync(request, BodyHandlers.ofString()).thenApply(response -> {
77-
if (response.statusCode() >= 300) {
78-
log.error("HTTP status [{}] getting public key from keycloak instance [{}].", response.statusCode(),
79-
authUrl);
80-
throw new GatewayTimeoutException(String
81-
.format("The keycloak instance returned an error (status: %d).", response.statusCode()));
82-
}
83-
return response.body();
84-
}).thenAccept(body -> {
85-
if (body == null) {
86-
log.warn("Received empty body.");
87-
return;
88-
}
89-
try {
90-
publicKey = objectMapper.readValue(body, PublishedRealmRepresentation.class).getPublicKey();
91-
log.info("Public key received.");
92-
} catch (IOException e) {
93-
log.error("Error parsing answer from keycloak.");
94-
throw new UncheckedIOException(e);
95-
}
96-
}).join();
97-
} catch (URISyntaxException e) {
98-
log.error("The keycloak URL was illegal [{}].", authUrl);
102+
publicKey = fetchPublicKey(authUrl);
103+
} catch (Exception e) {
104+
log.error("There was an error fetching the PublicKey from the openIdConnect-server [{}].", authUrl);
99105
throw new IllegalStateException(e);
100106
}
101107
}

0 commit comments

Comments
 (0)