Skip to content

Commit bc58bc3

Browse files
authored
feat: allow to check for more Keycloak issuers (#289)
* feat: allow to check for more Keycloak issuers * chore: formatting
1 parent 766c455 commit bc58bc3

File tree

4 files changed

+45
-5
lines changed

4 files changed

+45
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.avast.grpc.jwt.keycloak.server;
2+
3+
import org.keycloak.TokenVerifier;
4+
import org.keycloak.common.VerificationException;
5+
import org.keycloak.representations.JsonWebToken;
6+
7+
public class IssuersCheck implements TokenVerifier.Predicate<JsonWebToken> {
8+
private final String[] issuers;
9+
10+
public IssuersCheck(String[] issuers) {
11+
this.issuers = issuers;
12+
}
13+
14+
@Override
15+
public boolean test(JsonWebToken t) throws VerificationException {
16+
for (String i : issuers) {
17+
if (i.equals(t.getIssuer())) {
18+
return true;
19+
}
20+
}
21+
throw new VerificationException(
22+
"Invalid token issuer. Was '"
23+
+ t.getIssuer()
24+
+ "' but expected one of: "
25+
+ String.join(" ", issuers));
26+
}
27+
}
28+
;

keycloak/src/main/java/com/avast/grpc/jwt/keycloak/server/KeycloakJwtServerInterceptor.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ public static KeycloakJwtServerInterceptor fromConfig(Config config) {
2424
Clock.systemUTC());
2525
KeycloakJwtTokenParser tokenParser =
2626
new KeycloakJwtTokenParser(
27-
fc.getString("serverUrl"), fc.getString("realm"), publicKeyProvider);
27+
fc.getString("serverUrl"),
28+
fc.getString("realm"),
29+
fc.getStringList("allowedIssuers"),
30+
publicKeyProvider);
2831
tokenParser = tokenParser.withExpectedAudience(fc.getString("expectedAudience"));
2932
tokenParser = tokenParser.withExpectedIssuedFor(fc.getString("expectedIssuedFor"));
3033
return new KeycloakJwtServerInterceptor(tokenParser);

keycloak/src/main/java/com/avast/grpc/jwt/keycloak/server/KeycloakJwtTokenParser.java

+12-4
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
import com.avast.grpc.jwt.server.JwtTokenParser;
44
import com.google.common.base.Strings;
5+
import java.util.List;
56
import java.util.concurrent.CompletableFuture;
67
import java.util.concurrent.CompletionException;
8+
import java.util.stream.Collectors;
79
import org.keycloak.TokenVerifier;
810
import org.keycloak.common.VerificationException;
911
import org.keycloak.constants.ServiceUrlConstants;
@@ -18,13 +20,19 @@ public class KeycloakJwtTokenParser implements JwtTokenParser<AccessToken> {
1820
protected String expectedIssuedFor;
1921

2022
public KeycloakJwtTokenParser(
21-
String serverUrl, String realm, KeycloakPublicKeyProvider publicKeyProvider) {
23+
String serverUrl,
24+
String realm,
25+
List<String> allowedIssuers,
26+
KeycloakPublicKeyProvider publicKeyProvider) {
2227
this.publicKeyProvider = publicKeyProvider;
23-
String realmUrl =
24-
serverUrl + ServiceUrlConstants.REALM_INFO_PATH.replace("{realm-name}", realm);
28+
29+
String suffix = ServiceUrlConstants.REALM_INFO_PATH.replace("{realm-name}", realm);
30+
List<String> issuers =
31+
allowedIssuers.stream().map(i -> i + suffix).collect(Collectors.toList());
32+
issuers.add(0, serverUrl + suffix);
2533
this.checks =
2634
new TokenVerifier.Predicate[] {
27-
new TokenVerifier.RealmUrlCheck(realmUrl),
35+
new IssuersCheck(issuers.toArray(new String[0])),
2836
TokenVerifier.SUBJECT_EXISTS_CHECK,
2937
new TokenVerifier.TokenTypeCheck(TokenUtil.TOKEN_TYPE_BEARER),
3038
TokenVerifier.IS_ACTIVE

keycloak/src/main/resources/reference.conf

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ keycloakDefaults {
1010
// for server
1111
expectedAudience = ""
1212
expectedIssuedFor = ""
13+
allowedIssuers = [] // list of additional allowed issuers, e.g. "https://sso-new.example.com/auth"
1314
minTimeBetweenJwksRequests = 10 seconds
1415
publicKeyCacheTtl = 1 day
1516
}

0 commit comments

Comments
 (0)