How to configure when keycloak is in Kubernetes? #163
-
My services/Keycloak runs inside a Kubernetes cluster, so I configured the ISS with the internal URL. The issuer is mismatched which caused a 401. A similar thing happened when I ran all my services, including Keycloak behind an Nginx proxy. Is there some configuration I am missing? |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 10 replies
-
You should use the service public name. For instance, the issuer for my Keycloak instance in K8s for the quiz application is If you can't configure Keycloak to use a hostname accessible from your resource server, then define the |
Beta Was this translation helpful? Give feedback.
-
I am about to release a version with authentication manager resolvers supporting dynamic multi-tenancy through Keycloak realms (accept all issuers with a given prefix). Here is what the synchronized version looks like (for servlet resource servers): class IssuerStartsWithAuthenticationManagerResolver implements AuthenticationManagerResolver<String> {
private final String issuerPrefix;
private final Converter<Jwt, AbstractAuthenticationToken> authenticationConverter;
private final Map<String, AuthenticationManager> jwtManagers = new ConcurrentHashMap<>();
/**
* @param issuerPrefix what access tokens iss claim must start with
* @param authenticationConverter converter from a valid {@link Jwt} to an {@link AbstractAuthenticationToken} instance
*/
public IssuerStartsWithAuthenticationManagerResolver(String issuerPrefix, Converter<Jwt, AbstractAuthenticationToken> authenticationConverter) {
super();
this.issuerPrefix = issuerPrefix.toString();
this.authenticationConverter = authenticationConverter;
}
@Override
public AuthenticationManager resolve(String issuer) {
if (!jwtManagers.containsKey(issuer)) {
if (!issuer.startsWith(issuerPrefix)) {
throw new UnknownIssuerException(issuer);
}
final var decoder = NimbusJwtDecoder.withIssuerLocation(issuer).build();
var provider = new JwtAuthenticationProvider(decoder);
provider.setJwtAuthenticationConverter(authenticationConverter);
jwtManagers.put(issuer, provider::authenticate);
}
return jwtManagers.get(issuer);
}
@ResponseStatus(HttpStatus.UNAUTHORIZED)
static class UnknownIssuerException extends RuntimeException {
private static final long serialVersionUID = -7140122776788781704L;
public UnknownIssuerException(String issuer) {
super("Unknown issuer: %s".formatted(issuer));
}
}
} All you'll have to do to switch to this authentication manager resolver is exposing this 2 beans in your conf: @Bean
ConfigurableClaimSetAuthoritiesConverter authoritiesConverter(@Value("${keycloak-host}") URI keycloakHost, SpringAddonsOidcProperties addonsProperties) {
final var opProperties = addonsProperties.getOpProperties(keycloakHost.toString());
return new ConfigurableClaimSetAuthoritiesConverter(claims -> opProperties.getAuthorities());
}
@Bean
AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver(
@Value("${keycloak-host}") URI keycloakHost,
Converter<Jwt, AbstractAuthenticationToken> authenticationConverter) {
return new JwtIssuerAuthenticationManagerResolver(new IssuerStartsWithAuthenticationManagerResolver(keycloakHost.toString(), authenticationConverter));
} The reason for the first bean is that the default expectation is that there are authorities mapping properties for each issuer. In the case of a Keycloak server with dynamic realms, we'll have only one set of authorities mapping properties (with the prefix as |
Beta Was this translation helpful? Give feedback.
-
Two more random questions: |
Beta Was this translation helpful? Give feedback.
You should use the service public name. For instance, the issuer for my Keycloak instance in K8s for the quiz application is
https://oidc.c4-soft.com/auth/realms/quiz
.If you can't configure Keycloak to use a hostname accessible from your resource server, then define the
jwk-set-uri
in addition to (keeps the issuer validator in JWT decoder) or instead of (removes the issuer validation) theiss
property.