diff --git a/spring-security-modules/pom.xml b/spring-security-modules/pom.xml
index 9fdfde282d..bb36909c79 100644
--- a/spring-security-modules/pom.xml
+++ b/spring-security-modules/pom.xml
@@ -46,6 +46,7 @@
spring-security-web-x509
spring-session
spring-social-login
+ spring-security-opa
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-opa/pom.xml b/spring-security-modules/spring-security-opa/pom.xml
new file mode 100644
index 0000000000..6665c33db3
--- /dev/null
+++ b/spring-security-modules/spring-security-opa/pom.xml
@@ -0,0 +1,49 @@
+
+ 4.0.0
+
+ com.baeldung
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../../parent-boot-2
+
+ spring-security-opa
+ Spring Security with OPA authorization
+
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+ com.google.guava
+ guava
+ 31.0.1-jre
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+
+
+
+ org.springframework.security
+ spring-security-test
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-opa/src/main/java/com/baeldung/security/opa/Application.java b/spring-security-modules/spring-security-opa/src/main/java/com/baeldung/security/opa/Application.java
new file mode 100644
index 0000000000..789a1f803f
--- /dev/null
+++ b/spring-security-modules/spring-security-opa/src/main/java/com/baeldung/security/opa/Application.java
@@ -0,0 +1,13 @@
+package com.baeldung.security.opa;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+}
diff --git a/spring-security-modules/spring-security-opa/src/main/java/com/baeldung/security/opa/config/OpaConfiguration.java b/spring-security-modules/spring-security-opa/src/main/java/com/baeldung/security/opa/config/OpaConfiguration.java
new file mode 100644
index 0000000000..e24fdbcf35
--- /dev/null
+++ b/spring-security-modules/spring-security-opa/src/main/java/com/baeldung/security/opa/config/OpaConfiguration.java
@@ -0,0 +1,25 @@
+package com.baeldung.security.opa.config;
+
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import lombok.RequiredArgsConstructor;
+
+@Configuration
+@RequiredArgsConstructor
+@EnableConfigurationProperties(OpaProperties.class)
+public class OpaConfiguration {
+
+ private final OpaProperties opaProperties;
+
+ @Bean
+ public WebClient opaWebClient(WebClient.Builder builder) {
+
+ return builder
+ .baseUrl(opaProperties.getEndpoint())
+ .build();
+ }
+
+}
diff --git a/spring-security-modules/spring-security-opa/src/main/java/com/baeldung/security/opa/config/OpaProperties.java b/spring-security-modules/spring-security-opa/src/main/java/com/baeldung/security/opa/config/OpaProperties.java
new file mode 100644
index 0000000000..acc23a2fd2
--- /dev/null
+++ b/spring-security-modules/spring-security-opa/src/main/java/com/baeldung/security/opa/config/OpaProperties.java
@@ -0,0 +1,14 @@
+package com.baeldung.security.opa.config;
+
+import javax.annotation.Nonnull;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import lombok.Data;
+
+@ConfigurationProperties(prefix = "opa")
+@Data
+public class OpaProperties {
+ @Nonnull
+ private String endpoint = "http://localhost:8181";
+}
diff --git a/spring-security-modules/spring-security-opa/src/main/java/com/baeldung/security/opa/config/SecurityConfiguration.java b/spring-security-modules/spring-security-opa/src/main/java/com/baeldung/security/opa/config/SecurityConfiguration.java
new file mode 100644
index 0000000000..7e10cb2e8a
--- /dev/null
+++ b/spring-security-modules/spring-security-opa/src/main/java/com/baeldung/security/opa/config/SecurityConfiguration.java
@@ -0,0 +1,110 @@
+package com.baeldung.security.opa.config;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.reactivestreams.Publisher;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.MediaType;
+import org.springframework.http.client.reactive.ClientHttpRequest;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.authorization.ReactiveAuthorizationManager;
+import org.springframework.security.config.web.server.ServerHttpSecurity;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.web.server.SecurityWebFilterChain;
+import org.springframework.security.web.server.authorization.AuthorizationContext;
+import org.springframework.web.reactive.function.BodyInserter;
+import org.springframework.web.reactive.function.BodyInserters;
+import org.springframework.web.reactive.function.client.ClientResponse;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.ImmutableMap;
+
+import reactor.core.publisher.Mono;
+
+@Configuration
+public class SecurityConfiguration {
+
+
+ @Bean
+ public SecurityWebFilterChain accountAuthorization(ServerHttpSecurity http, @Qualifier("opaWebClient")WebClient opaWebClient) {
+
+ // @formatter:on
+ return http
+ .httpBasic()
+ .and()
+ .authorizeExchange(exchanges -> {
+ exchanges
+ .pathMatchers("/account/*")
+ .access(opaAuthManager(opaWebClient));
+ })
+ .build();
+ // @formatter:on
+
+ }
+
+ @Bean
+ public ReactiveAuthorizationManager opaAuthManager(WebClient opaWebClient) {
+
+ return (auth, context) -> {
+ return opaWebClient.post()
+ .accept(MediaType.APPLICATION_JSON)
+ .contentType(MediaType.APPLICATION_JSON)
+ .body(toAuthorizationPayload(auth,context), Map.class)
+ .exchangeToMono(this::toDecision);
+ };
+ }
+
+ private Mono toDecision(ClientResponse response) {
+
+ if ( !response.statusCode().is2xxSuccessful()) {
+ return Mono.just(new AuthorizationDecision(false));
+ }
+
+ return response
+ .bodyToMono(ObjectNode.class)
+ .map(node -> {
+ boolean authorized = node.path("result").path("authorized").asBoolean(false);
+ return new AuthorizationDecision(authorized);
+ });
+
+ }
+
+ private Publisher