Issue
I am developing authentication and authorization in an environment where I use Spring Cloud Gateway Webflux + OAuth 2.0 the structure to achieve is the following:
src="https://i.stack.imgur.com/2YzJx.png" alt="enter image description here" />
As Authorization Server I have my own OAuth server that contains the /login page where I perform the authentication and it is also in charge of generating JWT and as Resource Server I have a WebFlux module that is also in charge of being the Gateway.
The Resource Server configuration is as follows:
application.yml
spring:
application:
name: spring-boot-gateway
security:
oauth2:
resourceserver:
jwt:
issuer-uri: http://adp-auth-provider/auth/oauth/token
client:
registration:
oauth:
client-name: oauth
client-id: first-client
client-secret: xxxxxx
provider: adp-auth-provider
authorization-grant-type: authorization_code
redirect-uri: /login
scope: read
provider:
adp-auth-provider:
authorization-uri: /auth/oauth/authorize
token-uri: http://adp-auth-provider/auth/oauth/token
user-info-uri: http://adp-auth-provider/userinfo
jwt-set-uri: http://adp-auth-provider/token_keys
WebFluxSecurityConfig.java
@Configuration(proxyBeanMethods = false)
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class WebFluxSecurityConfig {
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
return http
.httpBasic().disable()
.csrf().disable()
.authorizeExchange(exchanges -> exchanges
.pathMatchers(HttpMethod.GET, "/oauth2/authorization/**",
"/actuator",
"/actuator/**",
"/auth/login",
"/login")
.permitAll()
.anyExchange()
.authenticated()
.oauth2Login()
.and()
.build();
}
}
SpringGatewayApplication.java
@SpringBootApplication
public class SpringGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(SpringGatewayApplication.class, args);
}
}
When I type http://localhost in the browser, it redirects perfectly to the OAuth /login page, but when I enter my credentials it redirects me to the next page:
The requests appear to have been the right ones:
Does anyone know why I am not redirected to the index once I have logged in correctly? It stays on that page and if I click on oauth it redirects me to the same page again.
**EDIT:
Setting redirect-uri
to the default "{baseUrl}/login/oauth2/code/{registrationId}"
displays the following error:
2022-01-18 12:12:15.852 ERROR 2836 --- [ctor-http-nio-6] a.w.r.e.AbstractErrorWebExceptionHandler : [477242e5-1] 500 Server Error for HTTP GET "/login/oauth2/code/oauth?code=nTCRNi&state=Ub8jQjbp1baxhgsxcpNULMMHoV8z42bQsp62iL2jNV8%3D"
java.lang.IllegalStateException: No provider found for class org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeAuthenticationToken
at org.springframework.security.web.server.authentication.AuthenticationWebFilter.lambda$authenticate$6(AuthenticationWebFilter.java:123) ~[spring-security-web-5.6.0.jar:5.6.0]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
*__checkpoint ⇢ org.springframework.security.oauth2.client.web.server.authentication.OAuth2LoginAuthenticationWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.oauth2.client.web.server.OAuth2AuthorizationRequestRedirectWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.context.ReactorContextWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.header.HttpHeaderWriterWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ HTTP GET "/login/oauth2/code/oauth?code=nTCRNi&state=Ub8jQjbp1baxhgsxcpNULMMHoV8z42bQsp62iL2jNV8%3D" [ExceptionHandlingWebHandler]
Original Stack Trace:
at org.springframework.security.web.server.authentication.AuthenticationWebFilter.lambda$authenticate$6(AuthenticationWebFilter.java:123) ~[spring-security-web-5.6.0.jar:5.6.0]
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:44) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.Mono.subscribe(Mono.java:4400) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:82) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.FluxHide$SuppressFuseableSubscriber.onComplete(FluxHide.java:147) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.MonoNext$NextSubscriber.onComplete(MonoNext.java:102) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:367) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.innerComplete(FluxConcatMap.java:296) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onComplete(FluxConcatMap.java:885) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.secondComplete(MonoFlatMap.java:196) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.MonoFlatMap$FlatMapInner.onComplete(MonoFlatMap.java:268) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:142) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onComplete(Operators.java:2058) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onComplete(Operators.java:2058) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onComplete(FluxMapFuseable.java:150) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.secondComplete(MonoFlatMap.java:196) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.MonoFlatMap$FlatMapInner.onComplete(MonoFlatMap.java:268) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onComplete(Operators.java:2058) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.MonoNext$NextSubscriber.onComplete(MonoNext.java:102) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:142) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:142) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:260) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:142) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.netty.channel.FluxReceive.onInboundComplete(FluxReceive.java:400) ~[reactor-netty-core-1.0.13.jar:1.0.13]
at reactor.netty.channel.ChannelOperations.onInboundComplete(ChannelOperations.java:419) ~[reactor-netty-core-1.0.13.jar:1.0.13]
at reactor.netty.channel.ChannelOperations.terminate(ChannelOperations.java:473) ~[reactor-netty-core-1.0.13.jar:1.0.13]
at reactor.netty.http.client.HttpClientOperations.onInboundNext(HttpClientOperations.java:702) ~[reactor-netty-http-1.0.13.jar:1.0.13]
at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:93) ~[reactor-netty-core-1.0.13.jar:1.0.13]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103) ~[netty-codec-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:324) ~[netty-codec-4.1.70.Final.jar:4.1.70.Final]
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:296) ~[netty-codec-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:719) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:655) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:581) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986) ~[netty-common-4.1.70.Final.jar:4.1.70.Final]
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.70.Final.jar:4.1.70.Final]
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.70.Final.jar:4.1.70.Final]
at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]
The application.yml now looks like this:
spring:
application:
name: spring-boot-gateway
security:
oauth2:
resourceserver:
jwt:
issuer-uri: http://localhost/auth/oauth/token
client:
registration:
oauth:
client-name: oauth
client-id: first-client
client-secret: xxxx
provider: adp-auth-provider
authorization-grant-type: authorization_code
redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
scope: read
provider:
adp-auth-provider:
authorization-uri: http://localhost/auth/oauth/authorize
token-uri: http://localhost/auth/oauth/token
user-info-uri: http://localhost/auth/me
user-name-attribute: sub
Solution
The problem was occurring because the default authentication manager wasn't working for me, I had to implement one specifically for my problem.
@Configuration(proxyBeanMethods = false)
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class WebFluxSecurityConfig {
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http, AuthenticationManager authenticationManager) {
return http
.httpBasic().disable()
.csrf().disable()
.authorizeExchange(exchanges -> exchanges
.pathMatchers(HttpMethod.GET, "/oauth2/authorization/**",
"/actuator",
"/actuator/**",
"/auth/login",
"/login/**")
.permitAll()
.anyExchange()
.authenticated()
.oauth2Login()
.authenticationManager(authenticationManager)
.and()
.build();
}
}
I also had to modify the redirect-uri
and leave it as '/login/oauth2/code/{registrationId}'
.
spring:
application:
name: spring-boot-gateway
security:
oauth2:
resourceserver:
jwt:
issuer-uri: http://127.0.0.1/auth/oauth/token
client:
registration:
oauth:
client-name: oauth
client-id: first-client
client-secret: xxxxx
provider: adp-auth-provider
authorization-grant-type: authorization_code
redirect-uri: '/login/oauth2/code/{registrationId}'
scope: read
provider:
adp-auth-provider:
authorization-uri: /auth/oauth/authorize
token-uri: /auth/oauth/token
user-info-uri: /auth/me
Answered By - Pablo Aragonés
Answer Checked By - Pedro (JavaFixing Volunteer)