io.camunda.tasklist.webapp.security.oauth.OAuth2WebConfigurer Maven / Gradle / Ivy
/*
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under
* one or more contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright ownership.
* Licensed under the Camunda License 1.0. You may not use this file
* except in compliance with the Camunda License 1.0.
*/
package io.camunda.tasklist.webapp.security.oauth;
import static io.camunda.tasklist.util.CollectionUtil.firstOrDefault;
import static io.camunda.tasklist.util.CollectionUtil.getOrDefaultFromMap;
import static io.camunda.tasklist.webapp.security.TasklistProfileService.IDENTITY_AUTH_PROFILE;
import static io.camunda.tasklist.webapp.security.WebSecurityConfig.sendJSONErrorMessage;
import io.camunda.tasklist.property.ClientProperties;
import io.camunda.tasklist.property.TasklistProperties;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.env.Environment;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.stereotype.Component;
@Component
@Profile("!" + IDENTITY_AUTH_PROFILE)
public class OAuth2WebConfigurer {
public static final String SPRING_SECURITY_OAUTH_2_RESOURCESERVER_JWT_ISSUER_URI =
"spring.security.oauth2.resourceserver.jwt.issuer-uri";
public static final String SPRING_SECURITY_OAUTH_2_RESOURCESERVER_JWT_JWK_SET_URI =
"spring.security.oauth2.resourceserver.jwt.jwk-set-uri";
private static final Logger LOGGER = LoggerFactory.getLogger(OAuth2WebConfigurer.class);
@Autowired private Environment env;
@Autowired private TasklistProperties config;
private final CustomJwtAuthenticationConverter jwtConverter =
new CustomJwtAuthenticationConverter();
public void configure(HttpSecurity http) throws Exception {
if (isJWTEnabled()) {
http.oauth2ResourceServer(
serverCustomizer ->
serverCustomizer
.authenticationEntryPoint(this::authenticationFailure)
.jwt(jwtCustomizer -> jwtCustomizer.jwtAuthenticationConverter(jwtConverter)));
LOGGER.info("Enabled OAuth2 JWT access to REST API");
}
}
private void authenticationFailure(
final HttpServletRequest request,
final HttpServletResponse response,
final AuthenticationException e)
throws IOException {
request.getSession().invalidate();
sendJSONErrorMessage(response, e.getMessage());
}
protected boolean isJWTEnabled() {
return env.containsProperty(SPRING_SECURITY_OAUTH_2_RESOURCESERVER_JWT_ISSUER_URI)
|| env.containsProperty(SPRING_SECURITY_OAUTH_2_RESOURCESERVER_JWT_JWK_SET_URI);
}
class CustomJwtAuthenticationConverter implements Converter {
public static final String AUDIENCE = "aud";
public static final String CLUSTER_ID = "https://camunda.com/clusterId";
private final JwtAuthenticationConverter delegate = new JwtAuthenticationConverter();
@Override
public AbstractAuthenticationToken convert(final Jwt jwt) {
final JwtAuthenticationToken auth = (JwtAuthenticationToken) delegate.convert(jwt);
final Map payload = auth.getTokenAttributes();
if (isValid(payload)) {
return auth;
}
throw new InvalidBearerTokenException("JWT payload validation failed");
}
private boolean isValid(final Map payload) {
try {
final String audience = getAudience(payload);
final String clusterId = getClusterId(payload);
final ClientProperties clientConfig = config.getClient();
return clientConfig.getAudience().equals(audience)
&& clientConfig.getClusterId().equals(clusterId);
} catch (Exception e) {
LOGGER.warn("Validation of JWT payload failed. Request is not authenticated.");
return false;
}
}
private String getClusterId(final Map payload) {
return (String) payload.get(CLUSTER_ID);
}
private String getAudience(final Map payload) {
return firstOrDefault(
(List) getOrDefaultFromMap(payload, AUDIENCE, Collections.emptyList()), null);
}
}
}